2 Copyright (C) 2003 T. Joseph Carter
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.
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.
13 See the GNU General Public License for more details.
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.
19 #undef WIN32_LEAN_AND_MEAN //hush a warning, SDL.h redefines this
25 // Tell startup code that we have a client
26 int cl_available = true;
28 qboolean vid_supportrefreshrate = false;
30 cvar_t joy_detected = {CVAR_READONLY, "joy_detected", "0", "number of joysticks detected by engine"};
31 cvar_t joy_enable = {0, "joy_enable", "1", "enables joystick support"};
32 cvar_t joy_index = {0, "joy_index", "0", "selects which joystick to use if you have multiple"};
33 cvar_t joy_axisforward = {0, "joy_axisforward", "1", "which joystick axis to query for forward/backward movement"};
34 cvar_t joy_axisside = {0, "joy_axisside", "0", "which joystick axis to query for right/left movement"};
35 cvar_t joy_axisup = {0, "joy_axisup", "-1", "which joystick axis to query for up/down movement"};
36 cvar_t joy_axispitch = {0, "joy_axispitch", "3", "which joystick axis to query for looking up/down"};
37 cvar_t joy_axisyaw = {0, "joy_axisyaw", "2", "which joystick axis to query for looking right/left"};
38 cvar_t joy_axisroll = {0, "joy_axisroll", "-1", "which joystick axis to query for tilting head right/left"};
39 cvar_t joy_deadzoneforward = {0, "joy_deadzoneforward", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
40 cvar_t joy_deadzoneside = {0, "joy_deadzoneside", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
41 cvar_t joy_deadzoneup = {0, "joy_deadzoneup", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
42 cvar_t joy_deadzonepitch = {0, "joy_deadzonepitch", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
43 cvar_t joy_deadzoneyaw = {0, "joy_deadzoneyaw", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
44 cvar_t joy_deadzoneroll = {0, "joy_deadzoneroll", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
45 cvar_t joy_sensitivityforward = {0, "joy_sensitivityforward", "-1", "movement multiplier"};
46 cvar_t joy_sensitivityside = {0, "joy_sensitivityside", "1", "movement multiplier"};
47 cvar_t joy_sensitivityup = {0, "joy_sensitivityup", "1", "movement multiplier"};
48 cvar_t joy_sensitivitypitch = {0, "joy_sensitivitypitch", "1", "movement multiplier"};
49 cvar_t joy_sensitivityyaw = {0, "joy_sensitivityyaw", "-1", "movement multiplier"};
50 cvar_t joy_sensitivityroll = {0, "joy_sensitivityroll", "1", "movement multiplier"};
53 static qboolean vid_usingmouse;
54 static qboolean vid_isfullscreen;
55 static int vid_numjoysticks = 0;
56 #define MAX_JOYSTICKS 8
57 static SDL_Joystick *vid_joysticks[MAX_JOYSTICKS];
59 static SDL_Surface *screen;
61 /////////////////////////
64 //TODO: Add joystick support
65 //TODO: Add error checking
68 //keysym to quake keysym mapping
69 #define tenoh 0,0,0,0,0, 0,0,0,0,0
70 #define fiftyoh tenoh, tenoh, tenoh, tenoh, tenoh
71 #define hundredoh fiftyoh, fiftyoh
72 static unsigned int tbl_sdltoquake[] =
74 0,0,0,0, //SDLK_UNKNOWN = 0,
75 0,0,0,0, //SDLK_FIRST = 0,
76 K_BACKSPACE, //SDLK_BACKSPACE = 8,
77 K_TAB, //SDLK_TAB = 9,
80 K_ENTER, //SDLK_RETURN = 13,
82 K_PAUSE, //SDLK_PAUSE = 19,
84 K_ESCAPE, //SDLK_ESCAPE = 27,
86 K_SPACE, //SDLK_SPACE = 32,
87 '!', //SDLK_EXCLAIM = 33,
88 '"', //SDLK_QUOTEDBL = 34,
89 '#', //SDLK_HASH = 35,
90 '$', //SDLK_DOLLAR = 36,
92 '&', //SDLK_AMPERSAND = 38,
93 '\'', //SDLK_QUOTE = 39,
94 '(', //SDLK_LEFTPAREN = 40,
95 ')', //SDLK_RIGHTPAREN = 41,
96 '*', //SDLK_ASTERISK = 42,
97 '+', //SDLK_PLUS = 43,
98 ',', //SDLK_COMMA = 44,
99 '-', //SDLK_MINUS = 45,
100 '.', //SDLK_PERIOD = 46,
101 '/', //SDLK_SLASH = 47,
112 ':', //SDLK_COLON = 58,
113 ';', //SDLK_SEMICOLON = 59,
114 '<', //SDLK_LESS = 60,
115 '=', //SDLK_EQUALS = 61,
116 '>', //SDLK_GREATER = 62,
117 '?', //SDLK_QUESTION = 63,
119 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,
120 '[', //SDLK_LEFTBRACKET = 91,
121 '\\', //SDLK_BACKSLASH = 92,
122 ']', //SDLK_RIGHTBRACKET = 93,
123 '^', //SDLK_CARET = 94,
124 '_', //SDLK_UNDERSCORE = 95,
125 '`', //SDLK_BACKQUOTE = 96,
153 K_DEL, //SDLK_DELETE = 127,
154 hundredoh /*227*/, tenoh, tenoh, 0,0,0,0,0,0,0,0,
155 K_KP_0, //SDLK_KP0 = 256,
156 K_KP_1, //SDLK_KP1 = 257,
157 K_KP_2, //SDLK_KP2 = 258,
158 K_KP_3, //SDLK_KP3 = 259,
159 K_KP_4, //SDLK_KP4 = 260,
160 K_KP_5, //SDLK_KP5 = 261,
161 K_KP_6, //SDLK_KP6 = 262,
162 K_KP_7, //SDLK_KP7 = 263,
163 K_KP_8, //SDLK_KP8 = 264,
164 K_KP_9, //SDLK_KP9 = 265,
165 K_KP_PERIOD,//SDLK_KP_PERIOD = 266,
166 K_KP_DIVIDE,//SDLK_KP_DIVIDE = 267,
167 K_KP_MULTIPLY,//SDLK_KP_MULTIPLY= 268,
168 K_KP_MINUS, //SDLK_KP_MINUS = 269,
169 K_KP_PLUS, //SDLK_KP_PLUS = 270,
170 K_KP_ENTER, //SDLK_KP_ENTER = 271,
171 K_KP_EQUALS,//SDLK_KP_EQUALS = 272,
172 K_UPARROW, //SDLK_UP = 273,
173 K_DOWNARROW,//SDLK_DOWN = 274,
174 K_RIGHTARROW,//SDLK_RIGHT = 275,
175 K_LEFTARROW,//SDLK_LEFT = 276,
176 K_INS, //SDLK_INSERT = 277,
177 K_HOME, //SDLK_HOME = 278,
178 K_END, //SDLK_END = 279,
179 K_PGUP, //SDLK_PAGEUP = 280,
180 K_PGDN, //SDLK_PAGEDOWN = 281,
181 K_F1, //SDLK_F1 = 282,
182 K_F2, //SDLK_F2 = 283,
183 K_F3, //SDLK_F3 = 284,
184 K_F4, //SDLK_F4 = 285,
185 K_F5, //SDLK_F5 = 286,
186 K_F6, //SDLK_F6 = 287,
187 K_F7, //SDLK_F7 = 288,
188 K_F8, //SDLK_F8 = 289,
189 K_F9, //SDLK_F9 = 290,
190 K_F10, //SDLK_F10 = 291,
191 K_F11, //SDLK_F11 = 292,
192 K_F12, //SDLK_F12 = 293,
197 K_NUMLOCK, //SDLK_NUMLOCK = 300,
198 K_CAPSLOCK, //SDLK_CAPSLOCK = 301,
199 K_SCROLLOCK,//SDLK_SCROLLOCK= 302,
200 K_SHIFT, //SDLK_RSHIFT = 303,
201 K_SHIFT, //SDLK_LSHIFT = 304,
202 K_CTRL, //SDLK_RCTRL = 305,
203 K_CTRL, //SDLK_LCTRL = 306,
204 K_ALT, //SDLK_RALT = 307,
205 K_ALT, //SDLK_LALT = 308,
206 0, //SDLK_RMETA = 309,
207 0, //SDLK_LMETA = 310,
208 0, //SDLK_LSUPER = 311, /* Left "Windows" key */
209 0, //SDLK_RSUPER = 312, /* Right "Windows" key */
210 K_ALT, //SDLK_MODE = 313, /* "Alt Gr" key */
211 0, //SDLK_COMPOSE = 314, /* Multi-key compose key */
212 0, //SDLK_HELP = 315,
213 0, //SDLK_PRINT = 316,
214 0, //SDLK_SYSREQ = 317,
215 K_PAUSE, //SDLK_BREAK = 318,
216 0, //SDLK_MENU = 319,
217 0, //SDLK_POWER = 320, /* Power Macintosh power key */
218 'e', //SDLK_EURO = 321, /* Some european keyboards */
219 0 //SDLK_UNDO = 322, /* Atari keyboard has Undo */
225 static int MapKey( unsigned int sdlkey )
227 if( sdlkey > sizeof(tbl_sdltoquake)/ sizeof(int) )
229 return tbl_sdltoquake[ sdlkey ];
232 static void IN_Activate( qboolean grab )
238 vid_usingmouse = true;
239 cl_ignoremousemove = true;
240 SDL_WM_GrabInput( SDL_GRAB_ON );
241 SDL_ShowCursor( SDL_DISABLE );
248 vid_usingmouse = false;
249 cl_ignoremousemove = true;
250 SDL_WM_GrabInput( SDL_GRAB_OFF );
251 SDL_ShowCursor( SDL_ENABLE );
256 static double IN_JoystickGetAxis(SDL_Joystick *joy, int axis, double sensitivity, double deadzone)
259 if (axis < 0 || axis >= SDL_JoystickNumAxes(joy))
260 return 0; // no such axis on this joystick
261 value = SDL_JoystickGetAxis(joy, axis) * (1.0 / 32767.0);
262 value = bound(-1, value, 1);
263 if (fabs(value) < deadzone)
264 return 0; // within deadzone around center
265 return value * sensitivity;
274 SDL_GetRelativeMouseState( &x, &y );
278 if (vid_numjoysticks && joy_enable.integer && joy_index.integer >= 0 && joy_index.integer < vid_numjoysticks)
280 SDL_Joystick *joy = vid_joysticks[joy_index.integer];
281 int numballs = SDL_JoystickNumBalls(joy);
282 for (j = 0;j < numballs;j++)
284 SDL_JoystickGetBall(joy, j, &x, &y);
288 cl.cmd.forwardmove += IN_JoystickGetAxis(joy, joy_axisforward.integer, joy_sensitivityforward.value, joy_deadzoneforward.value) * cl_forwardspeed.value;
289 cl.cmd.sidemove += IN_JoystickGetAxis(joy, joy_axisside.integer, joy_sensitivityside.value, joy_deadzoneside.value) * cl_sidespeed.value;
290 cl.cmd.upmove += IN_JoystickGetAxis(joy, joy_axisup.integer, joy_sensitivityup.value, joy_deadzoneup.value) * cl_upspeed.value;
291 cl.viewangles[0] += IN_JoystickGetAxis(joy, joy_axispitch.integer, joy_sensitivitypitch.value, joy_deadzonepitch.value) * cl.realframetime * cl_pitchspeed.value;
292 cl.viewangles[1] += IN_JoystickGetAxis(joy, joy_axisyaw.integer, joy_sensitivityyaw.value, joy_deadzoneyaw.value) * cl.realframetime * cl_yawspeed.value;
293 //cl.viewangles[2] += IN_JoystickGetAxis(joy, joy_axisroll.integer, joy_sensitivityroll.value, joy_deadzoneroll.value) * cl.realframetime * cl_rollspeed.value;
297 /////////////////////
301 static int Sys_EventFilter( SDL_Event *event )
303 //TODO: Add a quit query in linux, too - though linux user are more likely to know what they do
305 if( event->type == SDL_QUIT && MessageBox( NULL, "Are you sure you want to quit?", "Confirm Exit", MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION ) == IDNO )
314 static keynum_t buttonremap[18] =
336 void Sys_SendKeyEvents( void )
338 static qboolean sound_active = true;
341 while( SDL_PollEvent( &event ) )
342 switch( event.type ) {
348 Key_Event( MapKey( event.key.keysym.sym ), (char)event.key.keysym.unicode, (event.key.state == SDL_PRESSED) );
350 case SDL_ACTIVEEVENT:
351 if( event.active.state == SDL_APPACTIVE )
353 if( event.active.gain )
359 case SDL_MOUSEBUTTONDOWN:
360 case SDL_MOUSEBUTTONUP:
361 if (event.button.button <= 18)
362 Key_Event( buttonremap[event.button.button - 1], 0, event.button.state == SDL_PRESSED );
364 case SDL_JOYBUTTONDOWN:
365 if (!joy_enable.integer)
366 break; // ignore down events if joystick has been disabled
367 case SDL_JOYBUTTONUP:
368 if (event.jbutton.button < 48)
369 Key_Event( event.jbutton.button + (event.jbutton.button < 16 ? K_JOY1 : K_AUX1 - 16), 0, (event.jbutton.state == SDL_PRESSED) );
373 // enable/disable sound on focus gain/loss
374 if (!vid_activewindow && sound_active)
377 sound_active = false;
379 else if (vid_activewindow && !sound_active)
390 void *GL_GetProcAddress(const char *name)
393 p = SDL_GL_GetProcAddress(name);
397 static int Sys_EventFilter( SDL_Event *event );
398 static qboolean vid_sdl_initjoysticksystem = false;
401 Cvar_RegisterVariable(&joy_detected);
402 Cvar_RegisterVariable(&joy_enable);
403 Cvar_RegisterVariable(&joy_index);
404 Cvar_RegisterVariable(&joy_axisforward);
405 Cvar_RegisterVariable(&joy_axisside);
406 Cvar_RegisterVariable(&joy_axisup);
407 Cvar_RegisterVariable(&joy_axispitch);
408 Cvar_RegisterVariable(&joy_axisyaw);
409 //Cvar_RegisterVariable(&joy_axisroll);
410 Cvar_RegisterVariable(&joy_deadzoneforward);
411 Cvar_RegisterVariable(&joy_deadzoneside);
412 Cvar_RegisterVariable(&joy_deadzoneup);
413 Cvar_RegisterVariable(&joy_deadzonepitch);
414 Cvar_RegisterVariable(&joy_deadzoneyaw);
415 //Cvar_RegisterVariable(&joy_deadzoneroll);
416 Cvar_RegisterVariable(&joy_sensitivityforward);
417 Cvar_RegisterVariable(&joy_sensitivityside);
418 Cvar_RegisterVariable(&joy_sensitivityup);
419 Cvar_RegisterVariable(&joy_sensitivitypitch);
420 Cvar_RegisterVariable(&joy_sensitivityyaw);
421 //Cvar_RegisterVariable(&joy_sensitivityroll);
423 if (SDL_Init(SDL_INIT_VIDEO) < 0)
424 Sys_Error ("Failed to init SDL video subsystem: %s", SDL_GetError());
425 vid_sdl_initjoysticksystem = SDL_Init(SDL_INIT_JOYSTICK) >= 0;
426 if (vid_sdl_initjoysticksystem)
427 Con_Printf("Failed to init SDL joystick subsystem: %s\n", SDL_GetError());
428 vid_isfullscreen = false;
431 // set the icon (we dont use SDL here since it would be too much a PITA)
433 #include "resource.h"
434 #include <SDL_syswm.h>
435 static void VID_SetCaption()
441 SDL_WM_SetCaption( gamename, NULL );
443 // get the HWND handle
444 SDL_VERSION( &info.version );
445 if( !SDL_GetWMInfo( &info ) )
448 icon = LoadIcon( GetModuleHandle( NULL ), MAKEINTRESOURCE( IDI_ICON1 ) );
449 #ifndef _W64 //If Windows 64bit data types don't exist
450 #define SetClassLongPtr SetClassLong
451 #define GCLP_HICON GCL_HICON
452 #define LONG_PTR LONG
454 SetClassLongPtr( info.window, GCLP_HICON, (LONG_PTR)icon );
457 static void VID_SetCaption()
459 SDL_WM_SetCaption( gamename, NULL );
463 static void VID_OutputVersion()
465 const SDL_version *version;
466 version = SDL_Linked_Version();
467 Con_Printf( "Linked against SDL version %d.%d.%d\n"
468 "Using SDL library version %d.%d.%d\n",
469 SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL,
470 version->major, version->minor, version->patch );
473 int VID_InitMode(int fullscreen, int width, int height, int bpp, int refreshrate, int stereobuffer)
476 static int notfirstvideomode = false;
477 int flags = SDL_OPENGL;
478 const char *drivername;
484 We cant switch from one OpenGL video mode to another.
485 Thus we first switch to some stupid 2D mode and then back to OpenGL.
487 if (notfirstvideomode)
488 SDL_SetVideoMode( 0, 0, 0, 0 );
489 notfirstvideomode = true;
491 // SDL usually knows best
494 // 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
495 i = COM_CheckParm("-gl_driver");
496 if (i && i < com_argc - 1)
497 drivername = com_argv[i + 1];
498 if (SDL_GL_LoadLibrary(drivername) < 0)
500 Con_Printf("Unable to load GL driver \"%s\": %s\n", drivername, SDL_GetError());
504 if ((qglGetString = (const GLubyte* (GLAPIENTRY *)(GLenum name))GL_GetProcAddress("glGetString")) == NULL)
507 Con_Print("Required OpenGL function glGetString not found\n");
511 // Knghtbrd: should do platform-specific extension string function here
513 vid_isfullscreen = false;
515 flags |= SDL_FULLSCREEN;
516 vid_isfullscreen = true;
519 SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1);
522 SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 8);
523 SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 8);
524 SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 8);
525 SDL_GL_SetAttribute (SDL_GL_ALPHA_SIZE, 8);
526 SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 24);
527 SDL_GL_SetAttribute (SDL_GL_STENCIL_SIZE, 8);
531 SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 1);
532 SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 1);
533 SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 1);
534 SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 16);
537 SDL_GL_SetAttribute (SDL_GL_STEREO, 1);
539 screen = SDL_SetVideoMode(width, height, bpp, flags);
542 Con_Printf("Failed to set video mode to %ix%i: %s\n", width, height, SDL_GetError());
549 // set up an event filter to ask confirmation on close button in WIN32
550 SDL_SetEventFilter( (SDL_EventFilter) Sys_EventFilter );
552 SDL_EnableUNICODE( SDL_ENABLE );
553 // enable key repeat since everyone expects it
554 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
556 gl_renderer = (const char *)qglGetString(GL_RENDERER);
557 gl_vendor = (const char *)qglGetString(GL_VENDOR);
558 gl_version = (const char *)qglGetString(GL_VERSION);
559 gl_extensions = (const char *)qglGetString(GL_EXTENSIONS);
561 // Knghtbrd: should assign platform-specific extensions here
563 gl_platformextensions = "";
564 gl_videosyncavailable = false;
568 vid_numjoysticks = SDL_NumJoysticks();
569 vid_numjoysticks = bound(0, vid_numjoysticks, MAX_JOYSTICKS);
570 Cvar_SetValueQuick(&joy_detected, vid_numjoysticks);
571 Con_Printf("%d SDL joystick(s) found:\n", vid_numjoysticks);
572 memset(vid_joysticks, 0, sizeof(vid_joysticks));
573 for (i = 0;i < vid_numjoysticks;i++)
576 joy = vid_joysticks[i] = SDL_JoystickOpen(i);
579 Con_Printf("joystick #%i: open failed: %s\n", i, SDL_GetError());
582 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));
586 vid_activewindow = false;
587 vid_usingmouse = false;
591 void VID_Shutdown (void)
593 // this is needed to retry gamma after a vid_restart
594 VID_RestoreSystemGamma();
597 SDL_QuitSubSystem(SDL_INIT_VIDEO);
600 int VID_SetGamma (unsigned short *ramps, int rampsize)
602 return !SDL_SetGammaRamp (ramps, ramps + rampsize, ramps + rampsize*2);
605 int VID_GetGamma (unsigned short *ramps, int rampsize)
607 return !SDL_GetGammaRamp (ramps, ramps + rampsize, ramps + rampsize*2);
610 void VID_Finish (qboolean allowmousegrab)
613 qboolean vid_usemouse;
615 //react on appstate changes
616 appstate = SDL_GetAppState();
618 vid_hidden = !(appstate & SDL_APPACTIVE);
620 if( vid_hidden || !( appstate & SDL_APPMOUSEFOCUS ) || !( appstate & SDL_APPINPUTFOCUS ) )
621 vid_activewindow = false;
623 vid_activewindow = true;
625 vid_usemouse = false;
626 if( allowmousegrab && vid_mouse.integer && !key_consoleactive && (key_dest != key_game || !cls.demoplayback) )
628 if( vid_isfullscreen )
630 if( !vid_activewindow )
631 vid_usemouse = false;
633 IN_Activate(vid_usemouse);
635 VID_UpdateGamma(false, 256);
637 if (r_render.integer && !vid_hidden)
640 if (r_speeds.integer || gl_finish.integer)
642 qglFinish();CHECKGLERROR
644 SDL_GL_SwapBuffers();