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_hidden && (vid_activewindow || !snd_mutewhenidle.integer))
387 sound_active = false;
396 void *GL_GetProcAddress(const char *name)
399 p = SDL_GL_GetProcAddress(name);
403 static int Sys_EventFilter( SDL_Event *event );
404 static qboolean vid_sdl_initjoysticksystem = false;
407 Cvar_RegisterVariable(&joy_detected);
408 Cvar_RegisterVariable(&joy_enable);
409 Cvar_RegisterVariable(&joy_index);
410 Cvar_RegisterVariable(&joy_axisforward);
411 Cvar_RegisterVariable(&joy_axisside);
412 Cvar_RegisterVariable(&joy_axisup);
413 Cvar_RegisterVariable(&joy_axispitch);
414 Cvar_RegisterVariable(&joy_axisyaw);
415 //Cvar_RegisterVariable(&joy_axisroll);
416 Cvar_RegisterVariable(&joy_deadzoneforward);
417 Cvar_RegisterVariable(&joy_deadzoneside);
418 Cvar_RegisterVariable(&joy_deadzoneup);
419 Cvar_RegisterVariable(&joy_deadzonepitch);
420 Cvar_RegisterVariable(&joy_deadzoneyaw);
421 //Cvar_RegisterVariable(&joy_deadzoneroll);
422 Cvar_RegisterVariable(&joy_sensitivityforward);
423 Cvar_RegisterVariable(&joy_sensitivityside);
424 Cvar_RegisterVariable(&joy_sensitivityup);
425 Cvar_RegisterVariable(&joy_sensitivitypitch);
426 Cvar_RegisterVariable(&joy_sensitivityyaw);
427 //Cvar_RegisterVariable(&joy_sensitivityroll);
429 if (SDL_Init(SDL_INIT_VIDEO) < 0)
430 Sys_Error ("Failed to init SDL video subsystem: %s", SDL_GetError());
431 vid_sdl_initjoysticksystem = SDL_Init(SDL_INIT_JOYSTICK) >= 0;
432 if (vid_sdl_initjoysticksystem)
433 Con_Printf("Failed to init SDL joystick subsystem: %s\n", SDL_GetError());
434 vid_isfullscreen = false;
437 // set the icon (we dont use SDL here since it would be too much a PITA)
439 #include "resource.h"
440 #include <SDL_syswm.h>
441 static void VID_SetCaption()
447 SDL_WM_SetCaption( gamename, NULL );
449 // get the HWND handle
450 SDL_VERSION( &info.version );
451 if( !SDL_GetWMInfo( &info ) )
454 icon = LoadIcon( GetModuleHandle( NULL ), MAKEINTRESOURCE( IDI_ICON1 ) );
455 #ifndef _W64 //If Windows 64bit data types don't exist
456 #define SetClassLongPtr SetClassLong
457 #define GCLP_HICON GCL_HICON
458 #define LONG_PTR LONG
460 SetClassLongPtr( info.window, GCLP_HICON, (LONG_PTR)icon );
463 static void VID_SetCaption()
465 SDL_WM_SetCaption( gamename, NULL );
469 static void VID_OutputVersion()
471 const SDL_version *version;
472 version = SDL_Linked_Version();
473 Con_Printf( "Linked against SDL version %d.%d.%d\n"
474 "Using SDL library version %d.%d.%d\n",
475 SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL,
476 version->major, version->minor, version->patch );
479 int VID_InitMode(int fullscreen, int width, int height, int bpp, int refreshrate, int stereobuffer)
482 static int notfirstvideomode = false;
483 int flags = SDL_OPENGL;
484 const char *drivername;
490 We cant switch from one OpenGL video mode to another.
491 Thus we first switch to some stupid 2D mode and then back to OpenGL.
493 if (notfirstvideomode)
494 SDL_SetVideoMode( 0, 0, 0, 0 );
495 notfirstvideomode = true;
497 // SDL usually knows best
500 // 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
501 i = COM_CheckParm("-gl_driver");
502 if (i && i < com_argc - 1)
503 drivername = com_argv[i + 1];
504 if (SDL_GL_LoadLibrary(drivername) < 0)
506 Con_Printf("Unable to load GL driver \"%s\": %s\n", drivername, SDL_GetError());
510 if ((qglGetString = (const GLubyte* (GLAPIENTRY *)(GLenum name))GL_GetProcAddress("glGetString")) == NULL)
513 Con_Print("Required OpenGL function glGetString not found\n");
517 // Knghtbrd: should do platform-specific extension string function here
519 vid_isfullscreen = false;
521 flags |= SDL_FULLSCREEN;
522 vid_isfullscreen = true;
525 SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1);
528 SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 8);
529 SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 8);
530 SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 8);
531 SDL_GL_SetAttribute (SDL_GL_ALPHA_SIZE, 8);
532 SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 24);
533 SDL_GL_SetAttribute (SDL_GL_STENCIL_SIZE, 8);
537 SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 1);
538 SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 1);
539 SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 1);
540 SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 16);
543 SDL_GL_SetAttribute (SDL_GL_STEREO, 1);
545 screen = SDL_SetVideoMode(width, height, bpp, flags);
548 Con_Printf("Failed to set video mode to %ix%i: %s\n", width, height, SDL_GetError());
555 // set up an event filter to ask confirmation on close button in WIN32
556 SDL_SetEventFilter( (SDL_EventFilter) Sys_EventFilter );
558 SDL_EnableUNICODE( SDL_ENABLE );
559 // enable key repeat since everyone expects it
560 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
562 gl_renderer = (const char *)qglGetString(GL_RENDERER);
563 gl_vendor = (const char *)qglGetString(GL_VENDOR);
564 gl_version = (const char *)qglGetString(GL_VERSION);
565 gl_extensions = (const char *)qglGetString(GL_EXTENSIONS);
567 // Knghtbrd: should assign platform-specific extensions here
569 gl_platformextensions = "";
570 gl_videosyncavailable = false;
574 vid_numjoysticks = SDL_NumJoysticks();
575 vid_numjoysticks = bound(0, vid_numjoysticks, MAX_JOYSTICKS);
576 Cvar_SetValueQuick(&joy_detected, vid_numjoysticks);
577 Con_Printf("%d SDL joystick(s) found:\n", vid_numjoysticks);
578 memset(vid_joysticks, 0, sizeof(vid_joysticks));
579 for (i = 0;i < vid_numjoysticks;i++)
582 joy = vid_joysticks[i] = SDL_JoystickOpen(i);
585 Con_Printf("joystick #%i: open failed: %s\n", i, SDL_GetError());
588 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));
592 vid_activewindow = false;
593 vid_usingmouse = false;
597 void VID_Shutdown (void)
599 // this is needed to retry gamma after a vid_restart
600 VID_RestoreSystemGamma();
603 SDL_QuitSubSystem(SDL_INIT_VIDEO);
606 int VID_SetGamma (unsigned short *ramps, int rampsize)
608 return !SDL_SetGammaRamp (ramps, ramps + rampsize, ramps + rampsize*2);
611 int VID_GetGamma (unsigned short *ramps, int rampsize)
613 return !SDL_GetGammaRamp (ramps, ramps + rampsize, ramps + rampsize*2);
616 void VID_Finish (qboolean allowmousegrab)
619 qboolean vid_usemouse;
621 //react on appstate changes
622 appstate = SDL_GetAppState();
624 vid_hidden = !(appstate & SDL_APPACTIVE);
626 if( vid_hidden || !( appstate & SDL_APPMOUSEFOCUS ) || !( appstate & SDL_APPINPUTFOCUS ) )
627 vid_activewindow = false;
629 vid_activewindow = true;
631 vid_usemouse = false;
632 if( allowmousegrab && vid_mouse.integer && !key_consoleactive && (key_dest != key_game || !cls.demoplayback) )
634 if( vid_isfullscreen )
636 if( !vid_activewindow )
637 vid_usemouse = false;
639 IN_Activate(vid_usemouse);
641 VID_UpdateGamma(false, 256);
643 if (r_render.integer && !vid_hidden)
646 if (r_speeds.integer || gl_finish.integer)
648 qglFinish();CHECKGLERROR
650 SDL_GL_SwapBuffers();