]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - vid_wgl.c
trying to fix many forward references in input code
[xonotic/darkplaces.git] / vid_wgl.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 // gl_vidnt.c -- NT GL vid component
21
22 #include "quakedef.h"
23 #include "winquake.h"
24 #include "resource.h"
25 #include <commctrl.h>
26
27 int cl_available = true;
28
29 int (WINAPI *qwglChoosePixelFormat)(HDC, CONST PIXELFORMATDESCRIPTOR *);
30 int (WINAPI *qwglDescribePixelFormat)(HDC, int, UINT, LPPIXELFORMATDESCRIPTOR);
31 //int (WINAPI *qwglGetPixelFormat)(HDC);
32 BOOL (WINAPI *qwglSetPixelFormat)(HDC, int, CONST PIXELFORMATDESCRIPTOR *);
33 BOOL (WINAPI *qwglSwapBuffers)(HDC);
34 HGLRC (WINAPI *qwglCreateContext)(HDC);
35 BOOL (WINAPI *qwglDeleteContext)(HGLRC);
36 HGLRC (WINAPI *qwglGetCurrentContext)(VOID);
37 HDC (WINAPI *qwglGetCurrentDC)(VOID);
38 PROC (WINAPI *qwglGetProcAddress)(LPCSTR);
39 BOOL (WINAPI *qwglMakeCurrent)(HDC, HGLRC);
40 BOOL (WINAPI *qwglSwapIntervalEXT)(int interval);
41 const char *(WINAPI *qwglGetExtensionsStringARB)(HDC hdc);
42
43 static gl_extensionfunctionlist_t wglfuncs[] =
44 {
45         {"wglChoosePixelFormat", (void **) &qwglChoosePixelFormat},
46         {"wglDescribePixelFormat", (void **) &qwglDescribePixelFormat},
47 //      {"wglGetPixelFormat", (void **) &qwglGetPixelFormat},
48         {"wglSetPixelFormat", (void **) &qwglSetPixelFormat},
49         {"wglSwapBuffers", (void **) &qwglSwapBuffers},
50         {"wglCreateContext", (void **) &qwglCreateContext},
51         {"wglDeleteContext", (void **) &qwglDeleteContext},
52         {"wglGetProcAddress", (void **) &qwglGetProcAddress},
53         {"wglMakeCurrent", (void **) &qwglMakeCurrent},
54         {"wglGetCurrentContext", (void **) &qwglGetCurrentContext},
55         {"wglGetCurrentDC", (void **) &qwglGetCurrentDC},
56         {NULL, NULL}
57 };
58
59 static gl_extensionfunctionlist_t wglswapintervalfuncs[] =
60 {
61         {"wglSwapIntervalEXT", (void **) &qwglSwapIntervalEXT},
62         {NULL, NULL}
63 };
64
65 void VID_RestoreGameGamma(void);
66 void VID_GetSystemGamma(void);
67 void VID_RestoreSystemGamma(void);
68
69 qboolean scr_skipupdate;
70
71 static DEVMODE gdevmode;
72 static qboolean vid_initialized = false;
73 static qboolean vid_wassuspended = false;
74 static int vid_usingmouse;
75 extern qboolean mouseactive;  // from in_win.c
76 static HICON hIcon;
77
78 HWND mainwindow;
79
80 //HWND WINAPI InitializeWindow (HINSTANCE hInstance, int nCmdShow);
81
82 static int vid_isfullscreen;
83
84 //void VID_MenuDraw (void);
85 //void VID_MenuKey (int key);
86
87 //LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
88 //void AppActivate(BOOL fActive, BOOL minimize);
89 //void ClearAllStates (void);
90 //void VID_UpdateWindowStatus (void);
91
92 //====================================
93
94 int window_x, window_y, window_width, window_height;
95 int window_center_x, window_center_y;
96
97 void IN_ShowMouse (void);
98 void IN_DeactivateMouse (void);
99 void IN_HideMouse (void);
100 void IN_ActivateMouse (void);
101 void IN_MouseEvent (int mstate);
102 void IN_UpdateClipCursor (void);
103
104 qboolean mouseinitialized;
105 static qboolean dinput;
106
107 // input code
108
109 #include <dinput.h>
110
111 #define DINPUT_BUFFERSIZE           16
112 #define iDirectInputCreate(a,b,c,d)     pDirectInputCreate(a,b,c,d)
113
114 HRESULT (WINAPI *pDirectInputCreate)(HINSTANCE hinst, DWORD dwVersion,
115         LPDIRECTINPUT * lplpDirectInput, LPUNKNOWN punkOuter);
116
117 // mouse variables
118 int                     mouse_buttons;
119 int                     mouse_oldbuttonstate;
120 POINT           current_pos;
121 int                     mouse_x, mouse_y, old_mouse_x, old_mouse_y, mx_accum, my_accum;
122
123 static qboolean restore_spi;
124 static int              originalmouseparms[3], newmouseparms[3] = {0, 0, 1};
125
126 unsigned int uiWheelMessage;
127 qboolean        mouseactive;
128 //qboolean              mouseinitialized;
129 static qboolean mouseparmsvalid, mouseactivatetoggle;
130 static qboolean mouseshowtoggle = 1;
131 static qboolean dinput_acquired;
132
133 static unsigned int             mstate_di;
134
135 // joystick defines and variables
136 // where should defines be moved?
137 #define JOY_ABSOLUTE_AXIS       0x00000000              // control like a joystick
138 #define JOY_RELATIVE_AXIS       0x00000010              // control like a mouse, spinner, trackball
139 #define JOY_MAX_AXES            6                               // X, Y, Z, R, U, V
140 #define JOY_AXIS_X                      0
141 #define JOY_AXIS_Y                      1
142 #define JOY_AXIS_Z                      2
143 #define JOY_AXIS_R                      3
144 #define JOY_AXIS_U                      4
145 #define JOY_AXIS_V                      5
146
147 enum _ControlList
148 {
149         AxisNada = 0, AxisForward, AxisLook, AxisSide, AxisTurn
150 };
151
152 DWORD   dwAxisFlags[JOY_MAX_AXES] =
153 {
154         JOY_RETURNX, JOY_RETURNY, JOY_RETURNZ, JOY_RETURNR, JOY_RETURNU, JOY_RETURNV
155 };
156
157 DWORD   dwAxisMap[JOY_MAX_AXES];
158 DWORD   dwControlMap[JOY_MAX_AXES];
159 PDWORD  pdwRawValue[JOY_MAX_AXES];
160
161 // none of these cvars are saved over a session
162 // this means that advanced controller configuration needs to be executed
163 // each time.  this avoids any problems with getting back to a default usage
164 // or when changing from one controller to another.  this way at least something
165 // works.
166 cvar_t  in_joystick = {CVAR_SAVE, "joystick","0"};
167 cvar_t  joy_name = {0, "joyname", "joystick"};
168 cvar_t  joy_advanced = {0, "joyadvanced", "0"};
169 cvar_t  joy_advaxisx = {0, "joyadvaxisx", "0"};
170 cvar_t  joy_advaxisy = {0, "joyadvaxisy", "0"};
171 cvar_t  joy_advaxisz = {0, "joyadvaxisz", "0"};
172 cvar_t  joy_advaxisr = {0, "joyadvaxisr", "0"};
173 cvar_t  joy_advaxisu = {0, "joyadvaxisu", "0"};
174 cvar_t  joy_advaxisv = {0, "joyadvaxisv", "0"};
175 cvar_t  joy_forwardthreshold = {0, "joyforwardthreshold", "0.15"};
176 cvar_t  joy_sidethreshold = {0, "joysidethreshold", "0.15"};
177 cvar_t  joy_pitchthreshold = {0, "joypitchthreshold", "0.15"};
178 cvar_t  joy_yawthreshold = {0, "joyyawthreshold", "0.15"};
179 cvar_t  joy_forwardsensitivity = {0, "joyforwardsensitivity", "-1.0"};
180 cvar_t  joy_sidesensitivity = {0, "joysidesensitivity", "-1.0"};
181 cvar_t  joy_pitchsensitivity = {0, "joypitchsensitivity", "1.0"};
182 cvar_t  joy_yawsensitivity = {0, "joyyawsensitivity", "-1.0"};
183 cvar_t  joy_wwhack1 = {0, "joywwhack1", "0.0"};
184 cvar_t  joy_wwhack2 = {0, "joywwhack2", "0.0"};
185
186 qboolean        joy_avail, joy_advancedinit, joy_haspov;
187 DWORD           joy_oldbuttonstate, joy_oldpovstate;
188
189 int                     joy_id;
190 DWORD           joy_flags;
191 DWORD           joy_numbuttons;
192
193 static LPDIRECTINPUT            g_pdi;
194 static LPDIRECTINPUTDEVICE      g_pMouse;
195
196 static JOYINFOEX        ji;
197
198 static HINSTANCE hInstDI;
199
200 //static qboolean       dinput;
201
202 typedef struct MYDATA {
203         LONG  lX;                   // X axis goes here
204         LONG  lY;                   // Y axis goes here
205         LONG  lZ;                   // Z axis goes here
206         BYTE  bButtonA;             // One button goes here
207         BYTE  bButtonB;             // Another button goes here
208         BYTE  bButtonC;             // Another button goes here
209         BYTE  bButtonD;             // Another button goes here
210 } MYDATA;
211
212 static DIOBJECTDATAFORMAT rgodf[] = {
213   { &GUID_XAxis,    FIELD_OFFSET(MYDATA, lX),       DIDFT_AXIS | DIDFT_ANYINSTANCE,   0,},
214   { &GUID_YAxis,    FIELD_OFFSET(MYDATA, lY),       DIDFT_AXIS | DIDFT_ANYINSTANCE,   0,},
215   { &GUID_ZAxis,    FIELD_OFFSET(MYDATA, lZ),       0x80000000 | DIDFT_AXIS | DIDFT_ANYINSTANCE,   0,},
216   { 0,              FIELD_OFFSET(MYDATA, bButtonA), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
217   { 0,              FIELD_OFFSET(MYDATA, bButtonB), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
218   { 0,              FIELD_OFFSET(MYDATA, bButtonC), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
219   { 0,              FIELD_OFFSET(MYDATA, bButtonD), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
220 };
221
222 #define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
223
224 static DIDATAFORMAT     df = {
225         sizeof(DIDATAFORMAT),       // this structure
226         sizeof(DIOBJECTDATAFORMAT), // size of object data format
227         DIDF_RELAXIS,               // absolute axis coordinates
228         sizeof(MYDATA),             // device data size
229         NUM_OBJECTS,                // number of objects
230         rgodf,                      // and here they are
231 };
232
233 // forward-referenced functions
234 void IN_StartupJoystick (void);
235 void Joy_AdvancedUpdate_f (void);
236 void IN_JoyMove (usercmd_t *cmd);
237
238 /*
239 ================
240 VID_UpdateWindowStatus
241 ================
242 */
243 void VID_UpdateWindowStatus (void)
244 {
245         window_center_x = window_x + window_width / 2;
246         window_center_y = window_y + window_height / 2;
247
248         if (mouseinitialized && mouseactive && !dinput)
249         {
250                 RECT window_rect;
251                 window_rect.left = window_x;
252                 window_rect.top = window_y;
253                 window_rect.right = window_x + window_width;
254                 window_rect.bottom = window_y + window_height;
255                 ClipCursor (&window_rect);
256         }
257 }
258
259
260 //====================================
261
262 /*
263 =================
264 VID_GetWindowSize
265 =================
266 */
267 void VID_GetWindowSize (int *x, int *y, int *width, int *height)
268 {
269         *x = 0;
270         *y = 0;
271         *width = window_width;
272         *height = window_height;
273 }
274
275
276 void VID_Finish (void)
277 {
278         HDC hdc;
279         int vid_usemouse;
280         if (r_render.integer && !scr_skipupdate)
281         {
282                 qglFinish();
283                 hdc = GetDC(mainwindow);
284                 SwapBuffers(hdc);
285                 ReleaseDC(mainwindow, hdc);
286         }
287
288 // handle the mouse state when windowed if that's changed
289         vid_usemouse = false;
290         if (vid_mouse.integer && !key_consoleactive)
291                 vid_usemouse = true;
292         if (vid_isfullscreen)
293                 vid_usemouse = true;
294         if (!vid_activewindow)
295                 vid_usemouse = false;
296         if (vid_usemouse)
297         {
298                 if (!vid_usingmouse)
299                 {
300                         vid_usingmouse = true;
301                         IN_ActivateMouse ();
302                         IN_HideMouse();
303                 }
304         }
305         else
306         {
307                 if (vid_usingmouse)
308                 {
309                         vid_usingmouse = false;
310                         IN_DeactivateMouse ();
311                         IN_ShowMouse();
312                 }
313         }
314 }
315
316 //==========================================================================
317
318
319
320
321 qbyte scantokey[128] =
322 {
323 //      0           1      2     3     4     5       6       7      8         9      A       B           C     D            E           F
324         0          ,27    ,'1'  ,'2'  ,'3'  ,'4'    ,'5'    ,'6'   ,'7'      ,'8'   ,'9'    ,'0'        ,'-'  ,'='         ,K_BACKSPACE,9     , // 0
325         'q'        ,'w'   ,'e'  ,'r'  ,'t'  ,'y'    ,'u'    ,'i'   ,'o'      ,'p'   ,'['    ,']'        ,13   ,K_CTRL      ,'a'        ,'s'   , // 1
326         'd'        ,'f'   ,'g'  ,'h'  ,'j'  ,'k'    ,'l'    ,';'   ,'\''     ,'`'   ,K_SHIFT,'\\'       ,'z'  ,'x'         ,'c'        ,'v'   , // 2
327         'b'        ,'n'   ,'m'  ,','  ,'.'  ,'/'    ,K_SHIFT,'*'   ,K_ALT    ,' '   ,0      ,K_F1       ,K_F2 ,K_F3        ,K_F4       ,K_F5  , // 3
328         K_F6       ,K_F7  ,K_F8 ,K_F9 ,K_F10,K_PAUSE,0      ,K_HOME,K_UPARROW,K_PGUP,'-'    ,K_LEFTARROW,'5'  ,K_RIGHTARROW,'+'        ,K_END , // 4
329         K_DOWNARROW,K_PGDN,K_INS,K_DEL,0    ,0      ,0      ,K_F11 ,K_F12    ,0     ,0      ,0          ,0    ,0           ,0          ,0     , // 5
330         0          ,0     ,0    ,0    ,0    ,0      ,0      ,0     ,0        ,0     ,0      ,0          ,0    ,0           ,0          ,0     , // 6
331         0          ,0     ,0    ,0    ,0    ,0      ,0      ,0     ,0        ,0     ,0      ,0          ,0    ,0           ,0          ,0       // 7
332 };
333
334
335 /*
336 =======
337 MapKey
338
339 Map from windows to quake keynums
340 =======
341 */
342 int MapKey (int key, int virtualkey)
343 {
344         key = (key>>16)&255;
345         if (key > 127)
346                 return 0;
347         if (scantokey[key] == 0)
348                 Con_DPrintf("key 0x%02x has no translation\n", key);
349         return scantokey[key];
350 }
351
352 /*
353 ===================================================================
354
355 MAIN WINDOW
356
357 ===================================================================
358 */
359
360 /*
361 ================
362 ClearAllStates
363 ================
364 */
365 void ClearAllStates (void)
366 {
367         int             i;
368
369 // send an up event for each key, to make sure the server clears them all
370         for (i=0 ; i<256 ; i++)
371         {
372                 Key_Event (i, false);
373         }
374
375         Key_ClearStates ();
376         IN_ClearStates ();
377 }
378
379 void VID_RestoreGameGamma(void);
380 extern qboolean host_loopactive;
381
382 void AppActivate(BOOL fActive, BOOL minimize)
383 /****************************************************************************
384 *
385 * Function:     AppActivate
386 * Parameters:   fActive - True if app is activating
387 *
388 * Description:  If the application is activating, then swap the system
389 *               into SYSPAL_NOSTATIC mode so that our palettes will display
390 *               correctly.
391 *
392 ****************************************************************************/
393 {
394         static BOOL     sound_active;
395
396         vid_activewindow = fActive;
397         vid_hidden = minimize;
398
399 // enable/disable sound on focus gain/loss
400         if (!vid_activewindow && sound_active)
401         {
402                 S_BlockSound ();
403                 sound_active = false;
404         }
405         else if (vid_activewindow && !sound_active)
406         {
407                 S_UnblockSound ();
408                 sound_active = true;
409         }
410
411         if (fActive)
412         {
413                 if (vid_isfullscreen)
414                 {
415                         if (vid_wassuspended)
416                         {
417                                 vid_wassuspended = false;
418                                 ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN);
419                                 ShowWindow(mainwindow, SW_SHOWNORMAL);
420                         }
421
422                         // LordHavoc: from dabb, fix for alt-tab bug in NVidia drivers
423                         MoveWindow(mainwindow,0,0,gdevmode.dmPelsWidth,gdevmode.dmPelsHeight,false);
424                 }
425                 if (host_loopactive)
426                         VID_RestoreGameGamma();
427         }
428
429         if (!fActive)
430         {
431                 vid_usingmouse = false;
432                 IN_DeactivateMouse ();
433                 IN_ShowMouse ();
434                 if (vid_isfullscreen)
435                 {
436                         ChangeDisplaySettings (NULL, 0);
437                         vid_wassuspended = true;
438                 }
439                 VID_RestoreSystemGamma();
440         }
441 }
442
443 LONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
444
445 /* main window procedure */
446 LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM  wParam, LPARAM lParam)
447 {
448         LONG    lRet = 1;
449         int             fActive, fMinimized, temp;
450         extern unsigned int uiWheelMessage;
451
452         if ( uMsg == uiWheelMessage )
453                 uMsg = WM_MOUSEWHEEL;
454
455         switch (uMsg)
456         {
457                 case WM_KILLFOCUS:
458                         if (vid_isfullscreen)
459                                 ShowWindow(mainwindow, SW_SHOWMINNOACTIVE);
460                         break;
461
462                 case WM_CREATE:
463                         break;
464
465                 case WM_MOVE:
466                         window_x = (int) LOWORD(lParam);
467                         window_y = (int) HIWORD(lParam);
468                         VID_UpdateWindowStatus ();
469                         break;
470
471                 case WM_KEYDOWN:
472                 case WM_SYSKEYDOWN:
473                         Key_Event (MapKey(lParam, wParam), true);
474                         break;
475
476                 case WM_KEYUP:
477                 case WM_SYSKEYUP:
478                         Key_Event (MapKey(lParam, wParam), false);
479                         break;
480
481                 case WM_SYSCHAR:
482                 // keep Alt-Space from happening
483                         break;
484
485         // this is complicated because Win32 seems to pack multiple mouse events into
486         // one update sometimes, so we always check all states and look for events
487                 case WM_LBUTTONDOWN:
488                 case WM_LBUTTONUP:
489                 case WM_RBUTTONDOWN:
490                 case WM_RBUTTONUP:
491                 case WM_MBUTTONDOWN:
492                 case WM_MBUTTONUP:
493                 case WM_MOUSEMOVE:
494                         temp = 0;
495
496                         if (wParam & MK_LBUTTON)
497                                 temp |= 1;
498
499                         if (wParam & MK_RBUTTON)
500                                 temp |= 2;
501
502                         if (wParam & MK_MBUTTON)
503                                 temp |= 4;
504
505                         IN_MouseEvent (temp);
506
507                         break;
508
509                 // JACK: This is the mouse wheel with the Intellimouse
510                 // Its delta is either positive or neg, and we generate the proper
511                 // Event.
512                 case WM_MOUSEWHEEL:
513                         if ((short) HIWORD(wParam) > 0) {
514                                 Key_Event(K_MWHEELUP, true);
515                                 Key_Event(K_MWHEELUP, false);
516                         } else {
517                                 Key_Event(K_MWHEELDOWN, true);
518                                 Key_Event(K_MWHEELDOWN, false);
519                         }
520                         break;
521
522                 case WM_SIZE:
523                         break;
524
525                 case WM_CLOSE:
526                         if (MessageBox (mainwindow, "Are you sure you want to quit?", "Confirm Exit", MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES)
527                                 Sys_Quit ();
528
529                         break;
530
531                 case WM_ACTIVATE:
532                         fActive = LOWORD(wParam);
533                         fMinimized = (BOOL) HIWORD(wParam);
534                         AppActivate(!(fActive == WA_INACTIVE), fMinimized);
535
536                 // fix the leftover Alt from any Alt-Tab or the like that switched us away
537                         ClearAllStates ();
538
539                         break;
540
541                 case WM_DESTROY:
542                 {
543                         if (mainwindow)
544                                 DestroyWindow (mainwindow);
545
546                         PostQuitMessage (0);
547                 }
548                 break;
549
550                 case MM_MCINOTIFY:
551                         lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam);
552                         break;
553
554                 default:
555                         /* pass all unhandled messages to DefWindowProc */
556                         lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
557                 break;
558         }
559
560         /* return 1 if handled message, 0 if not */
561         return lRet;
562 }
563
564
565 //static int grabsysgamma = true;
566 WORD systemgammaramps[3][256], currentgammaramps[3][256];
567
568 int VID_SetGamma(float prescale, float gamma, float scale, float base)
569 {
570         int i;
571         HDC hdc;
572         hdc = GetDC (NULL);
573
574         BuildGammaTable16(prescale, gamma, scale, base, &currentgammaramps[0][0]);
575         for (i = 0;i < 256;i++)
576                 currentgammaramps[1][i] = currentgammaramps[2][i] = currentgammaramps[0][i];
577
578         i = SetDeviceGammaRamp(hdc, &currentgammaramps[0][0]);
579
580         ReleaseDC (NULL, hdc);
581         return i; // return success or failure
582 }
583
584 void VID_RestoreGameGamma(void)
585 {
586         VID_UpdateGamma(true);
587 }
588
589 void VID_GetSystemGamma(void)
590 {
591         HDC hdc;
592         hdc = GetDC (NULL);
593
594         GetDeviceGammaRamp(hdc, &systemgammaramps[0][0]);
595
596         ReleaseDC (NULL, hdc);
597 }
598
599 void VID_RestoreSystemGamma(void)
600 {
601         HDC hdc;
602         hdc = GetDC (NULL);
603
604         SetDeviceGammaRamp(hdc, &systemgammaramps[0][0]);
605
606         ReleaseDC (NULL, hdc);
607 }
608
609 //========================================================
610 // Video menu stuff
611 //========================================================
612
613 #if 0
614 extern void M_Menu_Options_f (void);
615 extern void M_Print (float cx, float cy, char *str);
616 extern void M_PrintWhite (float cx, float cy, char *str);
617 extern void M_DrawCharacter (float cx, float cy, int num);
618 extern void M_DrawPic (float cx, float cy, char *picname);
619
620 static int vid_wmodes;
621
622 typedef struct
623 {
624         int modenum;
625         char *desc;
626         int iscur;
627 } modedesc_t;
628
629 #define MAX_COLUMN_SIZE         9
630 #define MODE_AREA_HEIGHT        (MAX_COLUMN_SIZE + 2)
631 #define MAX_MODEDESCS           (MAX_COLUMN_SIZE*3)
632
633 static modedesc_t modedescs[MAX_MODEDESCS];
634
635 /*
636 ================
637 VID_MenuDraw
638 ================
639 */
640 void VID_MenuDraw (void)
641 {
642         cachepic_t *p;
643         char *ptr;
644         int lnummodes, i, k, column, row;
645         vmode_t *pv;
646
647         p = Draw_CachePic ("gfx/vidmodes.lmp");
648         M_DrawPic ( (320-p->width)/2, 4, "gfx/vidmodes.lmp");
649
650         vid_wmodes = 0;
651         lnummodes = VID_NumModes ();
652
653         for (i=1 ; (i<lnummodes) && (vid_wmodes < MAX_MODEDESCS) ; i++)
654         {
655                 ptr = VID_GetModeDescription (i);
656                 pv = VID_GetModePtr (i);
657
658                 k = vid_wmodes;
659
660                 modedescs[k].modenum = i;
661                 modedescs[k].desc = ptr;
662                 modedescs[k].iscur = 0;
663
664                 if (i == vid_modenum)
665                         modedescs[k].iscur = 1;
666
667                 vid_wmodes++;
668
669         }
670
671         if (vid_wmodes > 0)
672         {
673                 M_Print (2*8, 36+0*8, "Fullscreen Modes (WIDTHxHEIGHTxBPP)");
674
675                 column = 8;
676                 row = 36+2*8;
677
678                 for (i=0 ; i<vid_wmodes ; i++)
679                 {
680                         if (modedescs[i].iscur)
681                                 M_PrintWhite (column, row, modedescs[i].desc);
682                         else
683                                 M_Print (column, row, modedescs[i].desc);
684
685                         column += 13*8;
686
687                         if ((i % VID_ROW_SIZE) == (VID_ROW_SIZE - 1))
688                         {
689                                 column = 8;
690                                 row += 8;
691                         }
692                 }
693         }
694
695         M_Print (3*8, 36 + MODE_AREA_HEIGHT * 8 + 8*2, "Video modes must be set from the");
696         M_Print (3*8, 36 + MODE_AREA_HEIGHT * 8 + 8*3, "command line with -width <width>");
697         M_Print (3*8, 36 + MODE_AREA_HEIGHT * 8 + 8*4, "and -bpp <bits-per-pixel>");
698         M_Print (3*8, 36 + MODE_AREA_HEIGHT * 8 + 8*6, "Select windowed mode with -window");
699 }
700
701
702 /*
703 ================
704 VID_MenuKey
705 ================
706 */
707 void VID_MenuKey (int key)
708 {
709         switch (key)
710         {
711         case K_ESCAPE:
712                 S_LocalSound ("misc/menu1.wav");
713                 M_Menu_Options_f ();
714                 break;
715
716         default:
717                 break;
718         }
719 }
720 #endif
721
722 static HINSTANCE gldll;
723
724 int GL_OpenLibrary(const char *name)
725 {
726         Con_Printf("Loading GL driver %s\n", name);
727         GL_CloseLibrary();
728         if (!(gldll = LoadLibrary(name)))
729         {
730                 Con_Printf("Unable to LoadLibrary %s\n", name);
731                 return false;
732         }
733         strcpy(gl_driver, name);
734         return true;
735 }
736
737 void GL_CloseLibrary(void)
738 {
739         FreeLibrary(gldll);
740         gldll = 0;
741         gl_driver[0] = 0;
742         qwglGetProcAddress = NULL;
743         gl_extensions = "";
744         gl_platform = "";
745         gl_platformextensions = "";
746 }
747
748 void *GL_GetProcAddress(const char *name)
749 {
750         void *p = NULL;
751         if (qwglGetProcAddress != NULL)
752                 p = (void *) qwglGetProcAddress(name);
753         if (p == NULL)
754                 p = (void *) GetProcAddress(gldll, name);
755         return p;
756 }
757
758 static void IN_Init(void);
759 void VID_Init(void)
760 {
761         WNDCLASS wc;
762
763         InitCommonControls();
764         hIcon = LoadIcon (global_hInstance, MAKEINTRESOURCE (IDI_ICON2));
765
766         // Register the frame class
767         wc.style         = 0;
768         wc.lpfnWndProc   = (WNDPROC)MainWndProc;
769         wc.cbClsExtra    = 0;
770         wc.cbWndExtra    = 0;
771         wc.hInstance     = global_hInstance;
772         wc.hIcon         = 0;
773         wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
774         wc.hbrBackground = NULL;
775         wc.lpszMenuName  = 0;
776         wc.lpszClassName = "DarkPlacesWindowClass";
777
778         if (!RegisterClass (&wc))
779                 Sys_Error("Couldn't register window class\n");
780
781         IN_Init();
782 }
783
784 int VID_InitMode (int fullscreen, int width, int height, int bpp, int stencil)
785 {
786         int i;
787         HDC hdc;
788         RECT rect;
789         MSG msg;
790         PIXELFORMATDESCRIPTOR pfd =
791         {
792                 sizeof(PIXELFORMATDESCRIPTOR),  // size of this pfd
793                 1,                              // version number
794                 PFD_DRAW_TO_WINDOW              // support window
795                 |  PFD_SUPPORT_OPENGL   // support OpenGL
796                 |  PFD_DOUBLEBUFFER ,   // double buffered
797                 PFD_TYPE_RGBA,                  // RGBA type
798                 24,                             // 24-bit color depth
799                 0, 0, 0, 0, 0, 0,               // color bits ignored
800                 0,                              // no alpha buffer
801                 0,                              // shift bit ignored
802                 0,                              // no accumulation buffer
803                 0, 0, 0, 0,                     // accum bits ignored
804                 32,                             // 32-bit z-buffer
805                 0,                              // no stencil buffer
806                 0,                              // no auxiliary buffer
807                 PFD_MAIN_PLANE,                 // main layer
808                 0,                              // reserved
809                 0, 0, 0                         // layer masks ignored
810         };
811         int pixelformat;
812         DWORD WindowStyle, ExWindowStyle;
813         HGLRC baseRC;
814         int CenterX, CenterY;
815         const char *gldrivername;
816         
817         if (vid_initialized)
818                 Sys_Error("VID_InitMode called when video is already initialised\n");
819
820         if (stencil)
821                 pfd.cStencilBits = 8;
822         else
823                 pfd.cStencilBits = 0;
824
825         gldrivername = "opengl32.dll";
826         i = COM_CheckParm("-gl_driver");
827         if (i && i < com_argc - 1)
828                 gldrivername = com_argv[i + 1];
829         if (!GL_OpenLibrary(gldrivername))
830         {
831                 Con_Printf("Unable to load GL driver %s\n", gldrivername);
832                 return false;
833         }
834
835         memset(&gdevmode, 0, sizeof(gdevmode));
836
837         VID_GetSystemGamma();
838
839         vid_isfullscreen = false;
840         if (fullscreen)
841         {
842                 gdevmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
843                 gdevmode.dmBitsPerPel = bpp;
844                 gdevmode.dmPelsWidth = width;
845                 gdevmode.dmPelsHeight = height;
846                 gdevmode.dmSize = sizeof (gdevmode);
847                 if (ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
848                 {
849                         VID_Shutdown();
850                         Con_Printf("Unable to change to requested mode %dx%dx%dbpp\n", width, height, bpp);
851                         return false;
852                 }
853
854                 vid_isfullscreen = true;
855                 WindowStyle = WS_POPUP;
856                 ExWindowStyle = 0;
857         }
858         else
859         {
860                 hdc = GetDC (NULL);
861                 i = GetDeviceCaps(hdc, RASTERCAPS);
862                 ReleaseDC (NULL, hdc);
863                 if (i & RC_PALETTE)
864                 {
865                         VID_Shutdown();
866                         Con_Printf ("Can't run in non-RGB mode\n");
867                         return false;
868                 }
869
870                 WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
871                 ExWindowStyle = 0;
872         }
873
874         rect.top = 0;
875         rect.left = 0;
876         rect.right = width;
877         rect.bottom = height;
878         AdjustWindowRectEx(&rect, WindowStyle, false, 0);
879
880         if (fullscreen)
881         {
882                 CenterX = 0;
883                 CenterY = 0;
884         }
885         else
886         {
887                 CenterX = (GetSystemMetrics(SM_CXSCREEN) - (rect.right - rect.left)) / 2;
888                 CenterY = (GetSystemMetrics(SM_CYSCREEN) - (rect.bottom - rect.top)) / 2;
889         }
890         CenterX = max(0, CenterX);
891         CenterY = max(0, CenterY);
892
893         // x and y may be changed by WM_MOVE messages
894         window_x = CenterX;
895         window_y = CenterY;
896         window_width = width;
897         window_height = height;
898         rect.left += CenterX;
899         rect.right += CenterX;
900         rect.top += CenterY;
901         rect.bottom += CenterY;
902
903         mainwindow = CreateWindowEx (ExWindowStyle, "DarkPlacesWindowClass", gamename, WindowStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, global_hInstance, NULL);
904         if (!mainwindow)
905         {
906                 VID_Shutdown();
907                 Con_Printf("CreateWindowEx(%d, %s, %s, %d, %d, %d, %d, %d, %p, %p, %d, %p) failed\n", ExWindowStyle, gamename, gamename, WindowStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, global_hInstance, NULL);
908                 return false;
909         }
910
911         /*
912         if (!fullscreen)
913                 SetWindowPos (mainwindow, NULL, CenterX, CenterY, 0, 0,SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME);
914         */
915
916         ShowWindow (mainwindow, SW_SHOWDEFAULT);
917         UpdateWindow (mainwindow);
918
919         SendMessage (mainwindow, WM_SETICON, (WPARAM)true, (LPARAM)hIcon);
920         SendMessage (mainwindow, WM_SETICON, (WPARAM)false, (LPARAM)hIcon);
921
922         VID_UpdateWindowStatus ();
923
924         // now we try to make sure we get the focus on the mode switch, because
925         // sometimes in some systems we don't.  We grab the foreground, then
926         // finish setting up, pump all our messages, and sleep for a little while
927         // to let messages finish bouncing around the system, then we put
928         // ourselves at the top of the z order, then grab the foreground again,
929         // Who knows if it helps, but it probably doesn't hurt
930         SetForegroundWindow (mainwindow);
931
932         while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
933         {
934                 TranslateMessage (&msg);
935                 DispatchMessage (&msg);
936         }
937
938         Sleep (100);
939
940         SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOCOPYBITS);
941
942         SetForegroundWindow (mainwindow);
943
944         // fix the leftover Alt from any Alt-Tab or the like that switched us away
945         ClearAllStates ();
946         
947         hdc = GetDC(mainwindow);
948
949         if ((pixelformat = ChoosePixelFormat(hdc, &pfd)) == 0)
950         {
951                 VID_Shutdown();
952                 Con_Printf("ChoosePixelFormat(%d, %p) failed\n", hdc, &pfd);
953                 return false;
954         }
955
956         if (SetPixelFormat(hdc, pixelformat, &pfd) == false)
957         {
958                 VID_Shutdown();
959                 Con_Printf("SetPixelFormat(%d, %d, %p) failed\n", hdc, pixelformat, &pfd);
960                 return false;
961         }
962
963         if (!GL_CheckExtension("wgl", wglfuncs, NULL, false))
964         {
965                 VID_Shutdown();
966                 Con_Printf("wgl functions not found\n");
967                 return false;
968         }
969
970         baseRC = qwglCreateContext(hdc);
971         if (!baseRC)
972         {
973                 VID_Shutdown();
974                 Con_Printf("Could not initialize GL (wglCreateContext failed).\n\nMake sure you are in 65536 color mode, and try running -window.\n");
975                 return false;
976         }
977         if (!qwglMakeCurrent(hdc, baseRC))
978         {
979                 VID_Shutdown();
980                 Con_Printf("wglMakeCurrent(%d, %d) failed\n", hdc, baseRC);
981                 return false;
982         }
983
984         qglGetString = GL_GetProcAddress("glGetString");
985         qwglGetExtensionsStringARB = GL_GetProcAddress("wglGetExtensionsStringARB");
986         if (qglGetString == NULL)
987         {
988                 VID_Shutdown();
989                 Con_Printf("glGetString not found\n");
990                 return false;
991         }
992         gl_renderer = qglGetString(GL_RENDERER);
993         gl_vendor = qglGetString(GL_VENDOR);
994         gl_version = qglGetString(GL_VERSION);
995         gl_extensions = qglGetString(GL_EXTENSIONS);
996         gl_platform = "WGL";
997         gl_platformextensions = "";
998
999         if (qwglGetExtensionsStringARB)
1000                 gl_platformextensions = qwglGetExtensionsStringARB(hdc);
1001
1002         gl_videosyncavailable = GL_CheckExtension("WGL_EXT_swap_control", wglswapintervalfuncs, NULL, false);
1003         ReleaseDC(mainwindow, hdc);
1004
1005         GL_Init ();
1006
1007         // LordHavoc: special differences for ATI (broken 8bit color when also using 32bit? weird!)
1008         if (strncasecmp(gl_vendor,"ATI",3)==0)
1009         {
1010                 if (strncasecmp(gl_renderer,"Rage Pro",8)==0)
1011                         isRagePro = true;
1012         }
1013         if (strncasecmp(gl_renderer,"Matrox G200 Direct3D",20)==0) // a D3D driver for GL? sigh...
1014                 isG200 = true;
1015
1016         //vid_menudrawfn = VID_MenuDraw;
1017         //vid_menukeyfn = VID_MenuKey;
1018         vid_hidden = false;
1019         vid_initialized = true;
1020
1021         IN_StartupMouse ();
1022         IN_StartupJoystick ();
1023
1024         return true;
1025 }
1026
1027 static void IN_Shutdown(void);
1028 void VID_Shutdown (void)
1029 {
1030         HGLRC hRC = 0;
1031         HDC hDC = 0;
1032
1033         if (vid_initialized)
1034         {
1035                 vid_initialized = false;
1036                 IN_Shutdown();
1037
1038                 if (qwglGetCurrentContext)
1039                         hRC = qwglGetCurrentContext();
1040
1041                 if (qwglGetCurrentDC)
1042                         hDC = qwglGetCurrentDC();
1043
1044                 if (qwglMakeCurrent)
1045                         qwglMakeCurrent(NULL, NULL);
1046
1047                 if (hRC && qwglDeleteContext)
1048                         qwglDeleteContext(hRC);
1049
1050                 // close the library before we get rid of the window
1051                 GL_CloseLibrary();
1052
1053                 if (hDC && mainwindow)
1054                         ReleaseDC(mainwindow, hDC);
1055
1056                 if (vid_isfullscreen)
1057                         ChangeDisplaySettings (NULL, 0);
1058
1059                 AppActivate(false, false);
1060
1061                 VID_RestoreSystemGamma();
1062         }
1063 }
1064
1065
1066 /*
1067 ===========
1068 IN_ShowMouse
1069 ===========
1070 */
1071 void IN_ShowMouse (void)
1072 {
1073         if (!mouseshowtoggle)
1074         {
1075                 ShowCursor (true);
1076                 mouseshowtoggle = 1;
1077         }
1078 }
1079
1080
1081 /*
1082 ===========
1083 IN_HideMouse
1084 ===========
1085 */
1086 void IN_HideMouse (void)
1087 {
1088         if (mouseshowtoggle)
1089         {
1090                 ShowCursor (false);
1091                 mouseshowtoggle = 0;
1092         }
1093 }
1094
1095
1096 /*
1097 ===========
1098 IN_ActivateMouse
1099 ===========
1100 */
1101 void IN_ActivateMouse (void)
1102 {
1103
1104         mouseactivatetoggle = true;
1105
1106         if (mouseinitialized)
1107         {
1108                 if (dinput)
1109                 {
1110                         if (g_pMouse)
1111                         {
1112                                 if (!dinput_acquired)
1113                                 {
1114                                         IDirectInputDevice_Acquire(g_pMouse);
1115                                         dinput_acquired = true;
1116                                 }
1117                         }
1118                         else
1119                         {
1120                                 return;
1121                         }
1122                 }
1123                 else
1124                 {
1125                         RECT window_rect;
1126                         window_rect.left = window_x;
1127                         window_rect.top = window_y;
1128                         window_rect.right = window_x + window_width;
1129                         window_rect.bottom = window_y + window_height;
1130
1131                         if (mouseparmsvalid)
1132                                 restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0);
1133
1134                         SetCursorPos (window_center_x, window_center_y);
1135                         SetCapture (mainwindow);
1136                         ClipCursor (&window_rect);
1137                 }
1138
1139                 mouseactive = true;
1140         }
1141 }
1142
1143
1144 /*
1145 ===========
1146 IN_DeactivateMouse
1147 ===========
1148 */
1149 void IN_DeactivateMouse (void)
1150 {
1151
1152         mouseactivatetoggle = false;
1153
1154         if (mouseinitialized)
1155         {
1156                 if (dinput)
1157                 {
1158                         if (g_pMouse)
1159                         {
1160                                 if (dinput_acquired)
1161                                 {
1162                                         IDirectInputDevice_Unacquire(g_pMouse);
1163                                         dinput_acquired = false;
1164                                 }
1165                         }
1166                 }
1167                 else
1168                 {
1169                         if (restore_spi)
1170                                 SystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0);
1171
1172                         ClipCursor (NULL);
1173                         ReleaseCapture ();
1174                 }
1175
1176                 mouseactive = false;
1177         }
1178 }
1179
1180
1181 /*
1182 ===========
1183 IN_InitDInput
1184 ===========
1185 */
1186 qboolean IN_InitDInput (void)
1187 {
1188     HRESULT             hr;
1189         DIPROPDWORD     dipdw = {
1190                 {
1191                         sizeof(DIPROPDWORD),        // diph.dwSize
1192                         sizeof(DIPROPHEADER),       // diph.dwHeaderSize
1193                         0,                          // diph.dwObj
1194                         DIPH_DEVICE,                // diph.dwHow
1195                 },
1196                 DINPUT_BUFFERSIZE,              // dwData
1197         };
1198
1199         if (!hInstDI)
1200         {
1201                 hInstDI = LoadLibrary("dinput.dll");
1202                 
1203                 if (hInstDI == NULL)
1204                 {
1205                         Con_SafePrintf ("Couldn't load dinput.dll\n");
1206                         return false;
1207                 }
1208         }
1209
1210         if (!pDirectInputCreate)
1211         {
1212                 pDirectInputCreate = (void *)GetProcAddress(hInstDI,"DirectInputCreateA");
1213
1214                 if (!pDirectInputCreate)
1215                 {
1216                         Con_SafePrintf ("Couldn't get DI proc addr\n");
1217                         return false;
1218                 }
1219         }
1220
1221 // register with DirectInput and get an IDirectInput to play with.
1222         hr = iDirectInputCreate(global_hInstance, DIRECTINPUT_VERSION, &g_pdi, NULL);
1223
1224         if (FAILED(hr))
1225         {
1226                 return false;
1227         }
1228
1229 // obtain an interface to the system mouse device.
1230         hr = IDirectInput_CreateDevice(g_pdi, &GUID_SysMouse, &g_pMouse, NULL);
1231
1232         if (FAILED(hr))
1233         {
1234                 Con_SafePrintf ("Couldn't open DI mouse device\n");
1235                 return false;
1236         }
1237
1238 // set the data format to "mouse format".
1239         hr = IDirectInputDevice_SetDataFormat(g_pMouse, &df);
1240
1241         if (FAILED(hr))
1242         {
1243                 Con_SafePrintf ("Couldn't set DI mouse format\n");
1244                 return false;
1245         }
1246
1247 // set the cooperativity level.
1248         hr = IDirectInputDevice_SetCooperativeLevel(g_pMouse, mainwindow,
1249                         DISCL_EXCLUSIVE | DISCL_FOREGROUND);
1250
1251         if (FAILED(hr))
1252         {
1253                 Con_SafePrintf ("Couldn't set DI coop level\n");
1254                 return false;
1255         }
1256
1257
1258 // set the buffer size to DINPUT_BUFFERSIZE elements.
1259 // the buffer size is a DWORD property associated with the device
1260         hr = IDirectInputDevice_SetProperty(g_pMouse, DIPROP_BUFFERSIZE, &dipdw.diph);
1261
1262         if (FAILED(hr))
1263         {
1264                 Con_SafePrintf ("Couldn't set DI buffersize\n");
1265                 return false;
1266         }
1267
1268         return true;
1269 }
1270
1271
1272 /*
1273 ===========
1274 IN_StartupMouse
1275 ===========
1276 */
1277 void IN_StartupMouse (void)
1278 {
1279         if (COM_CheckParm ("-nomouse") || COM_CheckParm("-safe")) 
1280                 return;
1281
1282         mouseinitialized = true;
1283
1284         if (COM_CheckParm ("-dinput"))
1285         {
1286                 dinput = IN_InitDInput ();
1287
1288                 if (dinput)
1289                 {
1290                         Con_SafePrintf ("DirectInput initialized\n");
1291                 }
1292                 else
1293                 {
1294                         Con_SafePrintf ("DirectInput not initialized\n");
1295                 }
1296         }
1297
1298         if (!dinput)
1299         {
1300                 mouseparmsvalid = SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0);
1301
1302                 if (mouseparmsvalid)
1303                 {
1304                         if ( COM_CheckParm ("-noforcemspd") ) 
1305                                 newmouseparms[2] = originalmouseparms[2];
1306
1307                         if ( COM_CheckParm ("-noforcemaccel") ) 
1308                         {
1309                                 newmouseparms[0] = originalmouseparms[0];
1310                                 newmouseparms[1] = originalmouseparms[1];
1311                         }
1312
1313                         if ( COM_CheckParm ("-noforcemparms") ) 
1314                         {
1315                                 newmouseparms[0] = originalmouseparms[0];
1316                                 newmouseparms[1] = originalmouseparms[1];
1317                                 newmouseparms[2] = originalmouseparms[2];
1318                         }
1319                 }
1320         }
1321
1322         mouse_buttons = 3;
1323
1324 // if a fullscreen video mode was set before the mouse was initialized,
1325 // set the mouse state appropriately
1326         if (mouseactivatetoggle)
1327                 IN_ActivateMouse ();
1328 }
1329
1330
1331 /*
1332 ===========
1333 IN_MouseEvent
1334 ===========
1335 */
1336 void IN_MouseEvent (int mstate)
1337 {
1338         int     i;
1339
1340         if (mouseactive && !dinput)
1341         {
1342         // perform button actions
1343                 for (i=0 ; i<mouse_buttons ; i++)
1344                 {
1345                         if ( (mstate & (1<<i)) &&
1346                                 !(mouse_oldbuttonstate & (1<<i)) )
1347                         {
1348                                 Key_Event (K_MOUSE1 + i, true);
1349                         }
1350
1351                         if ( !(mstate & (1<<i)) &&
1352                                 (mouse_oldbuttonstate & (1<<i)) )
1353                         {
1354                                 Key_Event (K_MOUSE1 + i, false);
1355                         }
1356                 }       
1357                         
1358                 mouse_oldbuttonstate = mstate;
1359         }
1360 }
1361
1362
1363 /*
1364 ===========
1365 IN_MouseMove
1366 ===========
1367 */
1368 void IN_MouseMove (usercmd_t *cmd)
1369 {
1370         int                                     i, mx, my;
1371         DIDEVICEOBJECTDATA      od;
1372         DWORD                           dwElements;
1373         HRESULT                         hr;
1374
1375         if (!mouseactive)
1376         {
1377                 GetCursorPos (&current_pos);
1378                 ui_mouseupdate(current_pos.x - window_x, current_pos.y - window_y);
1379                 return;
1380         }
1381
1382         if (dinput)
1383         {
1384                 mx = 0;
1385                 my = 0;
1386
1387                 for (;;)
1388                 {
1389                         dwElements = 1;
1390
1391                         hr = IDirectInputDevice_GetDeviceData(g_pMouse,
1392                                         sizeof(DIDEVICEOBJECTDATA), &od, &dwElements, 0);
1393
1394                         if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED))
1395                         {
1396                                 dinput_acquired = true;
1397                                 IDirectInputDevice_Acquire(g_pMouse);
1398                                 break;
1399                         }
1400
1401                         /* Unable to read data or no data available */
1402                         if (FAILED(hr) || dwElements == 0)
1403                                 break;
1404
1405                         /* Look at the element to see what happened */
1406
1407                         switch (od.dwOfs)
1408                         {
1409                                 case DIMOFS_X:
1410                                         mx += od.dwData;
1411                                         break;
1412
1413                                 case DIMOFS_Y:
1414                                         my += od.dwData;
1415                                         break;
1416
1417                                 case DIMOFS_BUTTON0:
1418                                         if (od.dwData & 0x80)
1419                                                 mstate_di |= 1;
1420                                         else
1421                                                 mstate_di &= ~1;
1422                                         break;
1423
1424                                 case DIMOFS_BUTTON1:
1425                                         if (od.dwData & 0x80)
1426                                                 mstate_di |= (1<<1);
1427                                         else
1428                                                 mstate_di &= ~(1<<1);
1429                                         break;
1430
1431                                 case DIMOFS_BUTTON2:
1432                                         if (od.dwData & 0x80)
1433                                                 mstate_di |= (1<<2);
1434                                         else
1435                                                 mstate_di &= ~(1<<2);
1436                                         break;
1437                         }
1438                 }
1439
1440         // perform button actions
1441                 for (i=0 ; i<mouse_buttons ; i++)
1442                 {
1443                         if ( (mstate_di & (1<<i)) &&
1444                                 !(mouse_oldbuttonstate & (1<<i)) )
1445                         {
1446                                 Key_Event (K_MOUSE1 + i, true);
1447                         }
1448
1449                         if ( !(mstate_di & (1<<i)) &&
1450                                 (mouse_oldbuttonstate & (1<<i)) )
1451                         {
1452                                 Key_Event (K_MOUSE1 + i, false);
1453                         }
1454                 }
1455
1456                 mouse_oldbuttonstate = mstate_di;
1457         }
1458         else
1459         {
1460                 GetCursorPos (&current_pos);
1461                 mx = current_pos.x - window_center_x + mx_accum;
1462                 my = current_pos.y - window_center_y + my_accum;
1463                 mx_accum = 0;
1464                 my_accum = 0;
1465         }
1466
1467         IN_Mouse(cmd, mx, my);
1468
1469         // if the mouse has moved, force it to the center, so there's room to move
1470         if (!dinput && (mx || my))
1471                 SetCursorPos (window_center_x, window_center_y);
1472 }
1473
1474
1475 /*
1476 ===========
1477 IN_Move
1478 ===========
1479 */
1480 void IN_Move (usercmd_t *cmd)
1481 {
1482         if (vid_activewindow && !vid_hidden)
1483         {
1484                 IN_MouseMove (cmd);
1485                 IN_JoyMove (cmd);
1486         }
1487 }
1488
1489
1490 /*
1491 ===========
1492 IN_Accumulate
1493 ===========
1494 */
1495 void IN_Accumulate (void)
1496 {
1497         if (mouseactive)
1498         {
1499                 if (!dinput)
1500                 {
1501                         GetCursorPos (&current_pos);
1502
1503                         mx_accum += current_pos.x - window_center_x;
1504                         my_accum += current_pos.y - window_center_y;
1505
1506                 // force the mouse to the center, so there's room to move
1507                         SetCursorPos (window_center_x, window_center_y);
1508                 }
1509         }
1510 }
1511
1512
1513 /*
1514 ===================
1515 IN_ClearStates
1516 ===================
1517 */
1518 void IN_ClearStates (void)
1519 {
1520         if (mouseactive)
1521         {
1522                 mx_accum = 0;
1523                 my_accum = 0;
1524                 mouse_oldbuttonstate = 0;
1525         }
1526 }
1527
1528
1529 /* 
1530 =============== 
1531 IN_StartupJoystick 
1532 ===============
1533 */  
1534 void IN_StartupJoystick (void) 
1535 {
1536         int                     numdevs;
1537         JOYCAPS         jc;
1538         MMRESULT        mmr;
1539         mmr = 0;
1540  
1541         // assume no joystick
1542         joy_avail = false; 
1543
1544         // abort startup if user requests no joystick
1545         if (COM_CheckParm ("-nojoy") || COM_CheckParm("-safe")) 
1546                 return; 
1547  
1548         // verify joystick driver is present
1549         if ((numdevs = joyGetNumDevs ()) == 0)
1550         {
1551                 Con_Printf ("\njoystick not found -- driver not present\n\n");
1552                 return;
1553         }
1554
1555         // cycle through the joystick ids for the first valid one
1556         for (joy_id=0 ; joy_id<numdevs ; joy_id++)
1557         {
1558                 memset (&ji, 0, sizeof(ji));
1559                 ji.dwSize = sizeof(ji);
1560                 ji.dwFlags = JOY_RETURNCENTERED;
1561
1562                 if ((mmr = joyGetPosEx (joy_id, &ji)) == JOYERR_NOERROR)
1563                         break;
1564         } 
1565
1566         // abort startup if we didn't find a valid joystick
1567         if (mmr != JOYERR_NOERROR)
1568         {
1569                 Con_Printf ("\njoystick not found -- no valid joysticks (%x)\n\n", mmr);
1570                 return;
1571         }
1572
1573         // get the capabilities of the selected joystick
1574         // abort startup if command fails
1575         memset (&jc, 0, sizeof(jc));
1576         if ((mmr = joyGetDevCaps (joy_id, &jc, sizeof(jc))) != JOYERR_NOERROR)
1577         {
1578                 Con_Printf ("\njoystick not found -- invalid joystick capabilities (%x)\n\n", mmr); 
1579                 return;
1580         }
1581
1582         // save the joystick's number of buttons and POV status
1583         joy_numbuttons = jc.wNumButtons;
1584         joy_haspov = jc.wCaps & JOYCAPS_HASPOV;
1585
1586         // old button and POV states default to no buttons pressed
1587         joy_oldbuttonstate = joy_oldpovstate = 0;
1588
1589         // mark the joystick as available and advanced initialization not completed
1590         // this is needed as cvars are not available during initialization
1591
1592         joy_avail = true; 
1593         joy_advancedinit = false;
1594
1595         Con_Printf ("\njoystick detected\n\n"); 
1596 }
1597
1598
1599 /*
1600 ===========
1601 RawValuePointer
1602 ===========
1603 */
1604 PDWORD RawValuePointer (int axis)
1605 {
1606         switch (axis)
1607         {
1608         case JOY_AXIS_X:
1609                 return &ji.dwXpos;
1610         case JOY_AXIS_Y:
1611                 return &ji.dwYpos;
1612         case JOY_AXIS_Z:
1613                 return &ji.dwZpos;
1614         case JOY_AXIS_R:
1615                 return &ji.dwRpos;
1616         case JOY_AXIS_U:
1617                 return &ji.dwUpos;
1618         case JOY_AXIS_V:
1619                 return &ji.dwVpos;
1620         }
1621         return NULL; // LordHavoc: hush compiler warning
1622 }
1623
1624
1625 /*
1626 ===========
1627 Joy_AdvancedUpdate_f
1628 ===========
1629 */
1630 void Joy_AdvancedUpdate_f (void)
1631 {
1632
1633         // called once by IN_ReadJoystick and by user whenever an update is needed
1634         // cvars are now available
1635         int     i;
1636         DWORD dwTemp;
1637
1638         // initialize all the maps
1639         for (i = 0; i < JOY_MAX_AXES; i++)
1640         {
1641                 dwAxisMap[i] = AxisNada;
1642                 dwControlMap[i] = JOY_ABSOLUTE_AXIS;
1643                 pdwRawValue[i] = RawValuePointer(i);
1644         }
1645
1646         if( joy_advanced.integer == 0)
1647         {
1648                 // default joystick initialization
1649                 // 2 axes only with joystick control
1650                 dwAxisMap[JOY_AXIS_X] = AxisTurn;
1651                 // dwControlMap[JOY_AXIS_X] = JOY_ABSOLUTE_AXIS;
1652                 dwAxisMap[JOY_AXIS_Y] = AxisForward;
1653                 // dwControlMap[JOY_AXIS_Y] = JOY_ABSOLUTE_AXIS;
1654         }
1655         else
1656         {
1657                 if (strcmp (joy_name.string, "joystick") != 0)
1658                 {
1659                         // notify user of advanced controller
1660                         Con_Printf ("\n%s configured\n\n", joy_name.string);
1661                 }
1662
1663                 // advanced initialization here
1664                 // data supplied by user via joy_axisn cvars
1665                 dwTemp = (DWORD) joy_advaxisx.value;
1666                 dwAxisMap[JOY_AXIS_X] = dwTemp & 0x0000000f;
1667                 dwControlMap[JOY_AXIS_X] = dwTemp & JOY_RELATIVE_AXIS;
1668                 dwTemp = (DWORD) joy_advaxisy.value;
1669                 dwAxisMap[JOY_AXIS_Y] = dwTemp & 0x0000000f;
1670                 dwControlMap[JOY_AXIS_Y] = dwTemp & JOY_RELATIVE_AXIS;
1671                 dwTemp = (DWORD) joy_advaxisz.value;
1672                 dwAxisMap[JOY_AXIS_Z] = dwTemp & 0x0000000f;
1673                 dwControlMap[JOY_AXIS_Z] = dwTemp & JOY_RELATIVE_AXIS;
1674                 dwTemp = (DWORD) joy_advaxisr.value;
1675                 dwAxisMap[JOY_AXIS_R] = dwTemp & 0x0000000f;
1676                 dwControlMap[JOY_AXIS_R] = dwTemp & JOY_RELATIVE_AXIS;
1677                 dwTemp = (DWORD) joy_advaxisu.value;
1678                 dwAxisMap[JOY_AXIS_U] = dwTemp & 0x0000000f;
1679                 dwControlMap[JOY_AXIS_U] = dwTemp & JOY_RELATIVE_AXIS;
1680                 dwTemp = (DWORD) joy_advaxisv.value;
1681                 dwAxisMap[JOY_AXIS_V] = dwTemp & 0x0000000f;
1682                 dwControlMap[JOY_AXIS_V] = dwTemp & JOY_RELATIVE_AXIS;
1683         }
1684
1685         // compute the axes to collect from DirectInput
1686         joy_flags = JOY_RETURNCENTERED | JOY_RETURNBUTTONS | JOY_RETURNPOV;
1687         for (i = 0; i < JOY_MAX_AXES; i++)
1688         {
1689                 if (dwAxisMap[i] != AxisNada)
1690                 {
1691                         joy_flags |= dwAxisFlags[i];
1692                 }
1693         }
1694 }
1695
1696
1697 /*
1698 ===========
1699 IN_Commands
1700 ===========
1701 */
1702 void IN_Commands (void)
1703 {
1704         int             i, key_index;
1705         DWORD   buttonstate, povstate;
1706
1707         if (!joy_avail)
1708         {
1709                 return;
1710         }
1711
1712
1713         // loop through the joystick buttons
1714         // key a joystick event or auxillary event for higher number buttons for each state change
1715         buttonstate = ji.dwButtons;
1716         for (i=0 ; i < (int) joy_numbuttons ; i++)
1717         {
1718                 if ( (buttonstate & (1<<i)) && !(joy_oldbuttonstate & (1<<i)) )
1719                 {
1720                         key_index = (i < 4) ? K_JOY1 : K_AUX1;
1721                         Key_Event (key_index + i, true);
1722                 }
1723
1724                 if ( !(buttonstate & (1<<i)) && (joy_oldbuttonstate & (1<<i)) )
1725                 {
1726                         key_index = (i < 4) ? K_JOY1 : K_AUX1;
1727                         Key_Event (key_index + i, false);
1728                 }
1729         }
1730         joy_oldbuttonstate = buttonstate;
1731
1732         if (joy_haspov)
1733         {
1734                 // convert POV information into 4 bits of state information
1735                 // this avoids any potential problems related to moving from one
1736                 // direction to another without going through the center position
1737                 povstate = 0;
1738                 if(ji.dwPOV != JOY_POVCENTERED)
1739                 {
1740                         if (ji.dwPOV == JOY_POVFORWARD)
1741                                 povstate |= 0x01;
1742                         if (ji.dwPOV == JOY_POVRIGHT)
1743                                 povstate |= 0x02;
1744                         if (ji.dwPOV == JOY_POVBACKWARD)
1745                                 povstate |= 0x04;
1746                         if (ji.dwPOV == JOY_POVLEFT)
1747                                 povstate |= 0x08;
1748                 }
1749                 // determine which bits have changed and key an auxillary event for each change
1750                 for (i=0 ; i < 4 ; i++)
1751                 {
1752                         if ( (povstate & (1<<i)) && !(joy_oldpovstate & (1<<i)) )
1753                         {
1754                                 Key_Event (K_AUX29 + i, true);
1755                         }
1756
1757                         if ( !(povstate & (1<<i)) && (joy_oldpovstate & (1<<i)) )
1758                         {
1759                                 Key_Event (K_AUX29 + i, false);
1760                         }
1761                 }
1762                 joy_oldpovstate = povstate;
1763         }
1764 }
1765
1766
1767 /* 
1768 =============== 
1769 IN_ReadJoystick
1770 =============== 
1771 */  
1772 qboolean IN_ReadJoystick (void)
1773 {
1774
1775         memset (&ji, 0, sizeof(ji));
1776         ji.dwSize = sizeof(ji);
1777         ji.dwFlags = joy_flags;
1778
1779         if (joyGetPosEx (joy_id, &ji) == JOYERR_NOERROR)
1780         {
1781                 // this is a hack -- there is a bug in the Logitech WingMan Warrior DirectInput Driver
1782                 // rather than having 32768 be the zero point, they have the zero point at 32668
1783                 // go figure -- anyway, now we get the full resolution out of the device
1784                 if (joy_wwhack1.integer != 0.0)
1785                 {
1786                         ji.dwUpos += 100;
1787                 }
1788                 return true;
1789         }
1790         else
1791         {
1792                 // read error occurred
1793                 // turning off the joystick seems too harsh for 1 read error,
1794                 // but what should be done?
1795                 return false;
1796         }
1797 }
1798
1799
1800 /*
1801 ===========
1802 IN_JoyMove
1803 ===========
1804 */
1805 void IN_JoyMove (usercmd_t *cmd)
1806 {
1807         float   speed, aspeed;
1808         float   fAxisValue, fTemp;
1809         int             i, mouselook = (in_mlook.state & 1) || freelook.integer;
1810
1811         // complete initialization if first time in
1812         // this is needed as cvars are not available at initialization time
1813         if( joy_advancedinit != true )
1814         {
1815                 Joy_AdvancedUpdate_f();
1816                 joy_advancedinit = true;
1817         }
1818
1819         // verify joystick is available and that the user wants to use it
1820         if (!joy_avail || !in_joystick.integer)
1821         {
1822                 return; 
1823         }
1824
1825         // collect the joystick data, if possible
1826         if (IN_ReadJoystick () != true)
1827         {
1828                 return;
1829         }
1830
1831         if (in_speed.state & 1)
1832                 speed = cl_movespeedkey.value;
1833         else
1834                 speed = 1;
1835         // LordHavoc: viewzoom affects sensitivity for sniping
1836         aspeed = speed * host_realframetime * cl.viewzoom;
1837
1838         // loop through the axes
1839         for (i = 0; i < JOY_MAX_AXES; i++)
1840         {
1841                 // get the floating point zero-centered, potentially-inverted data for the current axis
1842                 fAxisValue = (float) *pdwRawValue[i];
1843                 // move centerpoint to zero
1844                 fAxisValue -= 32768.0;
1845
1846                 if (joy_wwhack2.integer != 0.0)
1847                 {
1848                         if (dwAxisMap[i] == AxisTurn)
1849                         {
1850                                 // this is a special formula for the Logitech WingMan Warrior
1851                                 // y=ax^b; where a = 300 and b = 1.3
1852                                 // also x values are in increments of 800 (so this is factored out)
1853                                 // then bounds check result to level out excessively high spin rates
1854                                 fTemp = 300.0 * pow(abs(fAxisValue) / 800.0, 1.3);
1855                                 if (fTemp > 14000.0)
1856                                         fTemp = 14000.0;
1857                                 // restore direction information
1858                                 fAxisValue = (fAxisValue > 0.0) ? fTemp : -fTemp;
1859                         }
1860                 }
1861
1862                 // convert range from -32768..32767 to -1..1 
1863                 fAxisValue /= 32768.0;
1864
1865                 switch (dwAxisMap[i])
1866                 {
1867                 case AxisForward:
1868                         if ((joy_advanced.integer == 0) && mouselook)
1869                         {
1870                                 // user wants forward control to become look control
1871                                 if (fabs(fAxisValue) > joy_pitchthreshold.value)
1872                                 {               
1873                                         // if mouse invert is on, invert the joystick pitch value
1874                                         // only absolute control support here (joy_advanced is false)
1875                                         if (m_pitch.value < 0.0)
1876                                         {
1877                                                 cl.viewangles[PITCH] -= (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value;
1878                                         }
1879                                         else
1880                                         {
1881                                                 cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value;
1882                                         }
1883                                         V_StopPitchDrift();
1884                                 }
1885                                 else
1886                                 {
1887                                         // no pitch movement
1888                                         // disable pitch return-to-center unless requested by user
1889                                         // *** this code can be removed when the lookspring bug is fixed
1890                                         // *** the bug always has the lookspring feature on
1891                                         if(lookspring.value == 0.0)
1892                                                 V_StopPitchDrift();
1893                                 }
1894                         }
1895                         else
1896                         {
1897                                 // user wants forward control to be forward control
1898                                 if (fabs(fAxisValue) > joy_forwardthreshold.value)
1899                                 {
1900                                         cmd->forwardmove += (fAxisValue * joy_forwardsensitivity.value) * speed * cl_forwardspeed.value;
1901                                 }
1902                         }
1903                         break;
1904
1905                 case AxisSide:
1906                         if (fabs(fAxisValue) > joy_sidethreshold.value)
1907                         {
1908                                 cmd->sidemove += (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value;
1909                         }
1910                         break;
1911
1912                 case AxisTurn:
1913                         if ((in_strafe.state & 1) || (lookstrafe.integer && mouselook))
1914                         {
1915                                 // user wants turn control to become side control
1916                                 if (fabs(fAxisValue) > joy_sidethreshold.value)
1917                                 {
1918                                         cmd->sidemove -= (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value;
1919                                 }
1920                         }
1921                         else
1922                         {
1923                                 // user wants turn control to be turn control
1924                                 if (fabs(fAxisValue) > joy_yawthreshold.value)
1925                                 {
1926                                         if(dwControlMap[i] == JOY_ABSOLUTE_AXIS)
1927                                         {
1928                                                 cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity.value) * aspeed * cl_yawspeed.value;
1929                                         }
1930                                         else
1931                                         {
1932                                                 cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity.value) * speed * 180.0;
1933                                         }
1934
1935                                 }
1936                         }
1937                         break;
1938
1939                 case AxisLook:
1940                         if (mouselook)
1941                         {
1942                                 if (fabs(fAxisValue) > joy_pitchthreshold.value)
1943                                 {
1944                                         // pitch movement detected and pitch movement desired by user
1945                                         if(dwControlMap[i] == JOY_ABSOLUTE_AXIS)
1946                                         {
1947                                                 cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value;
1948                                         }
1949                                         else
1950                                         {
1951                                                 cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * speed * 180.0;
1952                                         }
1953                                         V_StopPitchDrift();
1954                                 }
1955                                 else
1956                                 {
1957                                         // no pitch movement
1958                                         // disable pitch return-to-center unless requested by user
1959                                         // *** this code can be removed when the lookspring bug is fixed
1960                                         // *** the bug always has the lookspring feature on
1961                                         if(lookspring.integer == 0)
1962                                                 V_StopPitchDrift();
1963                                 }
1964                         }
1965                         break;
1966
1967                 default:
1968                         break;
1969                 }
1970         }
1971 }
1972
1973 static void IN_Init(void)
1974 {
1975         uiWheelMessage = RegisterWindowMessage ( "MSWHEEL_ROLLMSG" );
1976
1977         // joystick variables
1978         Cvar_RegisterVariable (&in_joystick);
1979         Cvar_RegisterVariable (&joy_name);
1980         Cvar_RegisterVariable (&joy_advanced);
1981         Cvar_RegisterVariable (&joy_advaxisx);
1982         Cvar_RegisterVariable (&joy_advaxisy);
1983         Cvar_RegisterVariable (&joy_advaxisz);
1984         Cvar_RegisterVariable (&joy_advaxisr);
1985         Cvar_RegisterVariable (&joy_advaxisu);
1986         Cvar_RegisterVariable (&joy_advaxisv);
1987         Cvar_RegisterVariable (&joy_forwardthreshold);
1988         Cvar_RegisterVariable (&joy_sidethreshold);
1989         Cvar_RegisterVariable (&joy_pitchthreshold);
1990         Cvar_RegisterVariable (&joy_yawthreshold);
1991         Cvar_RegisterVariable (&joy_forwardsensitivity);
1992         Cvar_RegisterVariable (&joy_sidesensitivity);
1993         Cvar_RegisterVariable (&joy_pitchsensitivity);
1994         Cvar_RegisterVariable (&joy_yawsensitivity);
1995         Cvar_RegisterVariable (&joy_wwhack1);
1996         Cvar_RegisterVariable (&joy_wwhack2);
1997         Cmd_AddCommand ("joyadvancedupdate", Joy_AdvancedUpdate_f);
1998 }
1999
2000 static void IN_Shutdown(void)
2001 {
2002         IN_DeactivateMouse ();
2003         IN_ShowMouse ();
2004
2005         if (g_pMouse)
2006                 IDirectInputDevice_Release(g_pMouse);
2007         g_pMouse = NULL;
2008
2009         if (g_pdi)
2010                 IDirectInput_Release(g_pdi);
2011         g_pdi = NULL;
2012 }