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
21 #include <SDL_syswm.h>
26 #include "dpsoftrast.h"
29 #include <Carbon/Carbon.h>
30 #include <IOKit/hidsystem/IOHIDLib.h>
31 #include <IOKit/hidsystem/IOHIDParameter.h>
32 #include <IOKit/hidsystem/event_status_driver.h>
33 static cvar_t apple_mouse_noaccel = {CVAR_SAVE, "apple_mouse_noaccel", "1", "disables mouse acceleration while DarkPlaces is active"};
34 static qboolean vid_usingnoaccel;
35 static double originalMouseSpeed = -1.0;
36 io_connect_t IN_GetIOHandle(void)
38 io_connect_t iohandle = MACH_PORT_NULL;
40 io_service_t iohidsystem = MACH_PORT_NULL;
41 mach_port_t masterport;
43 status = IOMasterPort(MACH_PORT_NULL, &masterport);
44 if(status != KERN_SUCCESS)
47 iohidsystem = IORegistryEntryFromPath(masterport, kIOServicePlane ":/IOResources/IOHIDSystem");
51 status = IOServiceOpen(iohidsystem, mach_task_self(), kIOHIDParamConnectType, &iohandle);
52 IOObjectRelease(iohidsystem);
62 // Tell startup code that we have a client
63 int cl_available = true;
65 qboolean vid_supportrefreshrate = false;
67 cvar_t vid_soft = {CVAR_SAVE, "vid_soft", "0", "enables use of the DarkPlaces Software Rasterizer rather than OpenGL or Direct3D"};
68 cvar_t joy_detected = {CVAR_READONLY, "joy_detected", "0", "number of joysticks detected by engine"};
69 cvar_t joy_enable = {CVAR_SAVE, "joy_enable", "0", "enables joystick support"};
70 cvar_t joy_index = {0, "joy_index", "0", "selects which joystick to use if you have multiple"};
71 cvar_t joy_axisforward = {0, "joy_axisforward", "1", "which joystick axis to query for forward/backward movement"};
72 cvar_t joy_axisside = {0, "joy_axisside", "0", "which joystick axis to query for right/left movement"};
73 cvar_t joy_axisup = {0, "joy_axisup", "-1", "which joystick axis to query for up/down movement"};
74 cvar_t joy_axispitch = {0, "joy_axispitch", "3", "which joystick axis to query for looking up/down"};
75 cvar_t joy_axisyaw = {0, "joy_axisyaw", "2", "which joystick axis to query for looking right/left"};
76 cvar_t joy_axisroll = {0, "joy_axisroll", "-1", "which joystick axis to query for tilting head right/left"};
77 cvar_t joy_deadzoneforward = {0, "joy_deadzoneforward", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
78 cvar_t joy_deadzoneside = {0, "joy_deadzoneside", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
79 cvar_t joy_deadzoneup = {0, "joy_deadzoneup", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
80 cvar_t joy_deadzonepitch = {0, "joy_deadzonepitch", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
81 cvar_t joy_deadzoneyaw = {0, "joy_deadzoneyaw", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
82 cvar_t joy_deadzoneroll = {0, "joy_deadzoneroll", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
83 cvar_t joy_sensitivityforward = {0, "joy_sensitivityforward", "-1", "movement multiplier"};
84 cvar_t joy_sensitivityside = {0, "joy_sensitivityside", "1", "movement multiplier"};
85 cvar_t joy_sensitivityup = {0, "joy_sensitivityup", "1", "movement multiplier"};
86 cvar_t joy_sensitivitypitch = {0, "joy_sensitivitypitch", "1", "movement multiplier"};
87 cvar_t joy_sensitivityyaw = {0, "joy_sensitivityyaw", "-1", "movement multiplier"};
88 cvar_t joy_sensitivityroll = {0, "joy_sensitivityroll", "1", "movement multiplier"};
89 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"};
91 static qboolean vid_usingmouse = false;
92 static qboolean vid_usinghidecursor = false;
93 static qboolean vid_isfullscreen;
94 static int vid_numjoysticks = 0;
95 #define MAX_JOYSTICKS 8
96 static SDL_Joystick *vid_joysticks[MAX_JOYSTICKS];
98 static int win_half_width = 50;
99 static int win_half_height = 50;
100 static int video_bpp, video_flags;
102 static SDL_Surface *screen;
103 static SDL_Surface *vid_softsurface;
105 // joystick axes state
106 #define MAX_JOYSTICK_AXES 16
113 static joy_axiscache_t joy_axescache[MAX_JOYSTICK_AXES];
115 /////////////////////////
118 //TODO: Add joystick support
119 //TODO: Add error checking
122 //keysym to quake keysym mapping
123 #define tenoh 0,0,0,0,0, 0,0,0,0,0
124 #define fiftyoh tenoh, tenoh, tenoh, tenoh, tenoh
125 #define hundredoh fiftyoh, fiftyoh
126 static unsigned int tbl_sdltoquake[] =
128 0,0,0,0, //SDLK_UNKNOWN = 0,
129 0,0,0,0, //SDLK_FIRST = 0,
130 K_BACKSPACE, //SDLK_BACKSPACE = 8,
131 K_TAB, //SDLK_TAB = 9,
133 0, //SDLK_CLEAR = 12,
134 K_ENTER, //SDLK_RETURN = 13,
136 K_PAUSE, //SDLK_PAUSE = 19,
138 K_ESCAPE, //SDLK_ESCAPE = 27,
140 K_SPACE, //SDLK_SPACE = 32,
141 '!', //SDLK_EXCLAIM = 33,
142 '"', //SDLK_QUOTEDBL = 34,
143 '#', //SDLK_HASH = 35,
144 '$', //SDLK_DOLLAR = 36,
146 '&', //SDLK_AMPERSAND = 38,
147 '\'', //SDLK_QUOTE = 39,
148 '(', //SDLK_LEFTPAREN = 40,
149 ')', //SDLK_RIGHTPAREN = 41,
150 '*', //SDLK_ASTERISK = 42,
151 '+', //SDLK_PLUS = 43,
152 ',', //SDLK_COMMA = 44,
153 '-', //SDLK_MINUS = 45,
154 '.', //SDLK_PERIOD = 46,
155 '/', //SDLK_SLASH = 47,
166 ':', //SDLK_COLON = 58,
167 ';', //SDLK_SEMICOLON = 59,
168 '<', //SDLK_LESS = 60,
169 '=', //SDLK_EQUALS = 61,
170 '>', //SDLK_GREATER = 62,
171 '?', //SDLK_QUESTION = 63,
173 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,
174 '[', //SDLK_LEFTBRACKET = 91,
175 '\\', //SDLK_BACKSLASH = 92,
176 ']', //SDLK_RIGHTBRACKET = 93,
177 '^', //SDLK_CARET = 94,
178 '_', //SDLK_UNDERSCORE = 95,
179 '`', //SDLK_BACKQUOTE = 96,
207 K_DEL, //SDLK_DELETE = 127,
208 hundredoh /*227*/, tenoh, tenoh, 0,0,0,0,0,0,0,0,
209 K_KP_0, //SDLK_KP0 = 256,
210 K_KP_1, //SDLK_KP1 = 257,
211 K_KP_2, //SDLK_KP2 = 258,
212 K_KP_3, //SDLK_KP3 = 259,
213 K_KP_4, //SDLK_KP4 = 260,
214 K_KP_5, //SDLK_KP5 = 261,
215 K_KP_6, //SDLK_KP6 = 262,
216 K_KP_7, //SDLK_KP7 = 263,
217 K_KP_8, //SDLK_KP8 = 264,
218 K_KP_9, //SDLK_KP9 = 265,
219 K_KP_PERIOD,//SDLK_KP_PERIOD = 266,
220 K_KP_DIVIDE,//SDLK_KP_DIVIDE = 267,
221 K_KP_MULTIPLY,//SDLK_KP_MULTIPLY= 268,
222 K_KP_MINUS, //SDLK_KP_MINUS = 269,
223 K_KP_PLUS, //SDLK_KP_PLUS = 270,
224 K_KP_ENTER, //SDLK_KP_ENTER = 271,
225 K_KP_EQUALS,//SDLK_KP_EQUALS = 272,
226 K_UPARROW, //SDLK_UP = 273,
227 K_DOWNARROW,//SDLK_DOWN = 274,
228 K_RIGHTARROW,//SDLK_RIGHT = 275,
229 K_LEFTARROW,//SDLK_LEFT = 276,
230 K_INS, //SDLK_INSERT = 277,
231 K_HOME, //SDLK_HOME = 278,
232 K_END, //SDLK_END = 279,
233 K_PGUP, //SDLK_PAGEUP = 280,
234 K_PGDN, //SDLK_PAGEDOWN = 281,
235 K_F1, //SDLK_F1 = 282,
236 K_F2, //SDLK_F2 = 283,
237 K_F3, //SDLK_F3 = 284,
238 K_F4, //SDLK_F4 = 285,
239 K_F5, //SDLK_F5 = 286,
240 K_F6, //SDLK_F6 = 287,
241 K_F7, //SDLK_F7 = 288,
242 K_F8, //SDLK_F8 = 289,
243 K_F9, //SDLK_F9 = 290,
244 K_F10, //SDLK_F10 = 291,
245 K_F11, //SDLK_F11 = 292,
246 K_F12, //SDLK_F12 = 293,
251 K_NUMLOCK, //SDLK_NUMLOCK = 300,
252 K_CAPSLOCK, //SDLK_CAPSLOCK = 301,
253 K_SCROLLOCK,//SDLK_SCROLLOCK= 302,
254 K_SHIFT, //SDLK_RSHIFT = 303,
255 K_SHIFT, //SDLK_LSHIFT = 304,
256 K_CTRL, //SDLK_RCTRL = 305,
257 K_CTRL, //SDLK_LCTRL = 306,
258 K_ALT, //SDLK_RALT = 307,
259 K_ALT, //SDLK_LALT = 308,
260 0, //SDLK_RMETA = 309,
261 0, //SDLK_LMETA = 310,
262 0, //SDLK_LSUPER = 311, /* Left "Windows" key */
263 0, //SDLK_RSUPER = 312, /* Right "Windows" key */
264 K_ALT, //SDLK_MODE = 313, /* "Alt Gr" key */
265 0, //SDLK_COMPOSE = 314, /* Multi-key compose key */
266 0, //SDLK_HELP = 315,
267 0, //SDLK_PRINT = 316,
268 0, //SDLK_SYSREQ = 317,
269 K_PAUSE, //SDLK_BREAK = 318,
270 0, //SDLK_MENU = 319,
271 0, //SDLK_POWER = 320, /* Power Macintosh power key */
272 'e', //SDLK_EURO = 321, /* Some european keyboards */
273 0 //SDLK_UNDO = 322, /* Atari keyboard has Undo */
279 static int MapKey( unsigned int sdlkey )
281 if( sdlkey > sizeof(tbl_sdltoquake)/ sizeof(int) )
283 return tbl_sdltoquake[ sdlkey ];
286 void VID_SetMouse(qboolean fullscreengrab, qboolean relative, qboolean hidecursor)
290 if(vid_usingmouse && (vid_usingnoaccel != !!apple_mouse_noaccel.integer))
291 VID_SetMouse(false, false, false); // ungrab first!
293 if (vid_usingmouse != relative)
295 vid_usingmouse = relative;
296 cl_ignoremousemoves = 2;
297 SDL_WM_GrabInput( relative ? SDL_GRAB_ON : SDL_GRAB_OFF );
301 // Save the status of mouse acceleration
302 originalMouseSpeed = -1.0; // in case of error
303 if(apple_mouse_noaccel.integer)
305 io_connect_t mouseDev = IN_GetIOHandle();
308 if(IOHIDGetAccelerationWithKey(mouseDev, CFSTR(kIOHIDMouseAccelerationType), &originalMouseSpeed) == kIOReturnSuccess)
310 Con_DPrintf("previous mouse acceleration: %f\n", originalMouseSpeed);
311 if(IOHIDSetAccelerationWithKey(mouseDev, CFSTR(kIOHIDMouseAccelerationType), -1.0) != kIOReturnSuccess)
313 Con_Print("Could not disable mouse acceleration (failed at IOHIDSetAccelerationWithKey).\n");
314 Cvar_SetValueQuick(&apple_mouse_noaccel, 0);
319 Con_Print("Could not disable mouse acceleration (failed at IOHIDGetAccelerationWithKey).\n");
320 Cvar_SetValueQuick(&apple_mouse_noaccel, 0);
322 IOServiceClose(mouseDev);
326 Con_Print("Could not disable mouse acceleration (failed at IO_GetIOHandle).\n");
327 Cvar_SetValueQuick(&apple_mouse_noaccel, 0);
331 vid_usingnoaccel = !!apple_mouse_noaccel.integer;
335 if(originalMouseSpeed != -1.0)
337 io_connect_t mouseDev = IN_GetIOHandle();
340 Con_DPrintf("restoring mouse acceleration to: %f\n", originalMouseSpeed);
341 if(IOHIDSetAccelerationWithKey(mouseDev, CFSTR(kIOHIDMouseAccelerationType), originalMouseSpeed) != kIOReturnSuccess)
342 Con_Print("Could not re-enable mouse acceleration (failed at IOHIDSetAccelerationWithKey).\n");
343 IOServiceClose(mouseDev);
346 Con_Print("Could not re-enable mouse acceleration (failed at IO_GetIOHandle).\n");
351 if (vid_usinghidecursor != hidecursor)
353 vid_usinghidecursor = hidecursor;
354 SDL_ShowCursor( hidecursor ? SDL_DISABLE : SDL_ENABLE);
358 static double IN_JoystickGetAxis(SDL_Joystick *joy, int axis, double sensitivity, double deadzone)
361 if (axis < 0 || axis >= SDL_JoystickNumAxes(joy))
362 return 0; // no such axis on this joystick
363 value = SDL_JoystickGetAxis(joy, axis) * (1.0 / 32767.0);
364 value = bound(-1, value, 1);
365 if (fabs(value) < deadzone)
366 return 0; // within deadzone around center
367 return value * sensitivity;
370 /////////////////////
371 // Joystick axis keyevents
372 // a sort of hack emulating Arrow keys for joystick axises
373 // as some drives dont send such keyevents for them
374 // additionally we should block drivers that do send arrow keyevents to prevent double events
377 static void IN_JoystickKeyeventForAxis(SDL_Joystick *joy, int axis, int key_pos, int key_neg)
381 if (axis < 0 || axis >= SDL_JoystickNumAxes(joy))
382 return; // no such axis on this joystick
384 joytime = Sys_DoubleTime();
385 // no key event, continuous keydown event
386 if (joy_axescache[axis].move == joy_axescache[axis].oldmove)
388 if (joy_axescache[axis].move != 0 && joytime > joy_axescache[axis].keytime)
390 //Con_Printf("joy %s %i %f\n", Key_KeynumToString((joy_axescache[axis].move > 0) ? key_pos : key_neg), 1, cl.time);
391 Key_Event((joy_axescache[axis].move > 0) ? key_pos : key_neg, 0, 1);
392 joy_axescache[axis].keytime = joytime + 0.5 / 20;
396 // generate key up event
397 if (joy_axescache[axis].oldmove)
399 //Con_Printf("joy %s %i %f\n", Key_KeynumToString((joy_axescache[axis].oldmove > 0) ? key_pos : key_neg), 1, cl.time);
400 Key_Event((joy_axescache[axis].oldmove > 0) ? key_pos : key_neg, 0, 0);
402 // generate key down event
403 if (joy_axescache[axis].move)
405 //Con_Printf("joy %s %i %f\n", Key_KeynumToString((joy_axescache[axis].move > 0) ? key_pos : key_neg), 1, cl.time);
406 Key_Event((joy_axescache[axis].move > 0) ? key_pos : key_neg, 0, 1);
407 joy_axescache[axis].keytime = joytime + 0.5;
411 static qboolean IN_JoystickBlockDoubledKeyEvents(int keycode)
413 if (!joy_axiskeyevents.integer)
416 // block keyevent if it's going to be provided by joystick keyevent system
417 if (vid_numjoysticks && joy_enable.integer && joy_index.integer >= 0 && joy_index.integer < vid_numjoysticks)
419 SDL_Joystick *joy = vid_joysticks[joy_index.integer];
421 if (keycode == K_UPARROW || keycode == K_DOWNARROW)
422 if (IN_JoystickGetAxis(joy, joy_axisforward.integer, 1, 0.01) || joy_axescache[joy_axisforward.integer].move || joy_axescache[joy_axisforward.integer].oldmove)
424 if (keycode == K_RIGHTARROW || keycode == K_LEFTARROW)
425 if (IN_JoystickGetAxis(joy, joy_axisside.integer, 1, 0.01) || joy_axescache[joy_axisside.integer].move || joy_axescache[joy_axisside.integer].oldmove)
432 /////////////////////
439 static int old_x = 0, old_y = 0;
440 static int stuck = 0;
441 int x, y, numaxes, numballs;
445 if(vid_stick_mouse.integer)
447 // have the mouse stuck in the middle, example use: prevent expose effect of beryl during the game when not using
448 // window grabbing. --blub
450 // we need 2 frames to initialize the center position
453 SDL_WarpMouse(win_half_width, win_half_height);
454 SDL_GetMouseState(&x, &y);
455 SDL_GetRelativeMouseState(&x, &y);
458 SDL_GetRelativeMouseState(&x, &y);
459 in_mouse_x = x + old_x;
460 in_mouse_y = y + old_y;
461 SDL_GetMouseState(&x, &y);
462 old_x = x - win_half_width;
463 old_y = y - win_half_height;
464 SDL_WarpMouse(win_half_width, win_half_height);
467 SDL_GetRelativeMouseState( &x, &y );
473 SDL_GetMouseState(&x, &y);
474 in_windowmouse_x = x;
475 in_windowmouse_y = y;
477 if (vid_numjoysticks && joy_enable.integer && joy_index.integer >= 0 && joy_index.integer < vid_numjoysticks)
479 SDL_Joystick *joy = vid_joysticks[joy_index.integer];
481 // balls convert to mousemove
482 numballs = SDL_JoystickNumBalls(joy);
483 for (j = 0;j < numballs;j++)
485 SDL_JoystickGetBall(joy, j, &x, &y);
491 cl.cmd.forwardmove += IN_JoystickGetAxis(joy, joy_axisforward.integer, joy_sensitivityforward.value, joy_deadzoneforward.value) * cl_forwardspeed.value;
492 cl.cmd.sidemove += IN_JoystickGetAxis(joy, joy_axisside.integer, joy_sensitivityside.value, joy_deadzoneside.value) * cl_sidespeed.value;
493 cl.cmd.upmove += IN_JoystickGetAxis(joy, joy_axisup.integer, joy_sensitivityup.value, joy_deadzoneup.value) * cl_upspeed.value;
494 cl.viewangles[0] += IN_JoystickGetAxis(joy, joy_axispitch.integer, joy_sensitivitypitch.value, joy_deadzonepitch.value) * cl.realframetime * cl_pitchspeed.value;
495 cl.viewangles[1] += IN_JoystickGetAxis(joy, joy_axisyaw.integer, joy_sensitivityyaw.value, joy_deadzoneyaw.value) * cl.realframetime * cl_yawspeed.value;
496 //cl.viewangles[2] += IN_JoystickGetAxis(joy, joy_axisroll.integer, joy_sensitivityroll.value, joy_deadzoneroll.value) * cl.realframetime * cl_rollspeed.value;
498 // cache state of axes to emulate button events for them
499 numaxes = min(MAX_JOYSTICK_AXES, SDL_JoystickNumAxes(joy));
500 for (j = 0; j < numaxes; j++)
502 joy_axescache[j].oldmove = joy_axescache[j].move;
503 joy_axescache[j].move = IN_JoystickGetAxis(joy, j, 1, 0.01);
507 if (joy_axiskeyevents.integer)
509 IN_JoystickKeyeventForAxis(joy, joy_axisforward.integer, K_DOWNARROW, K_UPARROW);
510 IN_JoystickKeyeventForAxis(joy, joy_axisside.integer, K_RIGHTARROW, K_LEFTARROW);
515 /////////////////////
519 static int Sys_EventFilter( SDL_Event *event )
521 //TODO: Add a quit query in linux, too - though linux user are more likely to know what they do
522 if (event->type == SDL_QUIT)
525 if (MessageBox( NULL, "Are you sure you want to quit?", "Confirm Exit", MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION ) == IDNO)
533 static qboolean sdl_needs_restart;
534 static void sdl_start(void)
537 static void sdl_shutdown(void)
539 sdl_needs_restart = false;
541 static void sdl_newmap(void)
546 static keynum_t buttonremap[18] =
568 void Sys_SendKeyEvents( void )
570 static qboolean sound_active = true;
574 while( SDL_PollEvent( &event ) )
575 switch( event.type ) {
581 keycode = MapKey(event.key.keysym.sym);
582 if (!IN_JoystickBlockDoubledKeyEvents(keycode))
583 Key_Event(keycode, event.key.keysym.unicode, (event.key.state == SDL_PRESSED));
585 case SDL_ACTIVEEVENT:
586 if( event.active.state & SDL_APPACTIVE )
588 if( event.active.gain )
594 case SDL_MOUSEBUTTONDOWN:
595 case SDL_MOUSEBUTTONUP:
596 if (event.button.button <= 18)
597 Key_Event( buttonremap[event.button.button - 1], 0, event.button.state == SDL_PRESSED );
599 case SDL_JOYBUTTONDOWN:
600 if (!joy_enable.integer)
601 break; // ignore down events if joystick has been disabled
602 case SDL_JOYBUTTONUP:
603 if (event.jbutton.button < 48)
604 Key_Event( event.jbutton.button + (event.jbutton.button < 16 ? K_JOY1 : K_AUX1 - 16), 0, (event.jbutton.state == SDL_PRESSED) );
606 case SDL_VIDEORESIZE:
607 if(vid_resizable.integer < 2)
609 vid.width = event.resize.w;
610 vid.height = event.resize.h;
611 SDL_SetVideoMode(vid.width, vid.height, video_bpp, video_flags);
614 SDL_FreeSurface(vid_softsurface);
615 vid_softsurface = SDL_CreateRGBSurface(SDL_SWSURFACE, vid.width, vid.height, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
616 vid.softpixels = vid_softsurface->pixels;
617 SDL_SetAlpha(vid_softsurface, 0, 255);
618 if (vid.softdepthpixels)
619 free(vid.softdepthpixels);
620 vid.softdepthpixels = calloc(1, vid.width * vid.height * 4);
623 // better not call R_Modules_Restart from here directly, as this may wreak havoc...
624 // so, let's better queue it for next frame
625 if(!sdl_needs_restart)
627 Cbuf_AddText("\nr_restart\n");
628 sdl_needs_restart = true;
635 // enable/disable sound on focus gain/loss
636 if ((!vid_hidden && vid_activewindow) || !snd_mutewhenidle.integer)
649 sound_active = false;
658 void *GL_GetProcAddress(const char *name)
661 p = SDL_GL_GetProcAddress(name);
665 static int Sys_EventFilter( SDL_Event *event );
666 static qboolean vid_sdl_initjoysticksystem = false;
671 Cvar_RegisterVariable(&apple_mouse_noaccel);
673 Cvar_RegisterVariable(&vid_soft);
674 Cvar_RegisterVariable(&joy_detected);
675 Cvar_RegisterVariable(&joy_enable);
676 Cvar_RegisterVariable(&joy_index);
677 Cvar_RegisterVariable(&joy_axisforward);
678 Cvar_RegisterVariable(&joy_axisside);
679 Cvar_RegisterVariable(&joy_axisup);
680 Cvar_RegisterVariable(&joy_axispitch);
681 Cvar_RegisterVariable(&joy_axisyaw);
682 //Cvar_RegisterVariable(&joy_axisroll);
683 Cvar_RegisterVariable(&joy_deadzoneforward);
684 Cvar_RegisterVariable(&joy_deadzoneside);
685 Cvar_RegisterVariable(&joy_deadzoneup);
686 Cvar_RegisterVariable(&joy_deadzonepitch);
687 Cvar_RegisterVariable(&joy_deadzoneyaw);
688 //Cvar_RegisterVariable(&joy_deadzoneroll);
689 Cvar_RegisterVariable(&joy_sensitivityforward);
690 Cvar_RegisterVariable(&joy_sensitivityside);
691 Cvar_RegisterVariable(&joy_sensitivityup);
692 Cvar_RegisterVariable(&joy_sensitivitypitch);
693 Cvar_RegisterVariable(&joy_sensitivityyaw);
694 //Cvar_RegisterVariable(&joy_sensitivityroll);
695 Cvar_RegisterVariable(&joy_axiskeyevents);
698 R_RegisterModule("SDL", sdl_start, sdl_shutdown, sdl_newmap, NULL, NULL);
701 if (SDL_Init(SDL_INIT_VIDEO) < 0)
702 Sys_Error ("Failed to init SDL video subsystem: %s", SDL_GetError());
703 vid_sdl_initjoysticksystem = SDL_Init(SDL_INIT_JOYSTICK) >= 0;
704 if (vid_sdl_initjoysticksystem)
705 Con_Printf("Failed to init SDL joystick subsystem: %s\n", SDL_GetError());
706 vid_isfullscreen = false;
709 // set the icon (we dont use SDL here since it would be too much a PITA)
711 #include "resource.h"
712 #include <SDL_syswm.h>
713 static void VID_SetCaption(void)
719 SDL_WM_SetCaption( gamename, NULL );
721 // get the HWND handle
722 SDL_VERSION( &info.version );
723 if( !SDL_GetWMInfo( &info ) )
726 icon = LoadIcon( GetModuleHandle( NULL ), MAKEINTRESOURCE( IDI_ICON1 ) );
727 #ifndef _W64 //If Windows 64bit data types don't exist
728 #ifndef SetClassLongPtr
729 #define SetClassLongPtr SetClassLong
732 #define GCLP_HICON GCL_HICON
735 #define LONG_PTR LONG
738 SetClassLongPtr( info.window, GCLP_HICON, (LONG_PTR)icon );
740 static void VID_SetIcon_Pre(void)
743 static void VID_SetIcon_Post(void)
747 // Adding the OS independent XPM version --blub
748 #include "darkplaces.xpm"
749 #include "nexuiz.xpm"
750 static SDL_Surface *icon = NULL;
751 static void VID_SetIcon_Pre(void)
754 * Somewhat restricted XPM reader. Only supports XPMs saved by GIMP 2.4 at
755 * default settings with less than 91 colors and transparency.
758 int width, height, colors, isize, i, j;
760 static SDL_Color palette[256];
761 unsigned short palenc[256]; // store color id by char
764 const SDL_version *version;
766 version = SDL_Linked_Version();
767 // only use non-XPM icon support in SDL v1.3 and higher
768 // SDL v1.2 does not support "smooth" transparency, and thus is better
770 if(version->major >= 2 || (version->major == 1 && version->minor >= 3))
772 data = (char *) loadimagepixelsbgra("darkplaces-icon", false, false, false, NULL);
775 unsigned int red = 0x00FF0000;
776 unsigned int green = 0x0000FF00;
777 unsigned int blue = 0x000000FF;
778 unsigned int alpha = 0xFF000000;
780 height = image_height;
782 // reallocate with malloc, as this is in tempmempool (do not want)
784 data = malloc(width * height * 4);
785 memcpy(data, xpm, width * height * 4);
789 icon = SDL_CreateRGBSurface(SDL_SRCALPHA, width, height, 32, LittleLong(red), LittleLong(green), LittleLong(blue), LittleLong(alpha));
792 Con_Printf( "Failed to create surface for the window Icon!\n"
793 "%s\n", SDL_GetError());
802 // we only get here if non-XPM icon was missing, or SDL version is not
803 // sufficient for transparent non-XPM icons
806 xpm = (char *) FS_LoadFile("darkplaces-icon.xpm", tempmempool, false, NULL);
809 idata = XPM_DecodeString(xpm);
817 if(sscanf(data, "%i %i %i %i", &width, &height, &colors, &isize) != 4)
819 // NOTE: Only 1-char colornames are supported
820 Con_Printf("Sorry, but this does not even look similar to an XPM.\n");
826 // NOTE: Only 1-char colornames are supported
827 Con_Printf("This XPM's palette is either huge or idiotically unoptimized. It's key size is %i\n", isize);
831 for(i = 0; i < colors; ++i)
833 unsigned int r, g, b;
836 if(sscanf(idata[i+1], "%c c #%02x%02x%02x", &idx, &r, &g, &b) != 4)
839 if(sscanf(idata[i+1], "%c c Non%1[e]", &idx, foo) != 2) // I take the DailyWTF credit for this. --div0
841 Con_Printf("This XPM's palette looks odd. Can't continue.\n");
846 palette[i].r = 255; // color key
849 thenone = i; // weeeee
854 palette[i].r = r - (r == 255 && g == 0 && b == 255); // change 255/0/255 pink to 254/0/255 for color key
859 palenc[(unsigned char) idx] = i;
862 // allocate the image data
863 data = (char*) malloc(width*height);
865 for(j = 0; j < height; ++j)
867 for(i = 0; i < width; ++i)
869 // casting to the safest possible datatypes ^^
870 data[j * width + i] = palenc[((unsigned char*)idata[colors+j+1])[i]];
876 // SDL_FreeSurface should free the data too
877 // but for completeness' sake...
878 if(icon->flags & SDL_PREALLOC)
881 icon->pixels = NULL; // safety
883 SDL_FreeSurface(icon);
886 icon = SDL_CreateRGBSurface(SDL_SRCCOLORKEY, width, height, 8, 0,0,0,0);// rmask, gmask, bmask, amask); no mask needed
887 // 8 bit surfaces get an empty palette allocated according to the docs
888 // so it's a palette image for sure :) no endian check necessary for the mask
891 Con_Printf( "Failed to create surface for the window Icon!\n"
892 "%s\n", SDL_GetError());
898 SDL_SetPalette(icon, SDL_PHYSPAL|SDL_LOGPAL, palette, 0, colors);
899 SDL_SetColorKey(icon, SDL_SRCCOLORKEY, thenone);
902 SDL_WM_SetIcon(icon, NULL);
904 static void VID_SetIcon_Post(void)
906 #if SDL_VIDEO_DRIVER_X11 && !SDL_VIDEO_DRIVER_QUARTZ
909 const SDL_version *version;
911 version = SDL_Linked_Version();
912 // only use non-XPM icon support in SDL v1.3 and higher
913 // SDL v1.2 does not support "smooth" transparency, and thus is better
915 if(!(version->major >= 2 || (version->major == 1 && version->minor >= 3)))
917 // in this case, we did not set the good icon yet
919 SDL_VERSION(&info.version);
920 if(SDL_GetWMInfo(&info) == 1 && info.subsystem == SDL_SYSWM_X11)
922 data = (char *) loadimagepixelsbgra("darkplaces-icon", false, false, false, NULL);
925 // use _NET_WM_ICON too
926 static long netwm_icon[MAX_NETWM_ICON];
932 if(pos + 2 * image_width * image_height < MAX_NETWM_ICON)
934 netwm_icon[pos++] = image_width;
935 netwm_icon[pos++] = image_height;
936 for(i = 0; i < image_height; ++i)
937 for(j = 0; j < image_width; ++j)
938 netwm_icon[pos++] = BuffLittleLong((unsigned char *) &data[(i*image_width+j)*4]);
942 Con_Printf("Skipping NETWM icon #%d because there is no space left\n", i);
946 data = (char *) loadimagepixelsbgra(va("darkplaces-icon%d", i), false, false, false, NULL);
949 info.info.x11.lock_func();
951 Atom net_wm_icon = XInternAtom(info.info.x11.display, "_NET_WM_ICON", false);
952 XChangeProperty(info.info.x11.display, info.info.x11.wmwindow, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (const unsigned char *) netwm_icon, pos);
954 info.info.x11.unlock_func();
962 static void VID_SetCaption(void)
964 SDL_WM_SetCaption( gamename, NULL );
968 static void VID_OutputVersion(void)
970 const SDL_version *version;
971 version = SDL_Linked_Version();
972 Con_Printf( "Linked against SDL version %d.%d.%d\n"
973 "Using SDL library version %d.%d.%d\n",
974 SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL,
975 version->major, version->minor, version->patch );
978 qboolean VID_InitModeGL(viddef_mode_t *mode)
981 static int notfirstvideomode = false;
982 int flags = SDL_OPENGL;
983 const char *drivername;
985 win_half_width = mode->width>>1;
986 win_half_height = mode->height>>1;
988 if(vid_resizable.integer)
989 flags |= SDL_RESIZABLE;
995 We cant switch from one OpenGL video mode to another.
996 Thus we first switch to some stupid 2D mode and then back to OpenGL.
998 if (notfirstvideomode)
999 SDL_SetVideoMode( 0, 0, 0, 0 );
1000 notfirstvideomode = true;
1002 // SDL usually knows best
1005 // 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
1006 i = COM_CheckParm("-gl_driver");
1007 if (i && i < com_argc - 1)
1008 drivername = com_argv[i + 1];
1009 if (SDL_GL_LoadLibrary(drivername) < 0)
1011 Con_Printf("Unable to load GL driver \"%s\": %s\n", drivername, SDL_GetError());
1015 if ((qglGetString = (const GLubyte* (GLAPIENTRY *)(GLenum name))GL_GetProcAddress("glGetString")) == NULL)
1018 Con_Print("Required OpenGL function glGetString not found\n");
1022 // Knghtbrd: should do platform-specific extension string function here
1024 vid_isfullscreen = false;
1025 if (mode->fullscreen) {
1026 flags |= SDL_FULLSCREEN;
1027 vid_isfullscreen = true;
1029 //flags |= SDL_HWSURFACE;
1031 SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1);
1032 if (mode->bitsperpixel >= 32)
1034 SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 8);
1035 SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 8);
1036 SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 8);
1037 SDL_GL_SetAttribute (SDL_GL_ALPHA_SIZE, 8);
1038 SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 24);
1039 SDL_GL_SetAttribute (SDL_GL_STENCIL_SIZE, 8);
1043 SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 5);
1044 SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 5);
1045 SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 5);
1046 SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 16);
1048 if (mode->stereobuffer)
1049 SDL_GL_SetAttribute (SDL_GL_STEREO, 1);
1050 if (vid_vsync.integer)
1051 SDL_GL_SetAttribute (SDL_GL_SWAP_CONTROL, 1);
1053 SDL_GL_SetAttribute (SDL_GL_SWAP_CONTROL, 0);
1054 if (mode->samples > 1)
1056 SDL_GL_SetAttribute (SDL_GL_MULTISAMPLEBUFFERS, 1);
1057 SDL_GL_SetAttribute (SDL_GL_MULTISAMPLESAMPLES, mode->samples);
1060 video_bpp = mode->bitsperpixel;
1061 video_flags = flags;
1063 screen = SDL_SetVideoMode(mode->width, mode->height, mode->bitsperpixel, flags);
1068 Con_Printf("Failed to set video mode to %ix%i: %s\n", mode->width, mode->height, SDL_GetError());
1073 vid_softsurface = NULL;
1074 vid.softpixels = NULL;
1078 // set up an event filter to ask confirmation on close button in WIN32
1079 SDL_SetEventFilter( (SDL_EventFilter) Sys_EventFilter );
1081 SDL_EnableUNICODE( SDL_ENABLE );
1082 // enable key repeat since everyone expects it
1083 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
1085 gl_platform = "SDL";
1086 gl_platformextensions = "";
1090 vid_numjoysticks = SDL_NumJoysticks();
1091 vid_numjoysticks = bound(0, vid_numjoysticks, MAX_JOYSTICKS);
1092 Cvar_SetValueQuick(&joy_detected, vid_numjoysticks);
1093 Con_Printf("%d SDL joystick(s) found:\n", vid_numjoysticks);
1094 memset(vid_joysticks, 0, sizeof(vid_joysticks));
1095 for (i = 0;i < vid_numjoysticks;i++)
1098 joy = vid_joysticks[i] = SDL_JoystickOpen(i);
1101 Con_Printf("joystick #%i: open failed: %s\n", i, SDL_GetError());
1104 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));
1108 vid_activewindow = false;
1109 vid_usingmouse = false;
1110 vid_usinghidecursor = false;
1112 SDL_WM_GrabInput(SDL_GRAB_OFF);
1116 extern cvar_t gl_info_extensions;
1117 extern cvar_t gl_info_vendor;
1118 extern cvar_t gl_info_renderer;
1119 extern cvar_t gl_info_version;
1120 extern cvar_t gl_info_platform;
1121 extern cvar_t gl_info_driver;
1123 qboolean VID_InitModeSoft(viddef_mode_t *mode)
1126 int flags = SDL_HWSURFACE;
1128 win_half_width = mode->width>>1;
1129 win_half_height = mode->height>>1;
1131 if(vid_resizable.integer)
1132 flags |= SDL_RESIZABLE;
1134 VID_OutputVersion();
1136 vid_isfullscreen = false;
1137 if (mode->fullscreen) {
1138 flags |= SDL_FULLSCREEN;
1139 vid_isfullscreen = true;
1142 video_bpp = mode->bitsperpixel;
1143 video_flags = flags;
1145 screen = SDL_SetVideoMode(mode->width, mode->height, mode->bitsperpixel, flags);
1150 Con_Printf("Failed to set video mode to %ix%i: %s\n", mode->width, mode->height, SDL_GetError());
1155 // create a framebuffer using our specific color format, we let the SDL blit function convert it in VID_Finish
1156 vid_softsurface = SDL_CreateRGBSurface(SDL_SWSURFACE, mode->width, mode->height, 32, 0x00FF0000, 0x0000FF00, 0x00000000FF, 0xFF000000);
1157 if (vid_softsurface == NULL)
1159 Con_Printf("Failed to setup software rasterizer framebuffer %ix%ix32bpp: %s\n", mode->width, mode->height, SDL_GetError());
1163 SDL_SetAlpha(vid_softsurface, 0, 255);
1165 vid.softpixels = vid_softsurface->pixels;
1166 vid.softdepthpixels = calloc(1, mode->width * mode->height * 4);
1167 DPSOFTRAST_Init(mode->width, mode->height, vid_softsurface->pixels, vid.softdepthpixels);
1171 // set up an event filter to ask confirmation on close button in WIN32
1172 SDL_SetEventFilter( (SDL_EventFilter) Sys_EventFilter );
1174 SDL_EnableUNICODE( SDL_ENABLE );
1175 // enable key repeat since everyone expects it
1176 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
1178 gl_platform = "SDLSoft";
1179 gl_platformextensions = "";
1181 gl_renderer = "DarkPlaces-Soft";
1182 gl_vendor = "Forest Hale";
1186 // clear the extension flags
1187 memset(&vid.support, 0, sizeof(vid.support));
1188 Cvar_SetQuick(&gl_info_extensions, "");
1190 vid.forcevbo = false;
1191 vid.support.arb_depth_texture = true;
1192 vid.support.arb_draw_buffers = true;
1193 vid.support.arb_occlusion_query = true;
1194 vid.support.arb_shadow = true;
1195 vid.support.arb_texture_compression = true;
1196 vid.support.arb_texture_cube_map = true;
1197 vid.support.arb_texture_non_power_of_two = false;
1198 vid.support.arb_vertex_buffer_object = true;
1199 vid.support.ext_blend_subtract = true;
1200 vid.support.ext_draw_range_elements = true;
1201 vid.support.ext_framebuffer_object = true;
1202 vid.support.ext_texture_3d = true;
1203 vid.support.ext_texture_compression_s3tc = true;
1204 vid.support.ext_texture_filter_anisotropic = true;
1205 vid.support.ati_separate_stencil = true;
1207 vid.maxtexturesize_2d = 16384;
1208 vid.maxtexturesize_3d = 512;
1209 vid.maxtexturesize_cubemap = 16384;
1211 vid.teximageunits = 32;
1212 vid.texarrayunits = 8;
1213 vid.max_anisotropy = 1;
1214 vid.maxdrawbuffers = 4;
1216 vid.texunits = bound(4, vid.texunits, MAX_TEXTUREUNITS);
1217 vid.teximageunits = bound(16, vid.teximageunits, MAX_TEXTUREUNITS);
1218 vid.texarrayunits = bound(8, vid.texarrayunits, MAX_TEXTUREUNITS);
1219 Con_DPrintf("Using DarkPlaces Software Rasterizer rendering path\n");
1220 vid.renderpath = RENDERPATH_SOFT;
1221 vid.useinterleavedarrays = false;
1223 Cvar_SetQuick(&gl_info_vendor, gl_vendor);
1224 Cvar_SetQuick(&gl_info_renderer, gl_renderer);
1225 Cvar_SetQuick(&gl_info_version, gl_version);
1226 Cvar_SetQuick(&gl_info_platform, gl_platform ? gl_platform : "");
1227 Cvar_SetQuick(&gl_info_driver, gl_driver);
1229 // LordHavoc: report supported extensions
1230 Con_DPrintf("\nQuakeC extensions for server and client: %s\nQuakeC extensions for menu: %s\n", vm_sv_extensions, vm_m_extensions );
1232 // clear to black (loading plaque will be seen over this)
1233 GL_Clear(GL_COLOR_BUFFER_BIT, NULL, 1.0f, 128);
1235 vid_numjoysticks = SDL_NumJoysticks();
1236 vid_numjoysticks = bound(0, vid_numjoysticks, MAX_JOYSTICKS);
1237 Cvar_SetValueQuick(&joy_detected, vid_numjoysticks);
1238 Con_Printf("%d SDL joystick(s) found:\n", vid_numjoysticks);
1239 memset(vid_joysticks, 0, sizeof(vid_joysticks));
1240 for (i = 0;i < vid_numjoysticks;i++)
1243 joy = vid_joysticks[i] = SDL_JoystickOpen(i);
1246 Con_Printf("joystick #%i: open failed: %s\n", i, SDL_GetError());
1249 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));
1253 vid_activewindow = false;
1254 vid_usingmouse = false;
1255 vid_usinghidecursor = false;
1257 SDL_WM_GrabInput(SDL_GRAB_OFF);
1261 qboolean VID_InitMode(viddef_mode_t *mode)
1263 if (vid_soft.integer)
1264 return VID_InitModeSoft(mode);
1266 return VID_InitModeGL(mode);
1269 void VID_Shutdown (void)
1271 VID_SetMouse(false, false, false);
1272 VID_RestoreSystemGamma();
1274 if (vid_softsurface)
1275 SDL_FreeSurface(vid_softsurface);
1276 vid_softsurface = NULL;
1277 vid.softpixels = NULL;
1278 if (vid.softdepthpixels)
1279 free(vid.softdepthpixels);
1280 vid.softdepthpixels = NULL;
1282 SDL_QuitSubSystem(SDL_INIT_VIDEO);
1287 gl_platformextensions = "";
1290 int VID_SetGamma (unsigned short *ramps, int rampsize)
1292 return !SDL_SetGammaRamp (ramps, ramps + rampsize, ramps + rampsize*2);
1295 int VID_GetGamma (unsigned short *ramps, int rampsize)
1297 return !SDL_GetGammaRamp (ramps, ramps + rampsize, ramps + rampsize*2);
1300 void VID_Finish (void)
1304 //react on appstate changes
1305 appstate = SDL_GetAppState();
1307 vid_hidden = !(appstate & SDL_APPACTIVE);
1309 if( vid_hidden || !( appstate & SDL_APPMOUSEFOCUS ) || !( appstate & SDL_APPINPUTFOCUS ) )
1310 vid_activewindow = false;
1312 vid_activewindow = true;
1314 VID_UpdateGamma(false, 256);
1318 switch(vid.renderpath)
1320 case RENDERPATH_GL11:
1321 case RENDERPATH_GL13:
1322 case RENDERPATH_GL20:
1323 case RENDERPATH_CGGL:
1325 if (r_speeds.integer == 2 || gl_finish.integer)
1327 qglFinish();CHECKGLERROR
1329 SDL_GL_SwapBuffers();
1331 case RENDERPATH_SOFT:
1332 SDL_BlitSurface(vid_softsurface, NULL, screen, NULL);
1335 case RENDERPATH_D3D9:
1336 case RENDERPATH_D3D10:
1337 case RENDERPATH_D3D11:
1343 size_t VID_ListModes(vid_mode_t *modes, size_t maxcount)
1346 SDL_Rect **vidmodes;
1347 int bpp = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
1350 for(vidmodes = SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_HWSURFACE); *vidmodes; ++vidmodes)
1354 modes[k].width = (*vidmodes)->w;
1355 modes[k].height = (*vidmodes)->h;
1357 modes[k].refreshrate = 60; // no support for refresh rate in SDL
1358 modes[k].pixelheight_num = 1;
1359 modes[k].pixelheight_denom = 1; // SDL does not provide this