make infobar height configurable
[divverent/darkplaces.git] / vid_sdl.c
1 /*
2 Copyright (C) 2003  T. Joseph Carter
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 #undef WIN32_LEAN_AND_MEAN  //hush a warning, SDL.h redefines this
20 #include <SDL.h>
21 #include <SDL_syswm.h>
22 #include <stdio.h>
23
24 #include "quakedef.h"
25 #include "image.h"
26
27 #ifdef MACOSX
28 #include <Carbon/Carbon.h>
29 #include <IOKit/hidsystem/IOHIDLib.h>
30 #include <IOKit/hidsystem/IOHIDParameter.h>
31 #include <IOKit/hidsystem/event_status_driver.h>
32 static cvar_t apple_mouse_noaccel = {CVAR_SAVE, "apple_mouse_noaccel", "1", "disables mouse acceleration while DarkPlaces is active"};
33 static qboolean vid_usingnoaccel;
34 static double originalMouseSpeed = -1.0;
35 io_connect_t IN_GetIOHandle(void)
36 {
37         io_connect_t iohandle = MACH_PORT_NULL;
38         kern_return_t status;
39         io_service_t iohidsystem = MACH_PORT_NULL;
40         mach_port_t masterport;
41
42         status = IOMasterPort(MACH_PORT_NULL, &masterport);
43         if(status != KERN_SUCCESS)
44                 return 0;
45
46         iohidsystem = IORegistryEntryFromPath(masterport, kIOServicePlane ":/IOResources/IOHIDSystem");
47         if(!iohidsystem)
48                 return 0;
49
50         status = IOServiceOpen(iohidsystem, mach_task_self(), kIOHIDParamConnectType, &iohandle);
51         IOObjectRelease(iohidsystem);
52
53         return iohandle;
54 }
55 #endif
56
57 #ifdef WIN32
58 #define SDL_R_RESTART
59 #endif
60
61 // Tell startup code that we have a client
62 int cl_available = true;
63
64 qboolean vid_supportrefreshrate = false;
65
66 cvar_t joy_detected = {CVAR_READONLY, "joy_detected", "0", "number of joysticks detected by engine"};
67 cvar_t joy_enable = {CVAR_SAVE, "joy_enable", "0", "enables joystick support"};
68 cvar_t joy_index = {0, "joy_index", "0", "selects which joystick to use if you have multiple"};
69 cvar_t joy_axisforward = {0, "joy_axisforward", "1", "which joystick axis to query for forward/backward movement"};
70 cvar_t joy_axisside = {0, "joy_axisside", "0", "which joystick axis to query for right/left movement"};
71 cvar_t joy_axisup = {0, "joy_axisup", "-1", "which joystick axis to query for up/down movement"};
72 cvar_t joy_axispitch = {0, "joy_axispitch", "3", "which joystick axis to query for looking up/down"};
73 cvar_t joy_axisyaw = {0, "joy_axisyaw", "2", "which joystick axis to query for looking right/left"};
74 cvar_t joy_axisroll = {0, "joy_axisroll", "-1", "which joystick axis to query for tilting head right/left"};
75 cvar_t joy_deadzoneforward = {0, "joy_deadzoneforward", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
76 cvar_t joy_deadzoneside = {0, "joy_deadzoneside", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
77 cvar_t joy_deadzoneup = {0, "joy_deadzoneup", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
78 cvar_t joy_deadzonepitch = {0, "joy_deadzonepitch", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
79 cvar_t joy_deadzoneyaw = {0, "joy_deadzoneyaw", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
80 cvar_t joy_deadzoneroll = {0, "joy_deadzoneroll", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
81 cvar_t joy_sensitivityforward = {0, "joy_sensitivityforward", "-1", "movement multiplier"};
82 cvar_t joy_sensitivityside = {0, "joy_sensitivityside", "1", "movement multiplier"};
83 cvar_t joy_sensitivityup = {0, "joy_sensitivityup", "1", "movement multiplier"};
84 cvar_t joy_sensitivitypitch = {0, "joy_sensitivitypitch", "1", "movement multiplier"};
85 cvar_t joy_sensitivityyaw = {0, "joy_sensitivityyaw", "-1", "movement multiplier"};
86 cvar_t joy_sensitivityroll = {0, "joy_sensitivityroll", "1", "movement multiplier"};
87 cvar_t joy_axiskeyevents = {CVAR_SAVE, "joy_axiskeyevents", "0", "generate uparrow/leftarrow etc. keyevents for joystick axes, use if your joystick driver is not generating them"};
88
89 static qboolean vid_usingmouse = false;
90 static qboolean vid_usinghidecursor = false;
91 static qboolean vid_isfullscreen;
92 static int vid_numjoysticks = 0;
93 #define MAX_JOYSTICKS 8
94 static SDL_Joystick *vid_joysticks[MAX_JOYSTICKS];
95
96 static int win_half_width = 50;
97 static int win_half_height = 50;
98 static int video_bpp, video_flags;
99
100 static SDL_Surface *screen;
101
102 // joystick axes state
103 #define MAX_JOYSTICK_AXES       16
104 typedef struct
105 {
106         float oldmove;
107         float move;
108         double keytime;
109 }joy_axiscache_t;
110 static joy_axiscache_t joy_axescache[MAX_JOYSTICK_AXES];
111
112 /////////////////////////
113 // Input handling
114 ////
115 //TODO: Add joystick support
116 //TODO: Add error checking
117
118
119 //keysym to quake keysym mapping
120 #define tenoh   0,0,0,0,0, 0,0,0,0,0
121 #define fiftyoh tenoh, tenoh, tenoh, tenoh, tenoh
122 #define hundredoh fiftyoh, fiftyoh
123 static unsigned int tbl_sdltoquake[] =
124 {
125         0,0,0,0,                //SDLK_UNKNOWN          = 0,
126         0,0,0,0,                //SDLK_FIRST            = 0,
127         K_BACKSPACE,    //SDLK_BACKSPACE        = 8,
128         K_TAB,                  //SDLK_TAB                      = 9,
129         0,0,
130         0,                              //SDLK_CLEAR            = 12,
131         K_ENTER,                //SDLK_RETURN           = 13,
132     0,0,0,0,0,
133         K_PAUSE,                //SDLK_PAUSE            = 19,
134         0,0,0,0,0,0,0,
135         K_ESCAPE,               //SDLK_ESCAPE           = 27,
136         0,0,0,0,
137         K_SPACE,                //SDLK_SPACE            = 32,
138         '!',                    //SDLK_EXCLAIM          = 33,
139         '"',                    //SDLK_QUOTEDBL         = 34,
140         '#',                    //SDLK_HASH                     = 35,
141         '$',                    //SDLK_DOLLAR           = 36,
142         0,
143         '&',                    //SDLK_AMPERSAND        = 38,
144         '\'',                   //SDLK_QUOTE            = 39,
145         '(',                    //SDLK_LEFTPAREN        = 40,
146         ')',                    //SDLK_RIGHTPAREN       = 41,
147         '*',                    //SDLK_ASTERISK         = 42,
148         '+',                    //SDLK_PLUS                     = 43,
149         ',',                    //SDLK_COMMA            = 44,
150         '-',                    //SDLK_MINUS            = 45,
151         '.',                    //SDLK_PERIOD           = 46,
152         '/',                    //SDLK_SLASH            = 47,
153         '0',                    //SDLK_0                        = 48,
154         '1',                    //SDLK_1                        = 49,
155         '2',                    //SDLK_2                        = 50,
156         '3',                    //SDLK_3                        = 51,
157         '4',                    //SDLK_4                        = 52,
158         '5',                    //SDLK_5                        = 53,
159         '6',                    //SDLK_6                        = 54,
160         '7',                    //SDLK_7                        = 55,
161         '8',                    //SDLK_8                        = 56,
162         '9',                    //SDLK_9                        = 57,
163         ':',                    //SDLK_COLON            = 58,
164         ';',                    //SDLK_SEMICOLON        = 59,
165         '<',                    //SDLK_LESS                     = 60,
166         '=',                    //SDLK_EQUALS           = 61,
167         '>',                    //SDLK_GREATER          = 62,
168         '?',                    //SDLK_QUESTION         = 63,
169         '@',                    //SDLK_AT                       = 64,
170         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
171         '[',            //SDLK_LEFTBRACKET      = 91,
172         '\\',           //SDLK_BACKSLASH        = 92,
173         ']',            //SDLK_RIGHTBRACKET     = 93,
174         '^',            //SDLK_CARET            = 94,
175         '_',            //SDLK_UNDERSCORE       = 95,
176         '`',            //SDLK_BACKQUOTE        = 96,
177         'a',            //SDLK_a                        = 97,
178         'b',            //SDLK_b                        = 98,
179         'c',            //SDLK_c                        = 99,
180         'd',            //SDLK_d                        = 100,
181         'e',            //SDLK_e                        = 101,
182         'f',            //SDLK_f                        = 102,
183         'g',            //SDLK_g                        = 103,
184         'h',            //SDLK_h                        = 104,
185         'i',            //SDLK_i                        = 105,
186         'j',            //SDLK_j                        = 106,
187         'k',            //SDLK_k                        = 107,
188         'l',            //SDLK_l                        = 108,
189         'm',            //SDLK_m                        = 109,
190         'n',            //SDLK_n                        = 110,
191         'o',            //SDLK_o                        = 111,
192         'p',            //SDLK_p                        = 112,
193         'q',            //SDLK_q                        = 113,
194         'r',            //SDLK_r                        = 114,
195         's',            //SDLK_s                        = 115,
196         't',            //SDLK_t                        = 116,
197         'u',            //SDLK_u                        = 117,
198         'v',            //SDLK_v                        = 118,
199         'w',            //SDLK_w                        = 119,
200         'x',            //SDLK_x                        = 120,
201         'y',            //SDLK_y                        = 121,
202         'z',            //SDLK_z                        = 122,
203         0,0,0,0,
204         K_DEL,          //SDLK_DELETE           = 127,
205         hundredoh /*227*/, tenoh, tenoh, 0,0,0,0,0,0,0,0,
206         K_KP_0,         //SDLK_KP0              = 256,
207         K_KP_1,         //SDLK_KP1              = 257,
208         K_KP_2,         //SDLK_KP2              = 258,
209         K_KP_3,         //SDLK_KP3              = 259,
210         K_KP_4,         //SDLK_KP4              = 260,
211         K_KP_5,         //SDLK_KP5              = 261,
212         K_KP_6,         //SDLK_KP6              = 262,
213         K_KP_7,         //SDLK_KP7              = 263,
214         K_KP_8,         //SDLK_KP8              = 264,
215         K_KP_9,         //SDLK_KP9              = 265,
216         K_KP_PERIOD,//SDLK_KP_PERIOD    = 266,
217         K_KP_DIVIDE,//SDLK_KP_DIVIDE    = 267,
218         K_KP_MULTIPLY,//SDLK_KP_MULTIPLY= 268,
219         K_KP_MINUS,     //SDLK_KP_MINUS         = 269,
220         K_KP_PLUS,      //SDLK_KP_PLUS          = 270,
221         K_KP_ENTER,     //SDLK_KP_ENTER         = 271,
222         K_KP_EQUALS,//SDLK_KP_EQUALS    = 272,
223         K_UPARROW,      //SDLK_UP               = 273,
224         K_DOWNARROW,//SDLK_DOWN         = 274,
225         K_RIGHTARROW,//SDLK_RIGHT       = 275,
226         K_LEFTARROW,//SDLK_LEFT         = 276,
227         K_INS,          //SDLK_INSERT   = 277,
228         K_HOME,         //SDLK_HOME             = 278,
229         K_END,          //SDLK_END              = 279,
230         K_PGUP,         //SDLK_PAGEUP   = 280,
231         K_PGDN,         //SDLK_PAGEDOWN = 281,
232         K_F1,           //SDLK_F1               = 282,
233         K_F2,           //SDLK_F2               = 283,
234         K_F3,           //SDLK_F3               = 284,
235         K_F4,           //SDLK_F4               = 285,
236         K_F5,           //SDLK_F5               = 286,
237         K_F6,           //SDLK_F6               = 287,
238         K_F7,           //SDLK_F7               = 288,
239         K_F8,           //SDLK_F8               = 289,
240         K_F9,           //SDLK_F9               = 290,
241         K_F10,          //SDLK_F10              = 291,
242         K_F11,          //SDLK_F11              = 292,
243         K_F12,          //SDLK_F12              = 293,
244         0,                      //SDLK_F13              = 294,
245         0,                      //SDLK_F14              = 295,
246         0,                      //SDLK_F15              = 296,
247         0,0,0,
248         K_NUMLOCK,      //SDLK_NUMLOCK  = 300,
249         K_CAPSLOCK,     //SDLK_CAPSLOCK = 301,
250         K_SCROLLOCK,//SDLK_SCROLLOCK= 302,
251         K_SHIFT,        //SDLK_RSHIFT   = 303,
252         K_SHIFT,        //SDLK_LSHIFT   = 304,
253         K_CTRL,         //SDLK_RCTRL    = 305,
254         K_CTRL,         //SDLK_LCTRL    = 306,
255         K_ALT,          //SDLK_RALT             = 307,
256         K_ALT,          //SDLK_LALT             = 308,
257         0,                      //SDLK_RMETA    = 309,
258         0,                      //SDLK_LMETA    = 310,
259         0,                      //SDLK_LSUPER   = 311,          /* Left "Windows" key */
260         0,                      //SDLK_RSUPER   = 312,          /* Right "Windows" key */
261         K_ALT,                  //SDLK_MODE             = 313,          /* "Alt Gr" key */
262         0,                      //SDLK_COMPOSE  = 314,          /* Multi-key compose key */
263         0,                      //SDLK_HELP             = 315,
264         0,                      //SDLK_PRINT    = 316,
265         0,                      //SDLK_SYSREQ   = 317,
266         K_PAUSE,        //SDLK_BREAK    = 318,
267         0,                      //SDLK_MENU             = 319,
268         0,                      //SDLK_POWER    = 320,          /* Power Macintosh power key */
269         'e',            //SDLK_EURO             = 321,          /* Some european keyboards */
270         0                       //SDLK_UNDO             = 322,          /* Atari keyboard has Undo */
271 };
272 #undef tenoh
273 #undef fiftyoh
274 #undef hundredoh
275
276 static int MapKey( unsigned int sdlkey )
277 {
278         if( sdlkey > sizeof(tbl_sdltoquake)/ sizeof(int) )
279                 return 0;
280     return tbl_sdltoquake[ sdlkey ];
281 }
282
283 void VID_SetMouse(qboolean fullscreengrab, qboolean relative, qboolean hidecursor)
284 {
285 #ifdef MACOSX
286         if(relative)
287                 if(vid_usingmouse && (vid_usingnoaccel != !!apple_mouse_noaccel.integer))
288                         VID_SetMouse(false, false, false); // ungrab first!
289 #endif
290         if (vid_usingmouse != relative)
291         {
292                 vid_usingmouse = relative;
293                 cl_ignoremousemoves = 2;
294                 SDL_WM_GrabInput( relative ? SDL_GRAB_ON : SDL_GRAB_OFF );
295 #ifdef MACOSX
296                 if(relative)
297                 {
298                         // Save the status of mouse acceleration
299                         originalMouseSpeed = -1.0; // in case of error
300                         if(apple_mouse_noaccel.integer)
301                         {
302                                 io_connect_t mouseDev = IN_GetIOHandle();
303                                 if(mouseDev != 0)
304                                 {
305                                         if(IOHIDGetAccelerationWithKey(mouseDev, CFSTR(kIOHIDMouseAccelerationType), &originalMouseSpeed) == kIOReturnSuccess)
306                                         {
307                                                 Con_DPrintf("previous mouse acceleration: %f\n", originalMouseSpeed);
308                                                 if(IOHIDSetAccelerationWithKey(mouseDev, CFSTR(kIOHIDMouseAccelerationType), -1.0) != kIOReturnSuccess)
309                                                 {
310                                                         Con_Print("Could not disable mouse acceleration (failed at IOHIDSetAccelerationWithKey).\n");
311                                                         Cvar_SetValueQuick(&apple_mouse_noaccel, 0);
312                                                 }
313                                         }
314                                         else
315                                         {
316                                                 Con_Print("Could not disable mouse acceleration (failed at IOHIDGetAccelerationWithKey).\n");
317                                                 Cvar_SetValueQuick(&apple_mouse_noaccel, 0);
318                                         }
319                                         IOServiceClose(mouseDev);
320                                 }
321                                 else
322                                 {
323                                         Con_Print("Could not disable mouse acceleration (failed at IO_GetIOHandle).\n");
324                                         Cvar_SetValueQuick(&apple_mouse_noaccel, 0);
325                                 }
326                         }
327
328                         vid_usingnoaccel = !!apple_mouse_noaccel.integer;
329                 }
330                 else
331                 {
332                         if(originalMouseSpeed != -1.0)
333                         {
334                                 io_connect_t mouseDev = IN_GetIOHandle();
335                                 if(mouseDev != 0)
336                                 {
337                                         Con_DPrintf("restoring mouse acceleration to: %f\n", originalMouseSpeed);
338                                         if(IOHIDSetAccelerationWithKey(mouseDev, CFSTR(kIOHIDMouseAccelerationType), originalMouseSpeed) != kIOReturnSuccess)
339                                                 Con_Print("Could not re-enable mouse acceleration (failed at IOHIDSetAccelerationWithKey).\n");
340                                         IOServiceClose(mouseDev);
341                                 }
342                                 else
343                                         Con_Print("Could not re-enable mouse acceleration (failed at IO_GetIOHandle).\n");
344                         }
345                 }
346 #endif
347         }
348         if (vid_usinghidecursor != hidecursor)
349         {
350                 vid_usinghidecursor = hidecursor;
351                 SDL_ShowCursor( hidecursor ? SDL_DISABLE : SDL_ENABLE);
352         }
353 }
354
355 static double IN_JoystickGetAxis(SDL_Joystick *joy, int axis, double sensitivity, double deadzone)
356 {
357         double value;
358         if (axis < 0 || axis >= SDL_JoystickNumAxes(joy))
359                 return 0; // no such axis on this joystick
360         value = SDL_JoystickGetAxis(joy, axis) * (1.0 / 32767.0);
361         value = bound(-1, value, 1);
362         if (fabs(value) < deadzone)
363                 return 0; // within deadzone around center
364         return value * sensitivity;
365 }
366
367 /////////////////////
368 // Joystick axis keyevents
369 // a sort of hack emulating Arrow keys for joystick axises
370 // as some drives dont send such keyevents for them
371 // additionally we should block drivers that do send arrow keyevents to prevent double events
372 ////
373
374 static void IN_JoystickKeyeventForAxis(SDL_Joystick *joy, int axis, int key_pos, int key_neg)
375 {
376         double joytime;
377
378         if (axis < 0 || axis >= SDL_JoystickNumAxes(joy))
379                 return; // no such axis on this joystick
380
381         joytime = Sys_DoubleTime();
382         // no key event, continuous keydown event
383         if (joy_axescache[axis].move == joy_axescache[axis].oldmove)
384         {
385                 if (joy_axescache[axis].move != 0 && joytime > joy_axescache[axis].keytime)
386                 {
387                         //Con_Printf("joy %s %i %f\n", Key_KeynumToString((joy_axescache[axis].move > 0) ? key_pos : key_neg), 1, cl.time);
388                         Key_Event((joy_axescache[axis].move > 0) ? key_pos : key_neg, 0, 1);
389                         joy_axescache[axis].keytime = joytime + 0.5 / 20;
390                 }
391                 return;
392         }
393         // generate key up event
394         if (joy_axescache[axis].oldmove)
395         {
396                 //Con_Printf("joy %s %i %f\n", Key_KeynumToString((joy_axescache[axis].oldmove > 0) ? key_pos : key_neg), 1, cl.time);
397                 Key_Event((joy_axescache[axis].oldmove > 0) ? key_pos : key_neg, 0, 0);
398         }
399         // generate key down event
400         if (joy_axescache[axis].move)
401         {
402                 //Con_Printf("joy %s %i %f\n", Key_KeynumToString((joy_axescache[axis].move > 0) ? key_pos : key_neg), 1, cl.time);
403                 Key_Event((joy_axescache[axis].move > 0) ? key_pos : key_neg, 0, 1);
404                 joy_axescache[axis].keytime = joytime + 0.5;
405         }
406 }
407
408 static qboolean IN_JoystickBlockDoubledKeyEvents(int keycode)
409 {
410         if (!joy_axiskeyevents.integer)
411                 return false;
412
413         // block keyevent if it's going to be provided by joystick keyevent system
414         if (vid_numjoysticks && joy_enable.integer && joy_index.integer >= 0 && joy_index.integer < vid_numjoysticks)
415         {
416                 SDL_Joystick *joy = vid_joysticks[joy_index.integer];
417
418                 if (keycode == K_UPARROW || keycode == K_DOWNARROW)
419                         if (IN_JoystickGetAxis(joy, joy_axisforward.integer, 1, 0.01) || joy_axescache[joy_axisforward.integer].move || joy_axescache[joy_axisforward.integer].oldmove)
420                                 return true;
421                 if (keycode == K_RIGHTARROW || keycode == K_LEFTARROW)
422                         if (IN_JoystickGetAxis(joy, joy_axisside.integer, 1, 0.01) || joy_axescache[joy_axisside.integer].move || joy_axescache[joy_axisside.integer].oldmove)
423                                 return true;
424         }
425
426         return false;
427 }
428
429 /////////////////////
430 // Movement handling
431 ////
432
433 void IN_Move( void )
434 {
435         int j;
436         static int old_x = 0, old_y = 0;
437         static int stuck = 0;
438         int x, y, numaxes, numballs;
439
440         if (vid_usingmouse)
441         {
442                 if(vid_stick_mouse.integer)
443                 {
444                         // have the mouse stuck in the middle, example use: prevent expose effect of beryl during the game when not using
445                         // window grabbing. --blub
446
447                         // we need 2 frames to initialize the center position
448                         if(!stuck)
449                         {
450                                 SDL_WarpMouse(win_half_width, win_half_height);
451                                 SDL_GetMouseState(&x, &y);
452                                 SDL_GetRelativeMouseState(&x, &y);
453                                 ++stuck;
454                         } else {
455                                 SDL_GetRelativeMouseState(&x, &y);
456                                 in_mouse_x = x + old_x;
457                                 in_mouse_y = y + old_y;
458                                 SDL_GetMouseState(&x, &y);
459                                 old_x = x - win_half_width;
460                                 old_y = y - win_half_height;
461                                 SDL_WarpMouse(win_half_width, win_half_height);
462                         }
463                 } else {
464                         SDL_GetRelativeMouseState( &x, &y );
465                         in_mouse_x = x;
466                         in_mouse_y = y;
467                 }
468         }
469
470         SDL_GetMouseState(&x, &y);
471         in_windowmouse_x = x;
472         in_windowmouse_y = y;
473
474         if (vid_numjoysticks && joy_enable.integer && joy_index.integer >= 0 && joy_index.integer < vid_numjoysticks)
475         {
476                 SDL_Joystick *joy = vid_joysticks[joy_index.integer];
477
478                 // balls convert to mousemove
479                 numballs = SDL_JoystickNumBalls(joy);
480                 for (j = 0;j < numballs;j++)
481                 {
482                         SDL_JoystickGetBall(joy, j, &x, &y);
483                         in_mouse_x += x;
484                         in_mouse_y += y;
485                 }
486
487                 // axes
488                 cl.cmd.forwardmove += IN_JoystickGetAxis(joy, joy_axisforward.integer, joy_sensitivityforward.value, joy_deadzoneforward.value) * cl_forwardspeed.value;
489                 cl.cmd.sidemove    += IN_JoystickGetAxis(joy, joy_axisside.integer, joy_sensitivityside.value, joy_deadzoneside.value) * cl_sidespeed.value;
490                 cl.cmd.upmove      += IN_JoystickGetAxis(joy, joy_axisup.integer, joy_sensitivityup.value, joy_deadzoneup.value) * cl_upspeed.value;
491                 cl.viewangles[0]   += IN_JoystickGetAxis(joy, joy_axispitch.integer, joy_sensitivitypitch.value, joy_deadzonepitch.value) * cl.realframetime * cl_pitchspeed.value;
492                 cl.viewangles[1]   += IN_JoystickGetAxis(joy, joy_axisyaw.integer, joy_sensitivityyaw.value, joy_deadzoneyaw.value) * cl.realframetime * cl_yawspeed.value;
493                 //cl.viewangles[2]   += IN_JoystickGetAxis(joy, joy_axisroll.integer, joy_sensitivityroll.value, joy_deadzoneroll.value) * cl.realframetime * cl_rollspeed.value;
494         
495                 // cache state of axes to emulate button events for them
496                 numaxes = min(MAX_JOYSTICK_AXES, SDL_JoystickNumAxes(joy));
497                 for (j = 0; j < numaxes; j++)
498                 {
499                         joy_axescache[j].oldmove = joy_axescache[j].move;
500                         joy_axescache[j].move = IN_JoystickGetAxis(joy, j, 1, 0.01);
501                 }
502
503                 // run keyevents
504                 if (joy_axiskeyevents.integer)
505                 {
506                         IN_JoystickKeyeventForAxis(joy, joy_axisforward.integer, K_DOWNARROW, K_UPARROW);
507                         IN_JoystickKeyeventForAxis(joy, joy_axisside.integer, K_RIGHTARROW, K_LEFTARROW);
508                 }
509         }
510 }
511
512 /////////////////////
513 // Message Handling
514 ////
515
516 static int Sys_EventFilter( SDL_Event *event )
517 {
518         //TODO: Add a quit query in linux, too - though linux user are more likely to know what they do
519         if (event->type == SDL_QUIT)
520         {
521 #ifdef WIN32
522                 if (MessageBox( NULL, "Are you sure you want to quit?", "Confirm Exit", MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION ) == IDNO)
523                         return 0;
524 #endif
525         }
526         return 1;
527 }
528
529 #ifdef SDL_R_RESTART
530 static qboolean sdl_needs_restart;
531 static void sdl_start(void)
532 {
533 }
534 static void sdl_shutdown(void)
535 {
536         sdl_needs_restart = false;
537 }
538 static void sdl_newmap(void)
539 {
540 }
541 #endif
542
543 static keynum_t buttonremap[18] =
544 {
545         K_MOUSE1,
546         K_MOUSE3,
547         K_MOUSE2,
548         K_MWHEELUP,
549         K_MWHEELDOWN,
550         K_MOUSE4,
551         K_MOUSE5,
552         K_MOUSE6,
553         K_MOUSE7,
554         K_MOUSE8,
555         K_MOUSE9,
556         K_MOUSE10,
557         K_MOUSE11,
558         K_MOUSE12,
559         K_MOUSE13,
560         K_MOUSE14,
561         K_MOUSE15,
562         K_MOUSE16,
563 };
564
565 void Sys_SendKeyEvents( void )
566 {
567         static qboolean sound_active = true;
568         int keycode;
569         SDL_Event event;
570
571         while( SDL_PollEvent( &event ) )
572                 switch( event.type ) {
573                         case SDL_QUIT:
574                                 Sys_Quit(0);
575                                 break;
576                         case SDL_KEYDOWN:
577                         case SDL_KEYUP:
578                                 keycode = MapKey(event.key.keysym.sym);
579                                 if (!IN_JoystickBlockDoubledKeyEvents(keycode))
580                                         Key_Event(keycode, event.key.keysym.unicode, (event.key.state == SDL_PRESSED));
581                                 break;
582                         case SDL_ACTIVEEVENT:
583                                 if( event.active.state & SDL_APPACTIVE )
584                                 {
585                                         if( event.active.gain )
586                                                 vid_hidden = false;
587                                         else
588                                                 vid_hidden = true;
589                                 }
590                                 break;
591                         case SDL_MOUSEBUTTONDOWN:
592                         case SDL_MOUSEBUTTONUP:
593                                 if (event.button.button <= 18)
594                                         Key_Event( buttonremap[event.button.button - 1], 0, event.button.state == SDL_PRESSED );
595                                 break;
596                         case SDL_JOYBUTTONDOWN:
597                                 if (!joy_enable.integer)
598                                         break; // ignore down events if joystick has been disabled
599                         case SDL_JOYBUTTONUP:
600                                 if (event.jbutton.button < 48)
601                                         Key_Event( event.jbutton.button + (event.jbutton.button < 16 ? K_JOY1 : K_AUX1 - 16), 0, (event.jbutton.state == SDL_PRESSED) );
602                                 break;
603                         case SDL_VIDEORESIZE:
604                                 if(vid_resizable.integer < 2)
605                                 {
606                                         vid.width = event.resize.w;
607                                         vid.height = event.resize.h;
608                                         SDL_SetVideoMode(vid.width, vid.height, video_bpp, video_flags);
609 #ifdef SDL_R_RESTART
610                                         // better not call R_Modules_Restart from here directly, as this may wreak havoc...
611                                         // so, let's better queue it for next frame
612                                         if(!sdl_needs_restart)
613                                         {
614                                                 Cbuf_AddText("\nr_restart\n");
615                                                 sdl_needs_restart = true;
616                                         }
617 #endif
618                                 }
619                                 break;
620                 }
621
622         // enable/disable sound on focus gain/loss
623         if ((!vid_hidden && vid_activewindow) || !snd_mutewhenidle.integer)
624         {
625                 if (!sound_active)
626                 {
627                         S_UnblockSound ();
628                         sound_active = true;
629                 }
630         }
631         else
632         {
633                 if (sound_active)
634                 {
635                         S_BlockSound ();
636                         sound_active = false;
637                 }
638         }
639 }
640
641 /////////////////
642 // Video system
643 ////
644
645 void *GL_GetProcAddress(const char *name)
646 {
647         void *p = NULL;
648         p = SDL_GL_GetProcAddress(name);
649         return p;
650 }
651
652 static int Sys_EventFilter( SDL_Event *event );
653 static qboolean vid_sdl_initjoysticksystem = false;
654
655 void VID_Init (void)
656 {
657 #ifdef MACOSX
658         Cvar_RegisterVariable(&apple_mouse_noaccel);
659 #endif
660         Cvar_RegisterVariable(&joy_detected);
661         Cvar_RegisterVariable(&joy_enable);
662         Cvar_RegisterVariable(&joy_index);
663         Cvar_RegisterVariable(&joy_axisforward);
664         Cvar_RegisterVariable(&joy_axisside);
665         Cvar_RegisterVariable(&joy_axisup);
666         Cvar_RegisterVariable(&joy_axispitch);
667         Cvar_RegisterVariable(&joy_axisyaw);
668         //Cvar_RegisterVariable(&joy_axisroll);
669         Cvar_RegisterVariable(&joy_deadzoneforward);
670         Cvar_RegisterVariable(&joy_deadzoneside);
671         Cvar_RegisterVariable(&joy_deadzoneup);
672         Cvar_RegisterVariable(&joy_deadzonepitch);
673         Cvar_RegisterVariable(&joy_deadzoneyaw);
674         //Cvar_RegisterVariable(&joy_deadzoneroll);
675         Cvar_RegisterVariable(&joy_sensitivityforward);
676         Cvar_RegisterVariable(&joy_sensitivityside);
677         Cvar_RegisterVariable(&joy_sensitivityup);
678         Cvar_RegisterVariable(&joy_sensitivitypitch);
679         Cvar_RegisterVariable(&joy_sensitivityyaw);
680         //Cvar_RegisterVariable(&joy_sensitivityroll);
681         Cvar_RegisterVariable(&joy_axiskeyevents);
682         
683 #ifdef SDL_R_RESTART
684         R_RegisterModule("SDL", sdl_start, sdl_shutdown, sdl_newmap, NULL, NULL);
685 #endif
686
687         if (SDL_Init(SDL_INIT_VIDEO) < 0)
688                 Sys_Error ("Failed to init SDL video subsystem: %s", SDL_GetError());
689         vid_sdl_initjoysticksystem = SDL_Init(SDL_INIT_JOYSTICK) >= 0;
690         if (vid_sdl_initjoysticksystem)
691                 Con_Printf("Failed to init SDL joystick subsystem: %s\n", SDL_GetError());
692         vid_isfullscreen = false;
693 }
694
695 // set the icon (we dont use SDL here since it would be too much a PITA)
696 #ifdef WIN32
697 #include "resource.h"
698 #include <SDL_syswm.h>
699 static void VID_SetCaption(void)
700 {
701     SDL_SysWMinfo       info;
702         HICON                   icon;
703
704         // set the caption
705         SDL_WM_SetCaption( gamename, NULL );
706
707         // get the HWND handle
708     SDL_VERSION( &info.version );
709         if( !SDL_GetWMInfo( &info ) )
710                 return;
711
712         icon = LoadIcon( GetModuleHandle( NULL ), MAKEINTRESOURCE( IDI_ICON1 ) );
713 #ifndef _W64 //If Windows 64bit data types don't exist
714 #ifndef SetClassLongPtr
715 #define SetClassLongPtr SetClassLong
716 #endif
717 #ifndef GCLP_HICON
718 #define GCLP_HICON GCL_HICON
719 #endif
720 #ifndef LONG_PTR
721 #define LONG_PTR LONG
722 #endif
723 #endif
724         SetClassLongPtr( info.window, GCLP_HICON, (LONG_PTR)icon );
725 }
726 static void VID_SetIcon_Pre(void)
727 {
728 }
729 static void VID_SetIcon_Post(void)
730 {
731 }
732 #else
733 // Adding the OS independent XPM version --blub
734 #include "darkplaces.xpm"
735 #include "nexuiz.xpm"
736 static SDL_Surface *icon = NULL;
737 static void VID_SetIcon_Pre(void)
738 {
739         /*
740          * Somewhat restricted XPM reader. Only supports XPMs saved by GIMP 2.4 at
741          * default settings with less than 91 colors and transparency.
742          */
743
744         int width, height, colors, isize, i, j;
745         int thenone = -1;
746         static SDL_Color palette[256];
747         unsigned short palenc[256]; // store color id by char
748         char *xpm;
749         char **idata, *data;
750         const SDL_version *version;
751
752         version = SDL_Linked_Version();
753         // only use non-XPM icon support in SDL v1.3 and higher
754         // SDL v1.2 does not support "smooth" transparency, and thus is better
755         // off the xpm way
756         if(version->major >= 2 || (version->major == 1 && version->minor >= 3))
757         {
758                 data = (char *) loadimagepixelsbgra("darkplaces-icon", false, false, false, NULL);
759                 if(data)
760                 {
761                         unsigned int red = 0x00FF0000;
762                         unsigned int green = 0x0000FF00;
763                         unsigned int blue = 0x000000FF;
764                         unsigned int alpha = 0xFF000000;
765                         width = image_width;
766                         height = image_height;
767
768                         // reallocate with malloc, as this is in tempmempool (do not want)
769                         xpm = data;
770                         data = malloc(width * height * 4);
771                         memcpy(data, xpm, width * height * 4);
772                         Mem_Free(xpm);
773                         xpm = NULL;
774
775                         icon = SDL_CreateRGBSurface(SDL_SRCALPHA, width, height, 32, LittleLong(red), LittleLong(green), LittleLong(blue), LittleLong(alpha));
776
777                         if(icon == NULL) {
778                                 Con_Printf(     "Failed to create surface for the window Icon!\n"
779                                                 "%s\n", SDL_GetError());
780                                 free(data);
781                                 return;
782                         }
783
784                         icon->pixels = data;
785                 }
786         }
787
788         // we only get here if non-XPM icon was missing, or SDL version is not
789         // sufficient for transparent non-XPM icons
790         if(!icon)
791         {
792                 xpm = (char *) FS_LoadFile("darkplaces-icon.xpm", tempmempool, false, NULL);
793                 idata = NULL;
794                 if(xpm)
795                         idata = XPM_DecodeString(xpm);
796                 if(!idata)
797                         idata = ENGINE_ICON;
798                 if(xpm)
799                         Mem_Free(xpm);
800
801                 data = idata[0];
802
803                 if(sscanf(data, "%i %i %i %i", &width, &height, &colors, &isize) != 4)
804                 {
805                         // NOTE: Only 1-char colornames are supported
806                         Con_Printf("Sorry, but this does not even look similar to an XPM.\n");
807                         return;
808                 }
809
810                 if(isize != 1)
811                 {
812                         // NOTE: Only 1-char colornames are supported
813                         Con_Printf("This XPM's palette is either huge or idiotically unoptimized. It's key size is %i\n", isize);
814                         return;
815                 }
816
817                 for(i = 0; i < colors; ++i)
818                 {
819                         unsigned int r, g, b;
820                         char idx;
821
822                         if(sscanf(idata[i+1], "%c c #%02x%02x%02x", &idx, &r, &g, &b) != 4)
823                         {
824                                 char foo[2];
825                                 if(sscanf(idata[i+1], "%c c Non%1[e]", &idx, foo) != 2) // I take the DailyWTF credit for this. --div0
826                                 {
827                                         Con_Printf("This XPM's palette looks odd. Can't continue.\n");
828                                         return;
829                                 }
830                                 else
831                                 {
832                                         palette[i].r = 255; // color key
833                                         palette[i].g = 0;
834                                         palette[i].b = 255;
835                                         thenone = i; // weeeee
836                                 }
837                         }
838                         else
839                         {
840                                 palette[i].r = r - (r == 255 && g == 0 && b == 255); // change 255/0/255 pink to 254/0/255 for color key
841                                 palette[i].g = g;
842                                 palette[i].b = b;
843                         }
844
845                         palenc[(unsigned char) idx] = i;
846                 }
847
848                 // allocate the image data
849                 data = (char*) malloc(width*height);
850
851                 for(j = 0; j < height; ++j)
852                 {
853                         for(i = 0; i < width; ++i)
854                         {
855                                 // casting to the safest possible datatypes ^^
856                                 data[j * width + i] = palenc[((unsigned char*)idata[colors+j+1])[i]];
857                         }
858                 }
859
860                 if(icon != NULL)
861                 {
862                         // SDL_FreeSurface should free the data too
863                         // but for completeness' sake...
864                         if(icon->flags & SDL_PREALLOC)
865                         {
866                                 free(icon->pixels);
867                                 icon->pixels = NULL; // safety
868                         }
869                         SDL_FreeSurface(icon);
870                 }
871
872                 icon = SDL_CreateRGBSurface(SDL_SRCCOLORKEY, width, height, 8, 0,0,0,0);// rmask, gmask, bmask, amask); no mask needed
873                 // 8 bit surfaces get an empty palette allocated according to the docs
874                 // so it's a palette image for sure :) no endian check necessary for the mask
875
876                 if(icon == NULL) {
877                         Con_Printf(     "Failed to create surface for the window Icon!\n"
878                                         "%s\n", SDL_GetError());
879                         free(data);
880                         return;
881                 }
882
883                 icon->pixels = data;
884                 SDL_SetPalette(icon, SDL_PHYSPAL|SDL_LOGPAL, palette, 0, colors);
885                 SDL_SetColorKey(icon, SDL_SRCCOLORKEY, thenone);
886         }
887
888         SDL_WM_SetIcon(icon, NULL);
889 }
890 static void VID_SetIcon_Post(void)
891 {
892 #if SDL_VIDEO_DRIVER_X11 && !SDL_VIDEO_DRIVER_QUARTZ
893         int j;
894         char *data;
895         const SDL_version *version;
896
897         version = SDL_Linked_Version();
898         // only use non-XPM icon support in SDL v1.3 and higher
899         // SDL v1.2 does not support "smooth" transparency, and thus is better
900         // off the xpm way
901         if(!(version->major >= 2 || (version->major == 1 && version->minor >= 3)))
902         {
903                 // in this case, we did not set the good icon yet
904                 SDL_SysWMinfo info;
905                 SDL_VERSION(&info.version);
906                 if(SDL_GetWMInfo(&info) == 1 && info.subsystem == SDL_SYSWM_X11)
907                 {
908                         data = (char *) loadimagepixelsbgra("darkplaces-icon", false, false, false, NULL);
909                         if(data)
910                         {
911                                 // use _NET_WM_ICON too
912                                 static long netwm_icon[MAX_NETWM_ICON];
913                                 int pos = 0;
914                                 int i = 1;
915
916                                 while(data)
917                                 {
918                                         if(pos + 2 * image_width * image_height < MAX_NETWM_ICON)
919                                         {
920                                                 netwm_icon[pos++] = image_width;
921                                                 netwm_icon[pos++] = image_height;
922                                                 for(i = 0; i < image_height; ++i)
923                                                         for(j = 0; j < image_width; ++j)
924                                                                 netwm_icon[pos++] = BuffLittleLong((unsigned char *) &data[(i*image_width+j)*4]);
925                                         }
926                                         else
927                                         {
928                                                 Con_Printf("Skipping NETWM icon #%d because there is no space left\n", i);
929                                         }
930                                         ++i;
931                                         Mem_Free(data);
932                                         data = (char *) loadimagepixelsbgra(va("darkplaces-icon%d", i), false, false, false, NULL);
933                                 }
934
935                                 info.info.x11.lock_func();
936                                 {
937                                         Atom net_wm_icon = XInternAtom(info.info.x11.display, "_NET_WM_ICON", false);
938                                         XChangeProperty(info.info.x11.display, info.info.x11.wmwindow, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (const unsigned char *) netwm_icon, pos);
939                                 }
940                                 info.info.x11.unlock_func();
941                         }
942                 }
943         }
944 #endif
945 }
946
947
948 static void VID_SetCaption(void)
949 {
950         SDL_WM_SetCaption( gamename, NULL );
951 }
952 #endif
953
954 static void VID_OutputVersion(void)
955 {
956         const SDL_version *version;
957         version = SDL_Linked_Version();
958         Con_Printf(     "Linked against SDL version %d.%d.%d\n"
959                                         "Using SDL library version %d.%d.%d\n",
960                                         SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL,
961                                         version->major, version->minor, version->patch );
962 }
963
964 qboolean VID_InitMode(viddef_mode_t *mode)
965 {
966         int i;
967         static int notfirstvideomode = false;
968         int flags = SDL_OPENGL;
969         const char *drivername;
970
971         win_half_width = mode->width>>1;
972         win_half_height = mode->height>>1;
973
974         if(vid_resizable.integer)
975                 flags |= SDL_RESIZABLE;
976
977         VID_OutputVersion();
978
979         /*
980         SDL Hack
981                 We cant switch from one OpenGL video mode to another.
982                 Thus we first switch to some stupid 2D mode and then back to OpenGL.
983         */
984         if (notfirstvideomode)
985                 SDL_SetVideoMode( 0, 0, 0, 0 );
986         notfirstvideomode = true;
987
988         // SDL usually knows best
989         drivername = NULL;
990
991 // COMMANDLINEOPTION: SDL GL: -gl_driver <drivername> selects a GL driver library, default is whatever SDL recommends, useful only for 3dfxogl.dll/3dfxvgl.dll or fxmesa or similar, if you don't know what this is for, you don't need it
992         i = COM_CheckParm("-gl_driver");
993         if (i && i < com_argc - 1)
994                 drivername = com_argv[i + 1];
995         if (SDL_GL_LoadLibrary(drivername) < 0)
996         {
997                 Con_Printf("Unable to load GL driver \"%s\": %s\n", drivername, SDL_GetError());
998                 return false;
999         }
1000
1001         if ((qglGetString = (const GLubyte* (GLAPIENTRY *)(GLenum name))GL_GetProcAddress("glGetString")) == NULL)
1002         {
1003                 VID_Shutdown();
1004                 Con_Print("Required OpenGL function glGetString not found\n");
1005                 return false;
1006         }
1007
1008         // Knghtbrd: should do platform-specific extension string function here
1009
1010         vid_isfullscreen = false;
1011         if (mode->fullscreen) {
1012                 flags |= SDL_FULLSCREEN;
1013                 vid_isfullscreen = true;
1014         }
1015         //flags |= SDL_HWSURFACE;
1016
1017         SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1);
1018         if (mode->bitsperpixel >= 32)
1019         {
1020                 SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 8);
1021                 SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 8);
1022                 SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 8);
1023                 SDL_GL_SetAttribute (SDL_GL_ALPHA_SIZE, 8);
1024                 SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 24);
1025                 SDL_GL_SetAttribute (SDL_GL_STENCIL_SIZE, 8);
1026         }
1027         else
1028         {
1029                 SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 5);
1030                 SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 5);
1031                 SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 5);
1032                 SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 16);
1033         }
1034         if (mode->stereobuffer)
1035                 SDL_GL_SetAttribute (SDL_GL_STEREO, 1);
1036         if (vid_vsync.integer)
1037                 SDL_GL_SetAttribute (SDL_GL_SWAP_CONTROL, 1);
1038         else
1039                 SDL_GL_SetAttribute (SDL_GL_SWAP_CONTROL, 0);
1040         if (mode->samples > 1)
1041         {
1042                 SDL_GL_SetAttribute (SDL_GL_MULTISAMPLEBUFFERS, 1);
1043                 SDL_GL_SetAttribute (SDL_GL_MULTISAMPLESAMPLES, mode->samples);
1044         }
1045
1046         video_bpp = mode->bitsperpixel;
1047         video_flags = flags;
1048         VID_SetIcon_Pre();
1049         screen = SDL_SetVideoMode(mode->width, mode->height, mode->bitsperpixel, flags);
1050         VID_SetIcon_Post();
1051
1052         if (screen == NULL)
1053         {
1054                 Con_Printf("Failed to set video mode to %ix%i: %s\n", mode->width, mode->height, SDL_GetError());
1055                 VID_Shutdown();
1056                 return false;
1057         }
1058
1059         // set window title
1060         VID_SetCaption();
1061         // set up an event filter to ask confirmation on close button in WIN32
1062         SDL_SetEventFilter( (SDL_EventFilter) Sys_EventFilter );
1063         // init keyboard
1064         SDL_EnableUNICODE( SDL_ENABLE );
1065         // enable key repeat since everyone expects it
1066         SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
1067
1068         gl_platform = "SDL";
1069         gl_platformextensions = "";
1070
1071         GL_Init();
1072
1073         vid_numjoysticks = SDL_NumJoysticks();
1074         vid_numjoysticks = bound(0, vid_numjoysticks, MAX_JOYSTICKS);
1075         Cvar_SetValueQuick(&joy_detected, vid_numjoysticks);
1076         Con_Printf("%d SDL joystick(s) found:\n", vid_numjoysticks);
1077         memset(vid_joysticks, 0, sizeof(vid_joysticks));
1078         for (i = 0;i < vid_numjoysticks;i++)
1079         {
1080                 SDL_Joystick *joy;
1081                 joy = vid_joysticks[i] = SDL_JoystickOpen(i);
1082                 if (!joy)
1083                 {
1084                         Con_Printf("joystick #%i: open failed: %s\n", i, SDL_GetError());
1085                         continue;
1086                 }
1087                 Con_Printf("joystick #%i: opened \"%s\" with %i axes, %i buttons, %i balls\n", i, SDL_JoystickName(i), (int)SDL_JoystickNumAxes(joy), (int)SDL_JoystickNumButtons(joy), (int)SDL_JoystickNumBalls(joy));
1088         }
1089
1090         vid_hidden = false;
1091         vid_activewindow = false;
1092         vid_usingmouse = false;
1093         vid_usinghidecursor = false;
1094
1095         SDL_WM_GrabInput(SDL_GRAB_OFF);
1096         return true;
1097 }
1098
1099 void VID_Shutdown (void)
1100 {
1101         VID_SetMouse(false, false, false);
1102         VID_RestoreSystemGamma();
1103
1104         SDL_QuitSubSystem(SDL_INIT_VIDEO);
1105
1106         gl_driver[0] = 0;
1107         gl_extensions = "";
1108         gl_platform = "";
1109         gl_platformextensions = "";
1110 }
1111
1112 int VID_SetGamma (unsigned short *ramps, int rampsize)
1113 {
1114         return !SDL_SetGammaRamp (ramps, ramps + rampsize, ramps + rampsize*2);
1115 }
1116
1117 int VID_GetGamma (unsigned short *ramps, int rampsize)
1118 {
1119         return !SDL_GetGammaRamp (ramps, ramps + rampsize, ramps + rampsize*2);
1120 }
1121
1122 void VID_Finish (void)
1123 {
1124         Uint8 appstate;
1125
1126         //react on appstate changes
1127         appstate = SDL_GetAppState();
1128
1129         vid_hidden = !(appstate & SDL_APPACTIVE);
1130
1131         if( vid_hidden || !( appstate & SDL_APPMOUSEFOCUS ) || !( appstate & SDL_APPINPUTFOCUS ) )
1132                 vid_activewindow = false;
1133         else
1134                 vid_activewindow = true;
1135
1136         VID_UpdateGamma(false, 256);
1137
1138         if (!vid_hidden)
1139         {
1140                 CHECKGLERROR
1141                 if (r_speeds.integer == 2 || gl_finish.integer)
1142                 {
1143                         qglFinish();CHECKGLERROR
1144                 }
1145                 SDL_GL_SwapBuffers();
1146         }
1147 }
1148
1149 size_t VID_ListModes(vid_mode_t *modes, size_t maxcount)
1150 {
1151         size_t k;
1152         SDL_Rect **vidmodes;
1153         int bpp = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
1154
1155         k = 0;
1156         for(vidmodes = SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_HWSURFACE); *vidmodes; ++vidmodes)
1157         {
1158                 if(k >= maxcount)
1159                         break;
1160                 modes[k].width = (*vidmodes)->w;
1161                 modes[k].height = (*vidmodes)->h;
1162                 modes[k].bpp = bpp;
1163                 modes[k].refreshrate = 60; // no support for refresh rate in SDL
1164                 modes[k].pixelheight_num = 1;
1165                 modes[k].pixelheight_denom = 1; // SDL does not provide this
1166                 ++k;
1167         }
1168         return k;
1169 }