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>
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)
37 io_connect_t iohandle = MACH_PORT_NULL;
39 io_service_t iohidsystem = MACH_PORT_NULL;
40 mach_port_t masterport;
42 status = IOMasterPort(MACH_PORT_NULL, &masterport);
43 if(status != KERN_SUCCESS)
46 iohidsystem = IORegistryEntryFromPath(masterport, kIOServicePlane ":/IOResources/IOHIDSystem");
50 status = IOServiceOpen(iohidsystem, mach_task_self(), kIOHIDParamConnectType, &iohandle);
51 IOObjectRelease(iohidsystem);
61 // Tell startup code that we have a client
62 int cl_available = true;
64 qboolean vid_supportrefreshrate = false;
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"};
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];
96 static int win_half_width = 50;
97 static int win_half_height = 50;
98 static int video_bpp, video_flags;
100 static SDL_Surface *screen;
102 // joystick axes state
103 #define MAX_JOYSTICK_AXES 16
110 static joy_axiscache_t joy_axescache[MAX_JOYSTICK_AXES];
112 /////////////////////////
115 //TODO: Add joystick support
116 //TODO: Add error checking
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[] =
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,
130 0, //SDLK_CLEAR = 12,
131 K_ENTER, //SDLK_RETURN = 13,
133 K_PAUSE, //SDLK_PAUSE = 19,
135 K_ESCAPE, //SDLK_ESCAPE = 27,
137 K_SPACE, //SDLK_SPACE = 32,
138 '!', //SDLK_EXCLAIM = 33,
139 '"', //SDLK_QUOTEDBL = 34,
140 '#', //SDLK_HASH = 35,
141 '$', //SDLK_DOLLAR = 36,
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,
163 ':', //SDLK_COLON = 58,
164 ';', //SDLK_SEMICOLON = 59,
165 '<', //SDLK_LESS = 60,
166 '=', //SDLK_EQUALS = 61,
167 '>', //SDLK_GREATER = 62,
168 '?', //SDLK_QUESTION = 63,
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,
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,
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 */
276 static int MapKey( unsigned int sdlkey )
278 if( sdlkey > sizeof(tbl_sdltoquake)/ sizeof(int) )
280 return tbl_sdltoquake[ sdlkey ];
283 void VID_SetMouse(qboolean fullscreengrab, qboolean relative, qboolean hidecursor)
287 if(vid_usingmouse && (vid_usingnoaccel != !!apple_mouse_noaccel.integer))
288 VID_SetMouse(false, false, false); // ungrab first!
290 if (vid_usingmouse != relative)
292 vid_usingmouse = relative;
293 cl_ignoremousemoves = 2;
294 SDL_WM_GrabInput( relative ? SDL_GRAB_ON : SDL_GRAB_OFF );
298 // Save the status of mouse acceleration
299 originalMouseSpeed = -1.0; // in case of error
300 if(apple_mouse_noaccel.integer)
302 io_connect_t mouseDev = IN_GetIOHandle();
305 if(IOHIDGetAccelerationWithKey(mouseDev, CFSTR(kIOHIDMouseAccelerationType), &originalMouseSpeed) == kIOReturnSuccess)
307 Con_DPrintf("previous mouse acceleration: %f\n", originalMouseSpeed);
308 if(IOHIDSetAccelerationWithKey(mouseDev, CFSTR(kIOHIDMouseAccelerationType), -1.0) != kIOReturnSuccess)
310 Con_Print("Could not disable mouse acceleration (failed at IOHIDSetAccelerationWithKey).\n");
311 Cvar_SetValueQuick(&apple_mouse_noaccel, 0);
316 Con_Print("Could not disable mouse acceleration (failed at IOHIDGetAccelerationWithKey).\n");
317 Cvar_SetValueQuick(&apple_mouse_noaccel, 0);
319 IOServiceClose(mouseDev);
323 Con_Print("Could not disable mouse acceleration (failed at IO_GetIOHandle).\n");
324 Cvar_SetValueQuick(&apple_mouse_noaccel, 0);
328 vid_usingnoaccel = !!apple_mouse_noaccel.integer;
332 if(originalMouseSpeed != -1.0)
334 io_connect_t mouseDev = IN_GetIOHandle();
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);
343 Con_Print("Could not re-enable mouse acceleration (failed at IO_GetIOHandle).\n");
348 if (vid_usinghidecursor != hidecursor)
350 vid_usinghidecursor = hidecursor;
351 SDL_ShowCursor( hidecursor ? SDL_DISABLE : SDL_ENABLE);
355 static double IN_JoystickGetAxis(SDL_Joystick *joy, int axis, double sensitivity, double deadzone)
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;
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
374 static void IN_JoystickKeyeventForAxis(SDL_Joystick *joy, int axis, int key_pos, int key_neg)
378 if (axis < 0 || axis >= SDL_JoystickNumAxes(joy))
379 return; // no such axis on this joystick
381 joytime = Sys_DoubleTime();
382 // no key event, continuous keydown event
383 if (joy_axescache[axis].move == joy_axescache[axis].oldmove)
385 if (joy_axescache[axis].move != 0 && joytime > joy_axescache[axis].keytime)
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;
393 // generate key up event
394 if (joy_axescache[axis].oldmove)
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);
399 // generate key down event
400 if (joy_axescache[axis].move)
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;
408 static qboolean IN_JoystickBlockDoubledKeyEvents(int keycode)
410 if (!joy_axiskeyevents.integer)
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)
416 SDL_Joystick *joy = vid_joysticks[joy_index.integer];
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)
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)
429 /////////////////////
436 static int old_x = 0, old_y = 0;
437 static int stuck = 0;
438 int x, y, numaxes, numballs;
442 if(vid_stick_mouse.integer)
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
447 // we need 2 frames to initialize the center position
450 SDL_WarpMouse(win_half_width, win_half_height);
451 SDL_GetMouseState(&x, &y);
452 SDL_GetRelativeMouseState(&x, &y);
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);
464 SDL_GetRelativeMouseState( &x, &y );
470 SDL_GetMouseState(&x, &y);
471 in_windowmouse_x = x;
472 in_windowmouse_y = y;
474 if (vid_numjoysticks && joy_enable.integer && joy_index.integer >= 0 && joy_index.integer < vid_numjoysticks)
476 SDL_Joystick *joy = vid_joysticks[joy_index.integer];
478 // balls convert to mousemove
479 numballs = SDL_JoystickNumBalls(joy);
480 for (j = 0;j < numballs;j++)
482 SDL_JoystickGetBall(joy, j, &x, &y);
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;
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++)
499 joy_axescache[j].oldmove = joy_axescache[j].move;
500 joy_axescache[j].move = IN_JoystickGetAxis(joy, j, 1, 0.01);
504 if (joy_axiskeyevents.integer)
506 IN_JoystickKeyeventForAxis(joy, joy_axisforward.integer, K_DOWNARROW, K_UPARROW);
507 IN_JoystickKeyeventForAxis(joy, joy_axisside.integer, K_RIGHTARROW, K_LEFTARROW);
512 /////////////////////
516 static int Sys_EventFilter( SDL_Event *event )
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)
522 if (MessageBox( NULL, "Are you sure you want to quit?", "Confirm Exit", MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION ) == IDNO)
530 static qboolean sdl_needs_restart;
531 static void sdl_start(void)
534 static void sdl_shutdown(void)
536 sdl_needs_restart = false;
538 static void sdl_newmap(void)
543 static keynum_t buttonremap[18] =
565 void Sys_SendKeyEvents( void )
567 static qboolean sound_active = true;
571 while( SDL_PollEvent( &event ) )
572 switch( event.type ) {
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));
582 case SDL_ACTIVEEVENT:
583 if( event.active.state & SDL_APPACTIVE )
585 if( event.active.gain )
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 );
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) );
603 case SDL_VIDEORESIZE:
604 if(vid_resizable.integer < 2)
606 vid.width = event.resize.w;
607 vid.height = event.resize.h;
608 SDL_SetVideoMode(vid.width, vid.height, video_bpp, video_flags);
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)
614 Cbuf_AddText("\nr_restart\n");
615 sdl_needs_restart = true;
622 // enable/disable sound on focus gain/loss
623 if ((!vid_hidden && vid_activewindow) || !snd_mutewhenidle.integer)
636 sound_active = false;
645 void *GL_GetProcAddress(const char *name)
648 p = SDL_GL_GetProcAddress(name);
652 static int Sys_EventFilter( SDL_Event *event );
653 static qboolean vid_sdl_initjoysticksystem = false;
658 Cvar_RegisterVariable(&apple_mouse_noaccel);
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);
684 R_RegisterModule("SDL", sdl_start, sdl_shutdown, sdl_newmap, NULL, NULL);
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;
695 // set the icon (we dont use SDL here since it would be too much a PITA)
697 #include "resource.h"
698 #include <SDL_syswm.h>
699 static void VID_SetCaption(void)
705 SDL_WM_SetCaption( gamename, NULL );
707 // get the HWND handle
708 SDL_VERSION( &info.version );
709 if( !SDL_GetWMInfo( &info ) )
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
718 #define GCLP_HICON GCL_HICON
721 #define LONG_PTR LONG
724 SetClassLongPtr( info.window, GCLP_HICON, (LONG_PTR)icon );
726 static void VID_SetIcon_Pre(void)
729 static void VID_SetIcon_Post(void)
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)
740 * Somewhat restricted XPM reader. Only supports XPMs saved by GIMP 2.4 at
741 * default settings with less than 91 colors and transparency.
744 int width, height, colors, isize, i, j;
746 static SDL_Color palette[256];
747 unsigned short palenc[256]; // store color id by char
750 const SDL_version *version;
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
756 if(version->major >= 2 || (version->major == 1 && version->minor >= 3))
758 data = (char *) loadimagepixelsbgra("darkplaces-icon", false, false, false, NULL);
761 unsigned int red = 0x00FF0000;
762 unsigned int green = 0x0000FF00;
763 unsigned int blue = 0x000000FF;
764 unsigned int alpha = 0xFF000000;
766 height = image_height;
768 // reallocate with malloc, as this is in tempmempool (do not want)
770 data = malloc(width * height * 4);
771 memcpy(data, xpm, width * height * 4);
775 icon = SDL_CreateRGBSurface(SDL_SRCALPHA, width, height, 32, LittleLong(red), LittleLong(green), LittleLong(blue), LittleLong(alpha));
778 Con_Printf( "Failed to create surface for the window Icon!\n"
779 "%s\n", SDL_GetError());
788 // we only get here if non-XPM icon was missing, or SDL version is not
789 // sufficient for transparent non-XPM icons
792 xpm = (char *) FS_LoadFile("darkplaces-icon.xpm", tempmempool, false, NULL);
795 idata = XPM_DecodeString(xpm);
803 if(sscanf(data, "%i %i %i %i", &width, &height, &colors, &isize) != 4)
805 // NOTE: Only 1-char colornames are supported
806 Con_Printf("Sorry, but this does not even look similar to an XPM.\n");
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);
817 for(i = 0; i < colors; ++i)
819 unsigned int r, g, b;
822 if(sscanf(idata[i+1], "%c c #%02x%02x%02x", &idx, &r, &g, &b) != 4)
825 if(sscanf(idata[i+1], "%c c Non%1[e]", &idx, foo) != 2) // I take the DailyWTF credit for this. --div0
827 Con_Printf("This XPM's palette looks odd. Can't continue.\n");
832 palette[i].r = 255; // color key
835 thenone = i; // weeeee
840 palette[i].r = r - (r == 255 && g == 0 && b == 255); // change 255/0/255 pink to 254/0/255 for color key
845 palenc[(unsigned char) idx] = i;
848 // allocate the image data
849 data = (char*) malloc(width*height);
851 for(j = 0; j < height; ++j)
853 for(i = 0; i < width; ++i)
855 // casting to the safest possible datatypes ^^
856 data[j * width + i] = palenc[((unsigned char*)idata[colors+j+1])[i]];
862 // SDL_FreeSurface should free the data too
863 // but for completeness' sake...
864 if(icon->flags & SDL_PREALLOC)
867 icon->pixels = NULL; // safety
869 SDL_FreeSurface(icon);
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
877 Con_Printf( "Failed to create surface for the window Icon!\n"
878 "%s\n", SDL_GetError());
884 SDL_SetPalette(icon, SDL_PHYSPAL|SDL_LOGPAL, palette, 0, colors);
885 SDL_SetColorKey(icon, SDL_SRCCOLORKEY, thenone);
888 SDL_WM_SetIcon(icon, NULL);
890 static void VID_SetIcon_Post(void)
892 #if SDL_VIDEO_DRIVER_X11 && !SDL_VIDEO_DRIVER_QUARTZ
895 const SDL_version *version;
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
901 if(!(version->major >= 2 || (version->major == 1 && version->minor >= 3)))
903 // in this case, we did not set the good icon yet
905 SDL_VERSION(&info.version);
906 if(SDL_GetWMInfo(&info) == 1 && info.subsystem == SDL_SYSWM_X11)
908 data = (char *) loadimagepixelsbgra("darkplaces-icon", false, false, false, NULL);
911 // use _NET_WM_ICON too
912 static long netwm_icon[MAX_NETWM_ICON];
918 if(pos + 2 * image_width * image_height < MAX_NETWM_ICON)
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]);
928 Con_Printf("Skipping NETWM icon #%d because there is no space left\n", i);
932 data = (char *) loadimagepixelsbgra(va("darkplaces-icon%d", i), false, false, false, NULL);
935 info.info.x11.lock_func();
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);
940 info.info.x11.unlock_func();
948 static void VID_SetCaption(void)
950 SDL_WM_SetCaption( gamename, NULL );
954 static void VID_OutputVersion(void)
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 );
964 qboolean VID_InitMode(viddef_mode_t *mode)
967 static int notfirstvideomode = false;
968 int flags = SDL_OPENGL;
969 const char *drivername;
971 win_half_width = mode->width>>1;
972 win_half_height = mode->height>>1;
974 if(vid_resizable.integer)
975 flags |= SDL_RESIZABLE;
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.
984 if (notfirstvideomode)
985 SDL_SetVideoMode( 0, 0, 0, 0 );
986 notfirstvideomode = true;
988 // SDL usually knows best
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)
997 Con_Printf("Unable to load GL driver \"%s\": %s\n", drivername, SDL_GetError());
1001 if ((qglGetString = (const GLubyte* (GLAPIENTRY *)(GLenum name))GL_GetProcAddress("glGetString")) == NULL)
1004 Con_Print("Required OpenGL function glGetString not found\n");
1008 // Knghtbrd: should do platform-specific extension string function here
1010 vid_isfullscreen = false;
1011 if (mode->fullscreen) {
1012 flags |= SDL_FULLSCREEN;
1013 vid_isfullscreen = true;
1015 //flags |= SDL_HWSURFACE;
1017 SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1);
1018 if (mode->bitsperpixel >= 32)
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);
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);
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);
1039 SDL_GL_SetAttribute (SDL_GL_SWAP_CONTROL, 0);
1040 if (mode->samples > 1)
1042 SDL_GL_SetAttribute (SDL_GL_MULTISAMPLEBUFFERS, 1);
1043 SDL_GL_SetAttribute (SDL_GL_MULTISAMPLESAMPLES, mode->samples);
1046 video_bpp = mode->bitsperpixel;
1047 video_flags = flags;
1049 screen = SDL_SetVideoMode(mode->width, mode->height, mode->bitsperpixel, flags);
1054 Con_Printf("Failed to set video mode to %ix%i: %s\n", mode->width, mode->height, SDL_GetError());
1061 // set up an event filter to ask confirmation on close button in WIN32
1062 SDL_SetEventFilter( (SDL_EventFilter) Sys_EventFilter );
1064 SDL_EnableUNICODE( SDL_ENABLE );
1065 // enable key repeat since everyone expects it
1066 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
1068 gl_platform = "SDL";
1069 gl_platformextensions = "";
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++)
1081 joy = vid_joysticks[i] = SDL_JoystickOpen(i);
1084 Con_Printf("joystick #%i: open failed: %s\n", i, SDL_GetError());
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));
1091 vid_activewindow = false;
1092 vid_usingmouse = false;
1093 vid_usinghidecursor = false;
1095 SDL_WM_GrabInput(SDL_GRAB_OFF);
1099 void VID_Shutdown (void)
1101 VID_SetMouse(false, false, false);
1102 VID_RestoreSystemGamma();
1104 SDL_QuitSubSystem(SDL_INIT_VIDEO);
1109 gl_platformextensions = "";
1112 int VID_SetGamma (unsigned short *ramps, int rampsize)
1114 return !SDL_SetGammaRamp (ramps, ramps + rampsize, ramps + rampsize*2);
1117 int VID_GetGamma (unsigned short *ramps, int rampsize)
1119 return !SDL_GetGammaRamp (ramps, ramps + rampsize, ramps + rampsize*2);
1122 void VID_Finish (void)
1126 //react on appstate changes
1127 appstate = SDL_GetAppState();
1129 vid_hidden = !(appstate & SDL_APPACTIVE);
1131 if( vid_hidden || !( appstate & SDL_APPMOUSEFOCUS ) || !( appstate & SDL_APPINPUTFOCUS ) )
1132 vid_activewindow = false;
1134 vid_activewindow = true;
1136 VID_UpdateGamma(false, 256);
1141 if (r_speeds.integer == 2 || gl_finish.integer)
1143 qglFinish();CHECKGLERROR
1145 SDL_GL_SwapBuffers();
1149 size_t VID_ListModes(vid_mode_t *modes, size_t maxcount)
1152 SDL_Rect **vidmodes;
1153 int bpp = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
1156 for(vidmodes = SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_HWSURFACE); *vidmodes; ++vidmodes)
1160 modes[k].width = (*vidmodes)->w;
1161 modes[k].height = (*vidmodes)->h;
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