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>
31 // Tell startup code that we have a client
32 int cl_available = true;
34 qboolean vid_supportrefreshrate = false;
36 cvar_t joy_detected = {CVAR_READONLY, "joy_detected", "0", "number of joysticks detected by engine"};
37 cvar_t joy_enable = {CVAR_SAVE, "joy_enable", "0", "enables joystick support"};
38 cvar_t joy_index = {0, "joy_index", "0", "selects which joystick to use if you have multiple"};
39 cvar_t joy_axisforward = {0, "joy_axisforward", "1", "which joystick axis to query for forward/backward movement"};
40 cvar_t joy_axisside = {0, "joy_axisside", "0", "which joystick axis to query for right/left movement"};
41 cvar_t joy_axisup = {0, "joy_axisup", "-1", "which joystick axis to query for up/down movement"};
42 cvar_t joy_axispitch = {0, "joy_axispitch", "3", "which joystick axis to query for looking up/down"};
43 cvar_t joy_axisyaw = {0, "joy_axisyaw", "2", "which joystick axis to query for looking right/left"};
44 cvar_t joy_axisroll = {0, "joy_axisroll", "-1", "which joystick axis to query for tilting head right/left"};
45 cvar_t joy_deadzoneforward = {0, "joy_deadzoneforward", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
46 cvar_t joy_deadzoneside = {0, "joy_deadzoneside", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
47 cvar_t joy_deadzoneup = {0, "joy_deadzoneup", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
48 cvar_t joy_deadzonepitch = {0, "joy_deadzonepitch", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
49 cvar_t joy_deadzoneyaw = {0, "joy_deadzoneyaw", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
50 cvar_t joy_deadzoneroll = {0, "joy_deadzoneroll", "0", "deadzone tolerance, suggested values are in the range 0 to 0.01"};
51 cvar_t joy_sensitivityforward = {0, "joy_sensitivityforward", "-1", "movement multiplier"};
52 cvar_t joy_sensitivityside = {0, "joy_sensitivityside", "1", "movement multiplier"};
53 cvar_t joy_sensitivityup = {0, "joy_sensitivityup", "1", "movement multiplier"};
54 cvar_t joy_sensitivitypitch = {0, "joy_sensitivitypitch", "1", "movement multiplier"};
55 cvar_t joy_sensitivityyaw = {0, "joy_sensitivityyaw", "-1", "movement multiplier"};
56 cvar_t joy_sensitivityroll = {0, "joy_sensitivityroll", "1", "movement multiplier"};
57 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"};
59 static qboolean vid_usingmouse = false;
60 static qboolean vid_usinghidecursor = false;
61 static qboolean vid_isfullscreen;
62 static int vid_numjoysticks = 0;
63 #define MAX_JOYSTICKS 8
64 static SDL_Joystick *vid_joysticks[MAX_JOYSTICKS];
66 static int win_half_width = 50;
67 static int win_half_height = 50;
68 static int video_bpp, video_flags;
70 static SDL_Surface *screen;
72 // joystick axes state
73 #define MAX_JOYSTICK_AXES 16
80 static joy_axiscache_t joy_axescache[MAX_JOYSTICK_AXES];
82 /////////////////////////
85 //TODO: Add joystick support
86 //TODO: Add error checking
89 //keysym to quake keysym mapping
90 #define tenoh 0,0,0,0,0, 0,0,0,0,0
91 #define fiftyoh tenoh, tenoh, tenoh, tenoh, tenoh
92 #define hundredoh fiftyoh, fiftyoh
93 static unsigned int tbl_sdltoquake[] =
95 0,0,0,0, //SDLK_UNKNOWN = 0,
96 0,0,0,0, //SDLK_FIRST = 0,
97 K_BACKSPACE, //SDLK_BACKSPACE = 8,
98 K_TAB, //SDLK_TAB = 9,
100 0, //SDLK_CLEAR = 12,
101 K_ENTER, //SDLK_RETURN = 13,
103 K_PAUSE, //SDLK_PAUSE = 19,
105 K_ESCAPE, //SDLK_ESCAPE = 27,
107 K_SPACE, //SDLK_SPACE = 32,
108 '!', //SDLK_EXCLAIM = 33,
109 '"', //SDLK_QUOTEDBL = 34,
110 '#', //SDLK_HASH = 35,
111 '$', //SDLK_DOLLAR = 36,
113 '&', //SDLK_AMPERSAND = 38,
114 '\'', //SDLK_QUOTE = 39,
115 '(', //SDLK_LEFTPAREN = 40,
116 ')', //SDLK_RIGHTPAREN = 41,
117 '*', //SDLK_ASTERISK = 42,
118 '+', //SDLK_PLUS = 43,
119 ',', //SDLK_COMMA = 44,
120 '-', //SDLK_MINUS = 45,
121 '.', //SDLK_PERIOD = 46,
122 '/', //SDLK_SLASH = 47,
133 ':', //SDLK_COLON = 58,
134 ';', //SDLK_SEMICOLON = 59,
135 '<', //SDLK_LESS = 60,
136 '=', //SDLK_EQUALS = 61,
137 '>', //SDLK_GREATER = 62,
138 '?', //SDLK_QUESTION = 63,
140 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,
141 '[', //SDLK_LEFTBRACKET = 91,
142 '\\', //SDLK_BACKSLASH = 92,
143 ']', //SDLK_RIGHTBRACKET = 93,
144 '^', //SDLK_CARET = 94,
145 '_', //SDLK_UNDERSCORE = 95,
146 '`', //SDLK_BACKQUOTE = 96,
174 K_DEL, //SDLK_DELETE = 127,
175 hundredoh /*227*/, tenoh, tenoh, 0,0,0,0,0,0,0,0,
176 K_KP_0, //SDLK_KP0 = 256,
177 K_KP_1, //SDLK_KP1 = 257,
178 K_KP_2, //SDLK_KP2 = 258,
179 K_KP_3, //SDLK_KP3 = 259,
180 K_KP_4, //SDLK_KP4 = 260,
181 K_KP_5, //SDLK_KP5 = 261,
182 K_KP_6, //SDLK_KP6 = 262,
183 K_KP_7, //SDLK_KP7 = 263,
184 K_KP_8, //SDLK_KP8 = 264,
185 K_KP_9, //SDLK_KP9 = 265,
186 K_KP_PERIOD,//SDLK_KP_PERIOD = 266,
187 K_KP_DIVIDE,//SDLK_KP_DIVIDE = 267,
188 K_KP_MULTIPLY,//SDLK_KP_MULTIPLY= 268,
189 K_KP_MINUS, //SDLK_KP_MINUS = 269,
190 K_KP_PLUS, //SDLK_KP_PLUS = 270,
191 K_KP_ENTER, //SDLK_KP_ENTER = 271,
192 K_KP_EQUALS,//SDLK_KP_EQUALS = 272,
193 K_UPARROW, //SDLK_UP = 273,
194 K_DOWNARROW,//SDLK_DOWN = 274,
195 K_RIGHTARROW,//SDLK_RIGHT = 275,
196 K_LEFTARROW,//SDLK_LEFT = 276,
197 K_INS, //SDLK_INSERT = 277,
198 K_HOME, //SDLK_HOME = 278,
199 K_END, //SDLK_END = 279,
200 K_PGUP, //SDLK_PAGEUP = 280,
201 K_PGDN, //SDLK_PAGEDOWN = 281,
202 K_F1, //SDLK_F1 = 282,
203 K_F2, //SDLK_F2 = 283,
204 K_F3, //SDLK_F3 = 284,
205 K_F4, //SDLK_F4 = 285,
206 K_F5, //SDLK_F5 = 286,
207 K_F6, //SDLK_F6 = 287,
208 K_F7, //SDLK_F7 = 288,
209 K_F8, //SDLK_F8 = 289,
210 K_F9, //SDLK_F9 = 290,
211 K_F10, //SDLK_F10 = 291,
212 K_F11, //SDLK_F11 = 292,
213 K_F12, //SDLK_F12 = 293,
218 K_NUMLOCK, //SDLK_NUMLOCK = 300,
219 K_CAPSLOCK, //SDLK_CAPSLOCK = 301,
220 K_SCROLLOCK,//SDLK_SCROLLOCK= 302,
221 K_SHIFT, //SDLK_RSHIFT = 303,
222 K_SHIFT, //SDLK_LSHIFT = 304,
223 K_CTRL, //SDLK_RCTRL = 305,
224 K_CTRL, //SDLK_LCTRL = 306,
225 K_ALT, //SDLK_RALT = 307,
226 K_ALT, //SDLK_LALT = 308,
227 0, //SDLK_RMETA = 309,
228 0, //SDLK_LMETA = 310,
229 0, //SDLK_LSUPER = 311, /* Left "Windows" key */
230 0, //SDLK_RSUPER = 312, /* Right "Windows" key */
231 K_ALT, //SDLK_MODE = 313, /* "Alt Gr" key */
232 0, //SDLK_COMPOSE = 314, /* Multi-key compose key */
233 0, //SDLK_HELP = 315,
234 0, //SDLK_PRINT = 316,
235 0, //SDLK_SYSREQ = 317,
236 K_PAUSE, //SDLK_BREAK = 318,
237 0, //SDLK_MENU = 319,
238 0, //SDLK_POWER = 320, /* Power Macintosh power key */
239 'e', //SDLK_EURO = 321, /* Some european keyboards */
240 0 //SDLK_UNDO = 322, /* Atari keyboard has Undo */
246 static int MapKey( unsigned int sdlkey )
248 if( sdlkey > sizeof(tbl_sdltoquake)/ sizeof(int) )
250 return tbl_sdltoquake[ sdlkey ];
253 void VID_SetMouse(qboolean fullscreengrab, qboolean relative, qboolean hidecursor)
255 if (vid_usingmouse != relative)
257 vid_usingmouse = relative;
258 cl_ignoremousemoves = 2;
259 SDL_WM_GrabInput( relative ? SDL_GRAB_ON : SDL_GRAB_OFF );
261 if (vid_usinghidecursor != hidecursor)
263 vid_usinghidecursor = hidecursor;
264 SDL_ShowCursor( hidecursor ? SDL_DISABLE : SDL_ENABLE);
268 static double IN_JoystickGetAxis(SDL_Joystick *joy, int axis, double sensitivity, double deadzone)
271 if (axis < 0 || axis >= SDL_JoystickNumAxes(joy))
272 return 0; // no such axis on this joystick
273 value = SDL_JoystickGetAxis(joy, axis) * (1.0 / 32767.0);
274 value = bound(-1, value, 1);
275 if (fabs(value) < deadzone)
276 return 0; // within deadzone around center
277 return value * sensitivity;
280 /////////////////////
281 // Joystick axis keyevents
282 // a sort of hack emulating Arrow keys for joystick axises
283 // as some drives dont send such keyevents for them
284 // additionally we should block drivers that do send arrow keyevents to prevent double events
287 static void IN_JoystickKeyeventForAxis(SDL_Joystick *joy, int axis, int key_pos, int key_neg)
291 if (axis < 0 || axis >= SDL_JoystickNumAxes(joy))
292 return; // no such axis on this joystick
294 joytime = Sys_DoubleTime();
295 // no key event, continuous keydown event
296 if (joy_axescache[axis].move == joy_axescache[axis].oldmove)
298 if (joy_axescache[axis].move != 0 && joytime > joy_axescache[axis].keytime)
300 //Con_Printf("joy %s %i %f\n", Key_KeynumToString((joy_axescache[axis].move > 0) ? key_pos : key_neg), 1, cl.time);
301 Key_Event((joy_axescache[axis].move > 0) ? key_pos : key_neg, 0, 1);
302 joy_axescache[axis].keytime = joytime + 0.5 / 20;
306 // generate key up event
307 if (joy_axescache[axis].oldmove)
309 //Con_Printf("joy %s %i %f\n", Key_KeynumToString((joy_axescache[axis].oldmove > 0) ? key_pos : key_neg), 1, cl.time);
310 Key_Event((joy_axescache[axis].oldmove > 0) ? key_pos : key_neg, 0, 0);
312 // generate key down event
313 if (joy_axescache[axis].move)
315 //Con_Printf("joy %s %i %f\n", Key_KeynumToString((joy_axescache[axis].move > 0) ? key_pos : key_neg), 1, cl.time);
316 Key_Event((joy_axescache[axis].move > 0) ? key_pos : key_neg, 0, 1);
317 joy_axescache[axis].keytime = joytime + 0.5;
321 static qboolean IN_JoystickBlockDoubledKeyEvents(int keycode)
323 if (!joy_axiskeyevents.integer)
326 // block keyevent if it's going to be provided by joystick keyevent system
327 if (vid_numjoysticks && joy_enable.integer && joy_index.integer >= 0 && joy_index.integer < vid_numjoysticks)
329 SDL_Joystick *joy = vid_joysticks[joy_index.integer];
331 if (keycode == K_UPARROW || keycode == K_DOWNARROW)
332 if (IN_JoystickGetAxis(joy, joy_axisforward.integer, 1, 0.01) || joy_axescache[joy_axisforward.integer].move || joy_axescache[joy_axisforward.integer].oldmove)
334 if (keycode == K_RIGHTARROW || keycode == K_LEFTARROW)
335 if (IN_JoystickGetAxis(joy, joy_axisside.integer, 1, 0.01) || joy_axescache[joy_axisside.integer].move || joy_axescache[joy_axisside.integer].oldmove)
342 /////////////////////
349 static int old_x = 0, old_y = 0;
350 static int stuck = 0;
351 int x, y, numaxes, numballs;
355 if(vid_stick_mouse.integer)
357 // have the mouse stuck in the middle, example use: prevent expose effect of beryl during the game when not using
358 // window grabbing. --blub
360 // we need 2 frames to initialize the center position
363 SDL_WarpMouse(win_half_width, win_half_height);
364 SDL_GetMouseState(&x, &y);
365 SDL_GetRelativeMouseState(&x, &y);
368 SDL_GetRelativeMouseState(&x, &y);
369 in_mouse_x = x + old_x;
370 in_mouse_y = y + old_y;
371 SDL_GetMouseState(&x, &y);
372 old_x = x - win_half_width;
373 old_y = y - win_half_height;
374 SDL_WarpMouse(win_half_width, win_half_height);
377 SDL_GetRelativeMouseState( &x, &y );
383 SDL_GetMouseState(&x, &y);
384 in_windowmouse_x = x;
385 in_windowmouse_y = y;
387 if (vid_numjoysticks && joy_enable.integer && joy_index.integer >= 0 && joy_index.integer < vid_numjoysticks)
389 SDL_Joystick *joy = vid_joysticks[joy_index.integer];
391 // balls convert to mousemove
392 numballs = SDL_JoystickNumBalls(joy);
393 for (j = 0;j < numballs;j++)
395 SDL_JoystickGetBall(joy, j, &x, &y);
401 cl.cmd.forwardmove += IN_JoystickGetAxis(joy, joy_axisforward.integer, joy_sensitivityforward.value, joy_deadzoneforward.value) * cl_forwardspeed.value;
402 cl.cmd.sidemove += IN_JoystickGetAxis(joy, joy_axisside.integer, joy_sensitivityside.value, joy_deadzoneside.value) * cl_sidespeed.value;
403 cl.cmd.upmove += IN_JoystickGetAxis(joy, joy_axisup.integer, joy_sensitivityup.value, joy_deadzoneup.value) * cl_upspeed.value;
404 cl.viewangles[0] += IN_JoystickGetAxis(joy, joy_axispitch.integer, joy_sensitivitypitch.value, joy_deadzonepitch.value) * cl.realframetime * cl_pitchspeed.value;
405 cl.viewangles[1] += IN_JoystickGetAxis(joy, joy_axisyaw.integer, joy_sensitivityyaw.value, joy_deadzoneyaw.value) * cl.realframetime * cl_yawspeed.value;
406 //cl.viewangles[2] += IN_JoystickGetAxis(joy, joy_axisroll.integer, joy_sensitivityroll.value, joy_deadzoneroll.value) * cl.realframetime * cl_rollspeed.value;
408 // cache state of axes to emulate button events for them
409 numaxes = min(MAX_JOYSTICK_AXES, SDL_JoystickNumAxes(joy));
410 for (j = 0; j < numaxes; j++)
412 joy_axescache[j].oldmove = joy_axescache[j].move;
413 joy_axescache[j].move = IN_JoystickGetAxis(joy, j, 1, 0.01);
417 if (joy_axiskeyevents.integer)
419 IN_JoystickKeyeventForAxis(joy, joy_axisforward.integer, K_DOWNARROW, K_UPARROW);
420 IN_JoystickKeyeventForAxis(joy, joy_axisside.integer, K_RIGHTARROW, K_LEFTARROW);
425 /////////////////////
429 static int Sys_EventFilter( SDL_Event *event )
431 //TODO: Add a quit query in linux, too - though linux user are more likely to know what they do
432 if (event->type == SDL_QUIT)
435 if (MessageBox( NULL, "Are you sure you want to quit?", "Confirm Exit", MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION ) == IDNO)
443 static qboolean sdl_needs_restart;
444 static void sdl_start(void)
447 static void sdl_shutdown(void)
449 sdl_needs_restart = false;
451 static void sdl_newmap(void)
456 static keynum_t buttonremap[18] =
478 void Sys_SendKeyEvents( void )
480 static qboolean sound_active = true;
484 while( SDL_PollEvent( &event ) )
485 switch( event.type ) {
491 keycode = MapKey(event.key.keysym.sym);
492 if (!IN_JoystickBlockDoubledKeyEvents(keycode))
493 Key_Event(keycode, event.key.keysym.unicode, (event.key.state == SDL_PRESSED));
495 case SDL_ACTIVEEVENT:
496 if( event.active.state & SDL_APPACTIVE )
498 if( event.active.gain )
504 case SDL_MOUSEBUTTONDOWN:
505 case SDL_MOUSEBUTTONUP:
506 if (event.button.button <= 18)
507 Key_Event( buttonremap[event.button.button - 1], 0, event.button.state == SDL_PRESSED );
509 case SDL_JOYBUTTONDOWN:
510 if (!joy_enable.integer)
511 break; // ignore down events if joystick has been disabled
512 case SDL_JOYBUTTONUP:
513 if (event.jbutton.button < 48)
514 Key_Event( event.jbutton.button + (event.jbutton.button < 16 ? K_JOY1 : K_AUX1 - 16), 0, (event.jbutton.state == SDL_PRESSED) );
516 case SDL_VIDEORESIZE:
517 if(vid_resizable.integer < 2)
519 vid.width = event.resize.w;
520 vid.height = event.resize.h;
521 SDL_SetVideoMode(vid.width, vid.height, video_bpp, video_flags);
523 // better not call R_Modules_Restart from here directly, as this may wreak havoc...
524 // so, let's better queue it for next frame
525 if(!sdl_needs_restart)
527 Cbuf_AddText("\nr_restart\n");
528 sdl_needs_restart = true;
535 // enable/disable sound on focus gain/loss
536 if ((!vid_hidden && vid_activewindow) || !snd_mutewhenidle.integer)
549 sound_active = false;
558 void *GL_GetProcAddress(const char *name)
561 p = SDL_GL_GetProcAddress(name);
565 static int Sys_EventFilter( SDL_Event *event );
566 static qboolean vid_sdl_initjoysticksystem = false;
570 Cvar_RegisterVariable(&joy_detected);
571 Cvar_RegisterVariable(&joy_enable);
572 Cvar_RegisterVariable(&joy_index);
573 Cvar_RegisterVariable(&joy_axisforward);
574 Cvar_RegisterVariable(&joy_axisside);
575 Cvar_RegisterVariable(&joy_axisup);
576 Cvar_RegisterVariable(&joy_axispitch);
577 Cvar_RegisterVariable(&joy_axisyaw);
578 //Cvar_RegisterVariable(&joy_axisroll);
579 Cvar_RegisterVariable(&joy_deadzoneforward);
580 Cvar_RegisterVariable(&joy_deadzoneside);
581 Cvar_RegisterVariable(&joy_deadzoneup);
582 Cvar_RegisterVariable(&joy_deadzonepitch);
583 Cvar_RegisterVariable(&joy_deadzoneyaw);
584 //Cvar_RegisterVariable(&joy_deadzoneroll);
585 Cvar_RegisterVariable(&joy_sensitivityforward);
586 Cvar_RegisterVariable(&joy_sensitivityside);
587 Cvar_RegisterVariable(&joy_sensitivityup);
588 Cvar_RegisterVariable(&joy_sensitivitypitch);
589 Cvar_RegisterVariable(&joy_sensitivityyaw);
590 //Cvar_RegisterVariable(&joy_sensitivityroll);
591 Cvar_RegisterVariable(&joy_axiskeyevents);
594 R_RegisterModule("SDL", sdl_start, sdl_shutdown, sdl_newmap, NULL, NULL);
597 if (SDL_Init(SDL_INIT_VIDEO) < 0)
598 Sys_Error ("Failed to init SDL video subsystem: %s", SDL_GetError());
599 vid_sdl_initjoysticksystem = SDL_Init(SDL_INIT_JOYSTICK) >= 0;
600 if (vid_sdl_initjoysticksystem)
601 Con_Printf("Failed to init SDL joystick subsystem: %s\n", SDL_GetError());
602 vid_isfullscreen = false;
605 // set the icon (we dont use SDL here since it would be too much a PITA)
607 #include "resource.h"
608 #include <SDL_syswm.h>
609 static void VID_SetCaption(void)
615 SDL_WM_SetCaption( gamename, NULL );
617 // get the HWND handle
618 SDL_VERSION( &info.version );
619 if( !SDL_GetWMInfo( &info ) )
622 icon = LoadIcon( GetModuleHandle( NULL ), MAKEINTRESOURCE( IDI_ICON1 ) );
623 #ifndef _W64 //If Windows 64bit data types don't exist
624 #ifndef SetClassLongPtr
625 #define SetClassLongPtr SetClassLong
628 #define GCLP_HICON GCL_HICON
631 #define LONG_PTR LONG
634 SetClassLongPtr( info.window, GCLP_HICON, (LONG_PTR)icon );
636 static void VID_SetIcon_Pre(void)
639 static void VID_SetIcon_Post(void)
643 // Adding the OS independent XPM version --blub
644 #include "darkplaces.xpm"
645 #include "nexuiz.xpm"
646 static SDL_Surface *icon = NULL;
647 static void VID_SetIcon_Pre(void)
650 * Somewhat restricted XPM reader. Only supports XPMs saved by GIMP 2.4 at
651 * default settings with less than 91 colors and transparency.
654 int width, height, colors, isize, i, j;
656 static SDL_Color palette[256];
657 unsigned short palenc[256]; // store color id by char
660 const SDL_version *version;
662 version = SDL_Linked_Version();
663 // only use non-XPM icon support in SDL v1.3 and higher
664 // SDL v1.2 does not support "smooth" transparency, and thus is better
666 if(version->major >= 2 || (version->major == 1 && version->minor >= 3))
668 data = (char *) loadimagepixelsbgra("darkplaces-icon", false, false, false, NULL);
671 unsigned int red = 0x00FF0000;
672 unsigned int green = 0x0000FF00;
673 unsigned int blue = 0x000000FF;
674 unsigned int alpha = 0xFF000000;
676 height = image_height;
678 // reallocate with malloc, as this is in tempmempool (do not want)
680 data = malloc(width * height * 4);
681 memcpy(data, xpm, width * height * 4);
685 icon = SDL_CreateRGBSurface(SDL_SRCALPHA, width, height, 32, LittleLong(red), LittleLong(green), LittleLong(blue), LittleLong(alpha));
688 Con_Printf( "Failed to create surface for the window Icon!\n"
689 "%s\n", SDL_GetError());
698 // we only get here if non-XPM icon was missing, or SDL version is not
699 // sufficient for transparent non-XPM icons
702 xpm = (char *) FS_LoadFile("darkplaces-icon.xpm", tempmempool, false, NULL);
705 idata = XPM_DecodeString(xpm);
713 if(sscanf(data, "%i %i %i %i", &width, &height, &colors, &isize) != 4)
715 // NOTE: Only 1-char colornames are supported
716 Con_Printf("Sorry, but this does not even look similar to an XPM.\n");
722 // NOTE: Only 1-char colornames are supported
723 Con_Printf("This XPM's palette is either huge or idiotically unoptimized. It's key size is %i\n", isize);
727 for(i = 0; i < colors; ++i)
729 unsigned int r, g, b;
732 if(sscanf(idata[i+1], "%c c #%02x%02x%02x", &idx, &r, &g, &b) != 4)
735 if(sscanf(idata[i+1], "%c c Non%1[e]", &idx, foo) != 2) // I take the DailyWTF credit for this. --div0
737 Con_Printf("This XPM's palette looks odd. Can't continue.\n");
742 palette[i].r = 255; // color key
745 thenone = i; // weeeee
750 palette[i].r = r - (r == 255 && g == 0 && b == 255); // change 255/0/255 pink to 254/0/255 for color key
755 palenc[(unsigned char) idx] = i;
758 // allocate the image data
759 data = (char*) malloc(width*height);
761 for(j = 0; j < height; ++j)
763 for(i = 0; i < width; ++i)
765 // casting to the safest possible datatypes ^^
766 data[j * width + i] = palenc[((unsigned char*)idata[colors+j+1])[i]];
772 // SDL_FreeSurface should free the data too
773 // but for completeness' sake...
774 if(icon->flags & SDL_PREALLOC)
777 icon->pixels = NULL; // safety
779 SDL_FreeSurface(icon);
782 icon = SDL_CreateRGBSurface(SDL_SRCCOLORKEY, width, height, 8, 0,0,0,0);// rmask, gmask, bmask, amask); no mask needed
783 // 8 bit surfaces get an empty palette allocated according to the docs
784 // so it's a palette image for sure :) no endian check necessary for the mask
787 Con_Printf( "Failed to create surface for the window Icon!\n"
788 "%s\n", SDL_GetError());
794 SDL_SetPalette(icon, SDL_PHYSPAL|SDL_LOGPAL, palette, 0, colors);
795 SDL_SetColorKey(icon, SDL_SRCCOLORKEY, thenone);
798 SDL_WM_SetIcon(icon, NULL);
800 static void VID_SetIcon_Post(void)
802 #if SDL_VIDEO_DRIVER_X11 && !SDL_VIDEO_DRIVER_QUARTZ
805 const SDL_version *version;
807 version = SDL_Linked_Version();
808 // only use non-XPM icon support in SDL v1.3 and higher
809 // SDL v1.2 does not support "smooth" transparency, and thus is better
811 if(!(version->major >= 2 || (version->major == 1 && version->minor >= 3)))
813 // in this case, we did not set the good icon yet
815 SDL_VERSION(&info.version);
816 if(SDL_GetWMInfo(&info) == 1 && info.subsystem == SDL_SYSWM_X11)
818 data = (char *) loadimagepixelsbgra("darkplaces-icon", false, false, false, NULL);
821 // use _NET_WM_ICON too
822 static long netwm_icon[MAX_NETWM_ICON];
828 if(pos + 2 * image_width * image_height < MAX_NETWM_ICON)
830 netwm_icon[pos++] = image_width;
831 netwm_icon[pos++] = image_height;
832 for(i = 0; i < image_height; ++i)
833 for(j = 0; j < image_width; ++j)
834 netwm_icon[pos++] = BuffLittleLong((unsigned char *) &data[(i*image_width+j)*4]);
838 Con_Printf("Skipping NETWM icon #%d because there is no space left\n", i);
842 data = (char *) loadimagepixelsbgra(va("darkplaces-icon%d", i), false, false, false, NULL);
845 info.info.x11.lock_func();
847 Atom net_wm_icon = XInternAtom(info.info.x11.display, "_NET_WM_ICON", false);
848 XChangeProperty(info.info.x11.display, info.info.x11.wmwindow, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (const unsigned char *) netwm_icon, pos);
850 info.info.x11.unlock_func();
858 static void VID_SetCaption(void)
860 SDL_WM_SetCaption( gamename, NULL );
864 static void VID_OutputVersion(void)
866 const SDL_version *version;
867 version = SDL_Linked_Version();
868 Con_Printf( "Linked against SDL version %d.%d.%d\n"
869 "Using SDL library version %d.%d.%d\n",
870 SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL,
871 version->major, version->minor, version->patch );
874 qboolean VID_InitMode(viddef_mode_t *mode)
877 static int notfirstvideomode = false;
878 int flags = SDL_OPENGL;
879 const char *drivername;
881 win_half_width = mode->width>>1;
882 win_half_height = mode->height>>1;
884 if(vid_resizable.integer)
885 flags |= SDL_RESIZABLE;
891 We cant switch from one OpenGL video mode to another.
892 Thus we first switch to some stupid 2D mode and then back to OpenGL.
894 if (notfirstvideomode)
895 SDL_SetVideoMode( 0, 0, 0, 0 );
896 notfirstvideomode = true;
898 // SDL usually knows best
901 // 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
902 i = COM_CheckParm("-gl_driver");
903 if (i && i < com_argc - 1)
904 drivername = com_argv[i + 1];
905 if (SDL_GL_LoadLibrary(drivername) < 0)
907 Con_Printf("Unable to load GL driver \"%s\": %s\n", drivername, SDL_GetError());
911 if ((qglGetString = (const GLubyte* (GLAPIENTRY *)(GLenum name))GL_GetProcAddress("glGetString")) == NULL)
914 Con_Print("Required OpenGL function glGetString not found\n");
918 // Knghtbrd: should do platform-specific extension string function here
920 vid_isfullscreen = false;
921 if (mode->fullscreen) {
922 flags |= SDL_FULLSCREEN;
923 vid_isfullscreen = true;
925 //flags |= SDL_HWSURFACE;
927 SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1);
928 if (mode->bitsperpixel >= 32)
930 SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 8);
931 SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 8);
932 SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 8);
933 SDL_GL_SetAttribute (SDL_GL_ALPHA_SIZE, 8);
934 SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 24);
935 SDL_GL_SetAttribute (SDL_GL_STENCIL_SIZE, 8);
939 SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 5);
940 SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 5);
941 SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 5);
942 SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 16);
944 if (mode->stereobuffer)
945 SDL_GL_SetAttribute (SDL_GL_STEREO, 1);
946 if (vid_vsync.integer)
947 SDL_GL_SetAttribute (SDL_GL_SWAP_CONTROL, 1);
949 SDL_GL_SetAttribute (SDL_GL_SWAP_CONTROL, 0);
950 if (mode->samples > 1)
952 SDL_GL_SetAttribute (SDL_GL_MULTISAMPLEBUFFERS, 1);
953 SDL_GL_SetAttribute (SDL_GL_MULTISAMPLESAMPLES, mode->samples);
956 video_bpp = mode->bitsperpixel;
959 screen = SDL_SetVideoMode(mode->width, mode->height, mode->bitsperpixel, flags);
964 Con_Printf("Failed to set video mode to %ix%i: %s\n", mode->width, mode->height, SDL_GetError());
971 // set up an event filter to ask confirmation on close button in WIN32
972 SDL_SetEventFilter( (SDL_EventFilter) Sys_EventFilter );
974 SDL_EnableUNICODE( SDL_ENABLE );
975 // enable key repeat since everyone expects it
976 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
979 gl_platformextensions = "";
983 vid_numjoysticks = SDL_NumJoysticks();
984 vid_numjoysticks = bound(0, vid_numjoysticks, MAX_JOYSTICKS);
985 Cvar_SetValueQuick(&joy_detected, vid_numjoysticks);
986 Con_Printf("%d SDL joystick(s) found:\n", vid_numjoysticks);
987 memset(vid_joysticks, 0, sizeof(vid_joysticks));
988 for (i = 0;i < vid_numjoysticks;i++)
991 joy = vid_joysticks[i] = SDL_JoystickOpen(i);
994 Con_Printf("joystick #%i: open failed: %s\n", i, SDL_GetError());
997 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));
1001 vid_activewindow = false;
1002 vid_usingmouse = false;
1003 vid_usinghidecursor = false;
1005 SDL_WM_GrabInput(SDL_GRAB_OFF);
1009 void VID_Shutdown (void)
1011 VID_SetMouse(false, false, false);
1012 VID_RestoreSystemGamma();
1014 SDL_QuitSubSystem(SDL_INIT_VIDEO);
1019 gl_platformextensions = "";
1022 int VID_SetGamma (unsigned short *ramps, int rampsize)
1024 return !SDL_SetGammaRamp (ramps, ramps + rampsize, ramps + rampsize*2);
1027 int VID_GetGamma (unsigned short *ramps, int rampsize)
1029 return !SDL_GetGammaRamp (ramps, ramps + rampsize, ramps + rampsize*2);
1032 void VID_Finish (void)
1036 //react on appstate changes
1037 appstate = SDL_GetAppState();
1039 vid_hidden = !(appstate & SDL_APPACTIVE);
1041 if( vid_hidden || !( appstate & SDL_APPMOUSEFOCUS ) || !( appstate & SDL_APPINPUTFOCUS ) )
1042 vid_activewindow = false;
1044 vid_activewindow = true;
1046 VID_UpdateGamma(false, 256);
1051 if (r_speeds.integer == 2 || gl_finish.integer)
1053 qglFinish();CHECKGLERROR
1055 SDL_GL_SwapBuffers();
1059 size_t VID_ListModes(vid_mode_t *modes, size_t maxcount)
1062 SDL_Rect **vidmodes;
1063 int bpp = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
1066 for(vidmodes = SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_HWSURFACE); *vidmodes; ++vidmodes)
1070 modes[k].width = (*vidmodes)->w;
1071 modes[k].height = (*vidmodes)->h;
1073 modes[k].refreshrate = 60; // no support for refresh rate in SDL
1074 modes[k].pixelheight_num = 1;
1075 modes[k].pixelheight_denom = 1; // SDL does not provide this