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)
476 int flags = SDL_OPENGL;
477 const char *drivername;
483 We cant switch from one OpenGL video mode to another.
484 Thus we first switch to some stupid 2D mode and then back to OpenGL.
486 SDL_SetVideoMode( 0, 0, 0, 0 );
488 // SDL usually knows best
491 // 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
492 i = COM_CheckParm("-gl_driver");
493 if (i && i < com_argc - 1)
494 drivername = com_argv[i + 1];
495 if (SDL_GL_LoadLibrary(drivername) < 0)
497 Con_Printf("Unable to load GL driver \"%s\": %s\n", drivername, SDL_GetError());
501 if ((qglGetString = (const GLubyte* (GLAPIENTRY *)(GLenum name))GL_GetProcAddress("glGetString")) == NULL)
504 Con_Print("Required OpenGL function glGetString not found\n");
508 // Knghtbrd: should do platform-specific extension string function here
510 vid_isfullscreen = false;
512 flags |= SDL_FULLSCREEN;
513 vid_isfullscreen = true;
516 SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1);
519 SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 8);
520 SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 8);
521 SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 8);
522 SDL_GL_SetAttribute (SDL_GL_ALPHA_SIZE, 8);
523 SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 24);
524 SDL_GL_SetAttribute (SDL_GL_STENCIL_SIZE, 8);
528 SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 1);
529 SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 1);
530 SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 1);
531 SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 16);
534 screen = SDL_SetVideoMode(width, height, bpp, flags);
537 Con_Printf("Failed to set video mode to %ix%i: %s\n", width, height, SDL_GetError());
544 // set up an event filter to ask confirmation on close button in WIN32
545 SDL_SetEventFilter( (SDL_EventFilter) Sys_EventFilter );
547 SDL_EnableUNICODE( SDL_ENABLE );
548 // enable key repeat since everyone expects it
549 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
551 gl_renderer = (const char *)qglGetString(GL_RENDERER);
552 gl_vendor = (const char *)qglGetString(GL_VENDOR);
553 gl_version = (const char *)qglGetString(GL_VERSION);
554 gl_extensions = (const char *)qglGetString(GL_EXTENSIONS);
556 // Knghtbrd: should assign platform-specific extensions here
558 gl_platformextensions = "";
559 gl_videosyncavailable = false;
563 vid_numjoysticks = SDL_NumJoysticks();
564 vid_numjoysticks = bound(0, vid_numjoysticks, MAX_JOYSTICKS);
565 Cvar_SetValueQuick(&joy_detected, vid_numjoysticks);
566 Con_Printf("%d SDL joystick(s) found:\n", vid_numjoysticks);
567 memset(vid_joysticks, 0, sizeof(vid_joysticks));
568 for (i = 0;i < vid_numjoysticks;i++)
571 joy = vid_joysticks[i] = SDL_JoystickOpen(i);
574 Con_Printf("joystick #%i: open failed: %s\n", i, SDL_GetError());
577 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));
581 vid_activewindow = false;
582 vid_usingmouse = false;
586 void VID_Shutdown (void)
589 SDL_QuitSubSystem(SDL_INIT_VIDEO);
592 int VID_SetGamma (unsigned short *ramps, int rampsize)
594 return !SDL_SetGammaRamp (ramps, ramps + rampsize, ramps + rampsize*2);
597 int VID_GetGamma (unsigned short *ramps, int rampsize)
599 return !SDL_GetGammaRamp (ramps, ramps + rampsize, ramps + rampsize*2);
602 void VID_Finish (qboolean allowmousegrab)
605 qboolean vid_usemouse;
607 //react on appstate changes
608 appstate = SDL_GetAppState();
610 vid_hidden = !(appstate & SDL_APPACTIVE);
612 if( vid_hidden || !( appstate & SDL_APPMOUSEFOCUS ) || !( appstate & SDL_APPINPUTFOCUS ) )
613 vid_activewindow = false;
615 vid_activewindow = true;
617 vid_usemouse = false;
618 if( allowmousegrab && vid_mouse.integer && !key_consoleactive && (key_dest != key_game || !cls.demoplayback) )
620 if( vid_isfullscreen )
622 if( !vid_activewindow )
623 vid_usemouse = false;
625 IN_Activate(vid_usemouse);
627 VID_UpdateGamma(false, 256);
629 if (r_render.integer && !vid_hidden)
632 if (r_speeds.integer || gl_finish.integer)
634 qglFinish();CHECKGLERROR
636 SDL_GL_SwapBuffers();