Add weapon cycling bindings for mouse and joystick buttons. Add weapon cycling bindi...
[chocolate-doom.git] / src / i_video.c
blob91b670b8a4314976027a11a50f61daf3a6c4421b
1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // Copyright(C) 1993-1996 Id Software, Inc.
5 // Copyright(C) 2005 Simon Howard
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 // 02111-1307, USA.
22 // DESCRIPTION:
23 // DOOM graphics stuff for SDL.
25 //-----------------------------------------------------------------------------
28 #include "SDL.h"
29 #include <stdlib.h>
30 #include <ctype.h>
31 #include <math.h>
33 #include "icon.c"
35 #include "config.h"
36 #include "deh_main.h"
37 #include "doomdef.h"
38 #include "doomstat.h"
39 #include "d_main.h"
40 #include "i_joystick.h"
41 #include "i_system.h"
42 #include "i_swap.h"
43 #include "i_timer.h"
44 #include "i_video.h"
45 #include "i_scale.h"
46 #include "m_argv.h"
47 #include "s_sound.h"
48 #include "sounds.h"
49 #include "v_video.h"
50 #include "w_wad.h"
51 #include "z_zone.h"
53 #define LOADING_DISK_W 16
54 #define LOADING_DISK_H 16
56 // Non aspect ratio-corrected modes (direct multiples of 320x200)
58 static screen_mode_t *screen_modes[] = {
59 &mode_scale_1x,
60 &mode_scale_2x,
61 &mode_scale_3x,
62 &mode_scale_4x,
63 &mode_scale_5x,
66 // Aspect ratio corrected modes (4:3 ratio)
68 static screen_mode_t *screen_modes_corrected[] = {
70 // Vertically stretched modes (320x200 -> 320x240 and multiples)
72 &mode_stretch_1x,
73 &mode_stretch_2x,
74 &mode_stretch_3x,
75 &mode_stretch_4x,
76 &mode_stretch_5x,
78 // Horizontally squashed modes (320x200 -> 256x200 and multiples)
80 &mode_squash_1x,
81 &mode_squash_2x,
82 &mode_squash_3x,
83 &mode_squash_4x,
84 &mode_squash_5x,
87 extern void M_QuitDOOM();
88 extern boolean advancedemo;
90 // SDL video driver name
92 char *video_driver = "";
94 static SDL_Surface *screen;
96 // palette
98 static SDL_Color palette[256];
99 static boolean palette_to_set;
101 // display has been set up?
103 static boolean initialized = false;
105 // disable mouse?
107 static boolean nomouse = false;
108 extern int usemouse;
110 // if true, screens[0] is screen->pixel
112 static boolean native_surface;
114 // Screen width and height, from configuration file.
116 int screen_width = SCREENWIDTH;
117 int screen_height = SCREENHEIGHT;
119 // Automatically adjust video settings if the selected mode is
120 // not a valid video mode.
122 int autoadjust_video_settings = 1;
124 // Run in full screen mode? (int type for config code)
126 int fullscreen = true;
128 // Aspect ratio correction mode
130 int aspect_ratio_correct = true;
132 // Time to wait for the screen to settle on startup before starting the
133 // game (ms)
135 int startup_delay = 1000;
137 // Grab the mouse? (int type for config code)
139 int grabmouse = true;
141 // Flag indicating whether the screen is currently visible:
142 // when the screen isnt visible, don't render the screen
144 boolean screenvisible;
146 // disk image data and background overwritten by the disk to be
147 // restored by EndRead
149 static byte *disk_image = NULL;
150 static byte *saved_background;
151 static boolean window_focused;
153 // Empty mouse cursor
155 static SDL_Cursor *cursors[2];
157 // The screen mode and scale functions being used
159 static screen_mode_t *screen_mode;
161 // Window resize state.
163 static boolean need_resize = false;
164 static unsigned int resize_w, resize_h;
165 static unsigned int last_resize_time;
167 // If true, keyboard mapping is ignored, like in Vanilla Doom.
168 // The sensible thing to do is to disable this if you have a non-US
169 // keyboard.
171 int vanilla_keyboard_mapping = true;
173 // Mouse acceleration
175 // This emulates some of the behavior of DOS mouse drivers by increasing
176 // the speed when the mouse is moved fast.
178 // The mouse input values are input directly to the game, but when
179 // the values exceed the value of mouse_threshold, they are multiplied
180 // by mouse_acceleration to increase the speed.
182 float mouse_acceleration = 2.0;
183 int mouse_threshold = 10;
185 static void ApplyWindowResize(unsigned int w, unsigned int h);
187 static boolean MouseShouldBeGrabbed()
189 // never grab the mouse when in screensaver mode
191 if (screensaver_mode)
192 return false;
194 // if the window doesn't have focus, never grab it
196 if (!window_focused)
197 return false;
199 // always grab the mouse when full screen (dont want to
200 // see the mouse pointer)
202 if (fullscreen)
203 return true;
205 #ifdef _WIN32_WCE
207 // On Windows CE, always grab input. This is because hardware
208 // button events are only acquired by SDL when the input is grabbed.
209 // Almost all Windows CE devices should have touch screens anyway,
210 // so this shouldn't affect mouse grabbing behavior.
212 return true;
214 #else
216 // Don't grab the mouse if mouse input is disabled
218 if (!usemouse || nomouse)
219 return false;
221 // Drone players don't need mouse focus
223 if (drone)
224 return false;
226 // if we specify not to grab the mouse, never grab
228 if (!grabmouse)
229 return false;
231 // when menu is active or game is paused, release the mouse
233 if (menuactive || paused)
234 return false;
236 // only grab mouse when playing levels (but not demos)
238 return (gamestate == GS_LEVEL) && !demoplayback && !advancedemo;
240 #endif /* #ifndef _WIN32_WCE */
243 // Update the value of window_focused when we get a focus event
245 // We try to make ourselves be well-behaved: the grab on the mouse
246 // is removed if we lose focus (such as a popup window appearing),
247 // and we dont move the mouse around if we aren't focused either.
249 static void UpdateFocus(void)
251 Uint8 state;
253 state = SDL_GetAppState();
255 // We should have input (keyboard) focus and be visible
256 // (not minimized)
258 window_focused = (state & SDL_APPINPUTFOCUS) && (state & SDL_APPACTIVE);
260 // Should the screen be grabbed?
262 screenvisible = (state & SDL_APPACTIVE) != 0;
265 static void LoadDiskImage(void)
267 patch_t *disk;
268 char *disk_name;
269 int y;
270 int xoffset = SCREENWIDTH - LOADING_DISK_W;
271 int yoffset = SCREENHEIGHT - LOADING_DISK_H;
272 char buf[20];
274 SDL_VideoDriverName(buf, 15);
276 if (!strcmp(buf, "Quartz"))
278 // MacOS Quartz gives us pageflipped graphics that screw up the
279 // display when we use the loading disk. Disable it.
280 // This is a gross hack.
282 return;
285 if (M_CheckParm("-cdrom") > 0)
286 disk_name = DEH_String("STCDROM");
287 else
288 disk_name = DEH_String("STDISK");
290 disk = W_CacheLumpName(disk_name, PU_STATIC);
292 // Draw the disk to the screen:
294 V_DrawPatch(SCREENWIDTH - LOADING_DISK_W,
295 SCREENHEIGHT - LOADING_DISK_H,
296 0, disk);
298 disk_image = Z_Malloc(LOADING_DISK_W * LOADING_DISK_H, PU_STATIC, NULL);
299 saved_background = Z_Malloc(LOADING_DISK_W * LOADING_DISK_H, PU_STATIC, NULL);
301 for (y=0; y<LOADING_DISK_H; ++y)
303 memcpy(disk_image + LOADING_DISK_W * y,
304 screens[0] + SCREENWIDTH * (y + yoffset) + xoffset,
305 LOADING_DISK_W);
308 W_ReleaseLumpName(disk_name);
312 // Translates the SDL key
315 static int TranslateKey(SDL_keysym *sym)
317 switch(sym->sym)
319 case SDLK_LEFT: return KEY_LEFTARROW;
320 case SDLK_RIGHT: return KEY_RIGHTARROW;
321 case SDLK_DOWN: return KEY_DOWNARROW;
322 case SDLK_UP: return KEY_UPARROW;
323 case SDLK_ESCAPE: return KEY_ESCAPE;
324 case SDLK_RETURN: return KEY_ENTER;
325 case SDLK_TAB: return KEY_TAB;
326 case SDLK_F1: return KEY_F1;
327 case SDLK_F2: return KEY_F2;
328 case SDLK_F3: return KEY_F3;
329 case SDLK_F4: return KEY_F4;
330 case SDLK_F5: return KEY_F5;
331 case SDLK_F6: return KEY_F6;
332 case SDLK_F7: return KEY_F7;
333 case SDLK_F8: return KEY_F8;
334 case SDLK_F9: return KEY_F9;
335 case SDLK_F10: return KEY_F10;
336 case SDLK_F11: return KEY_F11;
337 case SDLK_F12: return KEY_F12;
339 case SDLK_BACKSPACE: return KEY_BACKSPACE;
340 case SDLK_DELETE: return KEY_DEL;
342 case SDLK_PAUSE: return KEY_PAUSE;
344 #if !SDL_VERSION_ATLEAST(1, 3, 0)
345 case SDLK_EQUALS: return KEY_EQUALS;
346 #endif
348 case SDLK_MINUS: return KEY_MINUS;
350 case SDLK_LSHIFT:
351 case SDLK_RSHIFT:
352 return KEY_RSHIFT;
354 case SDLK_LCTRL:
355 case SDLK_RCTRL:
356 return KEY_RCTRL;
358 case SDLK_LALT:
359 case SDLK_RALT:
360 #if !SDL_VERSION_ATLEAST(1, 3, 0)
361 case SDLK_LMETA:
362 case SDLK_RMETA:
363 #endif
364 return KEY_RALT;
366 case SDLK_CAPSLOCK: return KEY_CAPSLOCK;
367 case SDLK_SCROLLOCK: return KEY_SCRLCK;
369 case SDLK_KP0: return KEYP_0;
370 case SDLK_KP1: return KEYP_1;
371 case SDLK_KP2: return KEYP_2;
372 case SDLK_KP3: return KEYP_3;
373 case SDLK_KP4: return KEYP_4;
374 case SDLK_KP5: return KEYP_5;
375 case SDLK_KP6: return KEYP_6;
376 case SDLK_KP7: return KEYP_7;
377 case SDLK_KP8: return KEYP_8;
378 case SDLK_KP9: return KEYP_9;
380 case SDLK_KP_PERIOD: return KEYP_PERIOD;
381 case SDLK_KP_MULTIPLY: return KEYP_MULTIPLY;
382 case SDLK_KP_PLUS: return KEYP_PLUS;
383 case SDLK_KP_MINUS: return KEYP_MINUS;
384 case SDLK_KP_DIVIDE: return KEYP_DIVIDE;
385 case SDLK_KP_EQUALS: return KEYP_EQUALS;
386 case SDLK_KP_ENTER: return KEYP_ENTER;
388 case SDLK_HOME: return KEY_HOME;
389 case SDLK_INSERT: return KEY_INS;
390 case SDLK_END: return KEY_END;
391 case SDLK_PAGEUP: return KEY_PGUP;
392 case SDLK_PAGEDOWN: return KEY_PGDN;
394 #ifdef SDL_HAVE_APP_KEYS
395 case SDLK_APP1: return KEY_F1;
396 case SDLK_APP2: return KEY_F2;
397 case SDLK_APP3: return KEY_F3;
398 case SDLK_APP4: return KEY_F4;
399 case SDLK_APP5: return KEY_F5;
400 case SDLK_APP6: return KEY_F6;
401 #endif
403 default:
404 return tolower(sym->sym);
408 void I_ShutdownGraphics(void)
410 if (initialized)
412 SDL_SetCursor(cursors[1]);
413 SDL_ShowCursor(1);
414 SDL_WM_GrabInput(SDL_GRAB_OFF);
416 SDL_QuitSubSystem(SDL_INIT_VIDEO);
418 initialized = false;
425 // I_StartFrame
427 void I_StartFrame (void)
429 // er?
433 static int MouseButtonState(void)
435 Uint8 state;
436 int result = 0;
438 #if SDL_VERSION_ATLEAST(1, 3, 0)
439 state = SDL_GetMouseState(0, NULL, NULL);
440 #else
441 state = SDL_GetMouseState(NULL, NULL);
442 #endif
444 // Note: button "0" is left, button "1" is right,
445 // button "2" is middle for Doom. This is different
446 // to how SDL sees things.
448 if (state & SDL_BUTTON(1))
449 result |= 1;
450 if (state & SDL_BUTTON(3))
451 result |= 2;
452 if (state & SDL_BUTTON(2))
453 result |= 4;
455 return result;
458 static int AccelerateMouse(int val)
460 if (val < 0)
461 return -AccelerateMouse(-val);
463 if (val > mouse_threshold)
465 return (int)((val - mouse_threshold) * mouse_acceleration + mouse_threshold);
467 else
469 return val;
473 void I_GetEvent(void)
475 SDL_Event sdlevent;
476 event_t event;
478 // possibly not needed
480 SDL_PumpEvents();
482 // put event-grabbing stuff in here
484 while (SDL_PollEvent(&sdlevent))
486 // ignore mouse events when the window is not focused
488 if (!window_focused
489 && (sdlevent.type == SDL_MOUSEMOTION
490 || sdlevent.type == SDL_MOUSEBUTTONDOWN
491 || sdlevent.type == SDL_MOUSEBUTTONUP))
493 continue;
496 if (screensaver_mode && sdlevent.type == SDL_QUIT)
498 I_Quit();
501 // process event
503 switch (sdlevent.type)
505 case SDL_KEYDOWN:
506 event.type = ev_keydown;
507 event.data1 = TranslateKey(&sdlevent.key.keysym);
509 // If Vanilla keyboard mapping enabled, the keyboard
510 // scan code is used to give the character typed.
511 // This does not change depending on keyboard layout.
512 // If you have a German keyboard, pressing 'z' will
513 // give 'y', for example. It is desirable to be able
514 // to fix this so that people with non-standard
515 // keyboard mappings can type properly. If vanilla
516 // mode is disabled, use the properly translated
517 // version.
519 if (vanilla_keyboard_mapping)
521 event.data2 = event.data1;
523 else
525 event.data2 = sdlevent.key.keysym.unicode;
528 if (event.data1 != 0)
530 D_PostEvent(&event);
532 break;
534 case SDL_KEYUP:
535 event.type = ev_keyup;
536 event.data1 = TranslateKey(&sdlevent.key.keysym);
538 if (event.data1 != 0)
540 D_PostEvent(&event);
542 break;
545 case SDL_MOUSEMOTION:
546 event.type = ev_mouse;
547 event.data1 = MouseButtonState();
548 event.data2 = AccelerateMouse(sdlevent.motion.xrel);
549 event.data3 = -AccelerateMouse(sdlevent.motion.yrel);
550 D_PostEvent(&event);
551 break;
554 case SDL_MOUSEBUTTONDOWN:
555 if (usemouse && !nomouse)
557 event.type = ev_mouse;
558 event.data1 = MouseButtonState();
559 event.data2 = event.data3 = 0;
560 D_PostEvent(&event);
562 break;
564 case SDL_MOUSEBUTTONUP:
565 if (usemouse && !nomouse)
567 event.type = ev_mouse;
568 event.data1 = MouseButtonState();
569 event.data2 = event.data3 = 0;
570 D_PostEvent(&event);
572 break;
574 case SDL_QUIT:
575 // bring up the "quit doom?" prompt
576 S_StartSound(NULL,sfx_swtchn);
577 M_QuitDOOM(0);
578 break;
580 case SDL_ACTIVEEVENT:
581 // need to update our focus state
582 UpdateFocus();
583 break;
585 case SDL_VIDEOEXPOSE:
586 palette_to_set = true;
587 break;
589 case SDL_RESIZABLE:
590 need_resize = true;
591 resize_w = sdlevent.resize.w;
592 resize_h = sdlevent.resize.h;
593 last_resize_time = SDL_GetTicks();
594 break;
596 default:
597 break;
602 // Warp the mouse back to the middle of the screen
604 static void CenterMouse(void)
606 // Warp the the screen center
608 SDL_WarpMouse(screen->w / 2, screen->h / 2);
610 // Clear any relative movement caused by warping
612 SDL_PumpEvents();
613 #if SDL_VERSION_ATLEAST(1, 3, 0)
614 SDL_GetRelativeMouseState(0, NULL, NULL);
615 #else
616 SDL_GetRelativeMouseState(NULL, NULL);
617 #endif
621 // Read the change in mouse state to generate mouse motion events
623 // This is to combine all mouse movement for a tic into one mouse
624 // motion event.
626 static void I_ReadMouse(void)
628 int x, y;
629 event_t ev;
631 #if SDL_VERSION_ATLEAST(1, 3, 0)
632 SDL_GetRelativeMouseState(0, &x, &y);
633 #else
634 SDL_GetRelativeMouseState(&x, &y);
635 #endif
637 if (x != 0 || y != 0)
639 ev.type = ev_mouse;
640 ev.data1 = MouseButtonState();
641 ev.data2 = AccelerateMouse(x);
642 ev.data3 = -AccelerateMouse(y);
644 D_PostEvent(&ev);
647 if (MouseShouldBeGrabbed())
649 CenterMouse();
654 // I_StartTic
656 void I_StartTic (void)
658 if (!initialized)
660 return;
663 I_GetEvent();
665 if (usemouse && !nomouse)
667 I_ReadMouse();
670 I_UpdateJoystick();
675 // I_UpdateNoBlit
677 void I_UpdateNoBlit (void)
679 // what is this?
682 static void UpdateGrab(void)
684 static boolean currently_grabbed = false;
685 boolean grab;
687 grab = MouseShouldBeGrabbed();
689 if (screensaver_mode)
691 // Hide the cursor in screensaver mode
693 SDL_SetCursor(cursors[0]);
695 else if (grab && !currently_grabbed)
697 SDL_SetCursor(cursors[0]);
698 SDL_WM_GrabInput(SDL_GRAB_ON);
700 else if (!grab && currently_grabbed)
702 SDL_SetCursor(cursors[1]);
703 SDL_WM_GrabInput(SDL_GRAB_OFF);
706 currently_grabbed = grab;
710 // Update a small portion of the screen
712 // Does stretching and buffer blitting if neccessary
714 // Return true if blit was successful.
716 static boolean BlitArea(int x1, int y1, int x2, int y2)
718 int x_offset, y_offset;
719 boolean result;
721 // No blit needed on native surface
723 if (native_surface)
725 return true;
728 x_offset = (screen->w - screen_mode->width) / 2;
729 y_offset = (screen->h - screen_mode->height) / 2;
731 if (SDL_LockSurface(screen) >= 0)
733 I_InitScale(screens[0],
734 (byte *) screen->pixels + (y_offset * screen->pitch)
735 + x_offset,
736 screen->pitch);
737 result = screen_mode->DrawScreen(x1, y1, x2, y2);
738 SDL_UnlockSurface(screen);
740 else
742 result = false;
745 return result;
748 static void UpdateRect(int x1, int y1, int x2, int y2)
750 int x1_scaled, x2_scaled, y1_scaled, y2_scaled;
752 // Do stretching and blitting
754 if (BlitArea(x1, y1, x2, y2))
756 // Update the area
758 x1_scaled = (x1 * screen_mode->width) / SCREENWIDTH;
759 y1_scaled = (y1 * screen_mode->height) / SCREENHEIGHT;
760 x2_scaled = (x2 * screen_mode->width) / SCREENWIDTH;
761 y2_scaled = (y2 * screen_mode->height) / SCREENHEIGHT;
763 SDL_UpdateRect(screen,
764 x1_scaled, y1_scaled,
765 x2_scaled - x1_scaled,
766 y2_scaled - y1_scaled);
770 void I_BeginRead(void)
772 byte *screenloc = screens[0]
773 + (SCREENHEIGHT - LOADING_DISK_H) * SCREENWIDTH
774 + (SCREENWIDTH - LOADING_DISK_W);
775 int y;
777 if (!initialized || disk_image == NULL)
778 return;
780 // save background and copy the disk image in
782 for (y=0; y<LOADING_DISK_H; ++y)
784 memcpy(saved_background + y * LOADING_DISK_W,
785 screenloc,
786 LOADING_DISK_W);
787 memcpy(screenloc,
788 disk_image + y * LOADING_DISK_W,
789 LOADING_DISK_W);
791 screenloc += SCREENWIDTH;
794 UpdateRect(SCREENWIDTH - LOADING_DISK_W, SCREENHEIGHT - LOADING_DISK_H,
795 SCREENWIDTH, SCREENHEIGHT);
798 void I_EndRead(void)
800 byte *screenloc = screens[0]
801 + (SCREENHEIGHT - LOADING_DISK_H) * SCREENWIDTH
802 + (SCREENWIDTH - LOADING_DISK_W);
803 int y;
805 if (!initialized || disk_image == NULL)
806 return;
808 // save background and copy the disk image in
810 for (y=0; y<LOADING_DISK_H; ++y)
812 memcpy(screenloc,
813 saved_background + y * LOADING_DISK_W,
814 LOADING_DISK_W);
816 screenloc += SCREENWIDTH;
819 UpdateRect(SCREENWIDTH - LOADING_DISK_W, SCREENHEIGHT - LOADING_DISK_H,
820 SCREENWIDTH, SCREENHEIGHT);
824 // I_FinishUpdate
826 void I_FinishUpdate (void)
828 static int lasttic;
829 int tics;
830 int i;
832 if (!initialized)
833 return;
835 if (noblit)
836 return;
838 if (need_resize && SDL_GetTicks() > last_resize_time + 500)
840 ApplyWindowResize(resize_w, resize_h);
841 need_resize = false;
842 palette_to_set = true;
845 UpdateGrab();
847 // Don't update the screen if the window isn't visible.
848 // Not doing this breaks under Windows when we alt-tab away
849 // while fullscreen.
851 if (!(SDL_GetAppState() & SDL_APPACTIVE))
852 return;
854 // draws little dots on the bottom of the screen
855 if (devparm)
858 i = I_GetTime();
859 tics = i - lasttic;
860 lasttic = i;
861 if (tics > 20) tics = 20;
863 for (i=0 ; i<tics*2 ; i+=4)
864 screens[0][ (SCREENHEIGHT-1)*SCREENWIDTH + i] = 0xff;
865 for ( ; i<20*4 ; i+=4)
866 screens[0][ (SCREENHEIGHT-1)*SCREENWIDTH + i] = 0x0;
870 // draw to screen
872 BlitArea(0, 0, SCREENWIDTH, SCREENHEIGHT);
874 // If we have a palette to set, the act of setting the palette
875 // updates the screen
877 if (palette_to_set)
879 SDL_SetColors(screen, palette, 0, 256);
880 palette_to_set = false;
882 else
884 SDL_Flip(screen);
890 // I_ReadScreen
892 void I_ReadScreen (byte* scr)
894 memcpy (scr, screens[0], SCREENWIDTH*SCREENHEIGHT);
899 // I_SetPalette
901 void I_SetPalette (byte *doompalette)
903 int i;
905 for (i=0; i<256; ++i)
907 palette[i].r = gammatable[usegamma][*doompalette++];
908 palette[i].g = gammatable[usegamma][*doompalette++];
909 palette[i].b = gammatable[usegamma][*doompalette++];
912 palette_to_set = true;
916 // Set the window caption
919 void I_SetWindowCaption(void)
921 char *buf;
923 buf = Z_Malloc(strlen(gamedescription) + strlen(PACKAGE_STRING) + 10,
924 PU_STATIC, NULL);
925 sprintf(buf, "%s - %s", gamedescription, PACKAGE_STRING);
927 SDL_WM_SetCaption(buf, NULL);
929 Z_Free(buf);
932 // Set the application icon
934 void I_SetWindowIcon(void)
936 SDL_Surface *surface;
937 Uint8 *mask;
938 int i;
940 // Generate the mask
942 mask = malloc(icon_w * icon_h / 8);
943 memset(mask, 0, icon_w * icon_h / 8);
945 for (i=0; i<icon_w * icon_h; ++i)
947 if (icon_data[i * 3] != 0x00
948 || icon_data[i * 3 + 1] != 0x00
949 || icon_data[i * 3 + 2] != 0x00)
951 mask[i / 8] |= 1 << (7 - i % 8);
955 surface = SDL_CreateRGBSurfaceFrom(icon_data,
956 icon_w,
957 icon_h,
959 icon_w * 3,
960 0xff << 0,
961 0xff << 8,
962 0xff << 16,
965 SDL_WM_SetIcon(surface, mask);
966 SDL_FreeSurface(surface);
967 free(mask);
970 // Pick the modes list to use:
972 static void GetScreenModes(screen_mode_t ***modes_list, int *num_modes)
974 if (aspect_ratio_correct)
976 *modes_list = screen_modes_corrected;
977 *num_modes = arrlen(screen_modes_corrected);
979 else
981 *modes_list = screen_modes;
982 *num_modes = arrlen(screen_modes);
986 // Find which screen_mode_t to use for the given width and height.
988 static screen_mode_t *I_FindScreenMode(int w, int h)
990 screen_mode_t **modes_list;
991 screen_mode_t *best_mode;
992 int modes_list_length;
993 int num_pixels;
994 int best_num_pixels;
995 int i;
997 // Special case: 320x200 and 640x400 are available even if aspect
998 // ratio correction is turned on. These modes have non-square
999 // pixels.
1001 if (fullscreen)
1003 if (w == SCREENWIDTH && h == SCREENHEIGHT)
1005 return &mode_scale_1x;
1007 else if (w == SCREENWIDTH*2 && h == SCREENHEIGHT*2)
1009 return &mode_scale_2x;
1013 GetScreenModes(&modes_list, &modes_list_length);
1015 // Find the biggest screen_mode_t in the list that fits within these
1016 // dimensions
1018 best_mode = NULL;
1019 best_num_pixels = 0;
1021 for (i=0; i<modes_list_length; ++i)
1023 // Will this fit within the dimensions? If not, ignore.
1025 if (modes_list[i]->width > w || modes_list[i]->height > h)
1027 continue;
1030 num_pixels = modes_list[i]->width * modes_list[i]->height;
1032 if (num_pixels > best_num_pixels)
1034 // This is a better mode than the current one
1036 best_mode = modes_list[i];
1037 best_num_pixels = num_pixels;
1041 return best_mode;
1044 // Adjust to an appropriate fullscreen mode.
1045 // Returns true if successful.
1047 static boolean AutoAdjustFullscreen(void)
1049 SDL_Rect **modes;
1050 SDL_Rect *best_mode;
1051 screen_mode_t *screen_mode;
1052 int target_pixels, diff, best_diff;
1053 int i;
1055 modes = SDL_ListModes(NULL, SDL_FULLSCREEN);
1057 // No fullscreen modes available at all?
1059 if (modes == NULL || modes == (SDL_Rect **) -1 || *modes == NULL)
1061 return false;
1064 // Find the best mode that matches the mode specified in the
1065 // configuration file
1067 best_mode = NULL;
1068 best_diff = INT_MAX;
1069 target_pixels = screen_width * screen_height;
1071 for (i=0; modes[i] != NULL; ++i)
1073 //printf("%ix%i?\n", modes[i]->w, modes[i]->h);
1075 // What screen_mode_t would be used for this video mode?
1077 screen_mode = I_FindScreenMode(modes[i]->w, modes[i]->h);
1079 // Never choose a screen mode that we cannot run in, or
1080 // is poor quality for fullscreen
1082 if (screen_mode == NULL || screen_mode->poor_quality)
1084 // printf("\tUnsupported / poor quality\n");
1085 continue;
1088 // Do we have the exact mode?
1089 // If so, no autoadjust needed
1091 if (screen_width == modes[i]->w && screen_height == modes[i]->h)
1093 // printf("\tExact mode!\n");
1094 return true;
1097 // Is this mode better than the current mode?
1099 diff = (screen_width - modes[i]->w) * (screen_width - modes[i]->w)
1100 + (screen_height - modes[i]->h) * (screen_height - modes[i]->h);
1102 if (diff < best_diff)
1104 // printf("\tA valid mode\n");
1105 best_mode = modes[i];
1106 best_diff = diff;
1110 if (best_mode == NULL)
1112 // Unable to find a valid mode!
1114 return false;
1117 printf("I_InitGraphics: %ix%i mode not supported on this machine.\n",
1118 screen_width, screen_height);
1120 screen_width = best_mode->w;
1121 screen_height = best_mode->h;
1123 return true;
1126 // Auto-adjust to a valid windowed mode.
1128 static void AutoAdjustWindowed(void)
1130 screen_mode_t *best_mode;
1132 // Find a screen_mode_t to fit within the current settings
1134 best_mode = I_FindScreenMode(screen_width, screen_height);
1136 if (best_mode == NULL)
1138 // Nothing fits within the current settings.
1139 // Pick the closest to 320x200 possible.
1141 best_mode = I_FindScreenMode(SCREENWIDTH, SCREENHEIGHT_4_3);
1144 // Switch to the best mode if necessary.
1146 if (best_mode->width != screen_width || best_mode->height != screen_height)
1148 printf("I_InitGraphics: Cannot run at specified mode: %ix%i\n",
1149 screen_width, screen_height);
1151 screen_width = best_mode->width;
1152 screen_height = best_mode->height;
1156 // If the video mode set in the configuration file is not available,
1157 // try to choose a different mode.
1159 static void I_AutoAdjustSettings(void)
1161 int old_screen_w, old_screen_h;
1163 old_screen_w = screen_width;
1164 old_screen_h = screen_height;
1166 // If we are running fullscreen, try to autoadjust to a valid fullscreen
1167 // mode. If this is impossible, switch to windowed.
1169 if (fullscreen && !AutoAdjustFullscreen())
1171 fullscreen = 0;
1174 // If we are running windowed, pick a valid window size.
1176 if (!fullscreen)
1178 AutoAdjustWindowed();
1181 // Have the settings changed? Show a message.
1183 if (screen_width != old_screen_w || screen_height != old_screen_h)
1185 printf("I_InitGraphics: Auto-adjusted to %ix%i.\n",
1186 screen_width, screen_height);
1188 printf("NOTE: Your video settings have been adjusted. "
1189 "To disable this behavior,\n"
1190 "set autoadjust_video_settings to 0 in your "
1191 "configuration file.\n");
1195 // Set video size to a particular scale factor (1x, 2x, 3x, etc.)
1197 static void SetScaleFactor(int factor)
1199 if (fullscreen)
1201 // In fullscreen, find a mode that will provide this scale factor
1203 SDL_Rect **modes;
1204 SDL_Rect *best_mode;
1205 screen_mode_t *scrmode;
1206 int best_num_pixels, num_pixels;
1207 int i;
1209 modes = SDL_ListModes(NULL, SDL_FULLSCREEN);
1211 best_mode = NULL;
1212 best_num_pixels = INT_MAX;
1214 for (i=0; modes[i] != NULL; ++i)
1216 // What screen_mode_t will this use?
1218 scrmode = I_FindScreenMode(modes[i]->w, modes[i]->h);
1220 if (scrmode == NULL)
1222 continue;
1225 // Only choose modes that fit the requested scale factor.
1227 // Note that this allows 320x240 as valid for 1x scale, as
1228 // 240/200 is rounded down to 1 by integer division.
1230 if ((scrmode->width / SCREENWIDTH) != factor
1231 || (scrmode->height / SCREENHEIGHT) != factor)
1233 continue;
1236 // Is this a better mode than what we currently have?
1238 num_pixels = modes[i]->w * modes[i]->h;
1240 if (num_pixels < best_num_pixels)
1242 best_num_pixels = num_pixels;
1243 best_mode = modes[i];
1247 if (best_mode == NULL)
1249 I_Error("No fullscreen graphics mode available to support "
1250 "%ix scale factor!", factor);
1253 screen_width = best_mode->w;
1254 screen_height = best_mode->h;
1256 else
1258 int w, h;
1260 // Pick 320x200 or 320x240, depending on aspect ratio correct
1262 if (aspect_ratio_correct)
1264 w = SCREENWIDTH;
1265 h = SCREENHEIGHT_4_3;
1267 else
1269 w = SCREENWIDTH;
1270 h = SCREENHEIGHT;
1273 screen_width = w * factor;
1274 screen_height = h * factor;
1278 static void CheckCommandLine(void)
1280 int i;
1283 // @category video
1285 // Grab the mouse when running in windowed mode.
1288 if (M_CheckParm("-grabmouse"))
1290 grabmouse = true;
1294 // @category video
1296 // Don't grab the mouse when running in windowed mode.
1299 if (M_CheckParm("-nograbmouse"))
1301 grabmouse = false;
1304 // default to fullscreen mode, allow override with command line
1305 // nofullscreen because we love prboom
1308 // @category video
1310 // Run in a window.
1313 if (M_CheckParm("-window") || M_CheckParm("-nofullscreen"))
1315 fullscreen = false;
1319 // @category video
1321 // Run in fullscreen mode.
1324 if (M_CheckParm("-fullscreen"))
1326 fullscreen = true;
1330 // @category video
1332 // Disable the mouse.
1335 nomouse = M_CheckParm("-nomouse") > 0;
1338 // @category video
1339 // @arg <x>
1341 // Specify the screen width, in pixels.
1344 i = M_CheckParm("-width");
1346 if (i > 0)
1348 screen_width = atoi(myargv[i + 1]);
1352 // @category video
1353 // @arg <y>
1355 // Specify the screen height, in pixels.
1358 i = M_CheckParm("-height");
1360 if (i > 0)
1362 screen_height = atoi(myargv[i + 1]);
1366 // @category video
1367 // @arg <WxY>
1369 // Specify the screen mode (when running fullscreen) or the window
1370 // dimensions (when running in windowed mode).
1372 i = M_CheckParm("-geometry");
1374 if (i > 0)
1376 int w, h;
1378 if (sscanf(myargv[i + 1], "%ix%i", &w, &h) == 2)
1380 screen_width = w;
1381 screen_height = h;
1386 // @category video
1388 // Don't scale up the screen.
1391 if (M_CheckParm("-1"))
1393 SetScaleFactor(1);
1397 // @category video
1399 // Double up the screen to 2x its normal size.
1402 if (M_CheckParm("-2"))
1404 SetScaleFactor(2);
1408 // @category video
1410 // Double up the screen to 3x its normal size.
1413 if (M_CheckParm("-3"))
1415 SetScaleFactor(3);
1419 // Check if we have been invoked as a screensaver by xscreensaver.
1421 void I_CheckIsScreensaver(void)
1423 char *env;
1425 env = getenv("XSCREENSAVER_WINDOW");
1427 if (env != NULL)
1429 screensaver_mode = true;
1433 static void CreateCursors(void)
1435 static Uint8 empty_cursor_data = 0;
1437 // Save the default cursor so it can be recalled later
1439 cursors[1] = SDL_GetCursor();
1441 // Create an empty cursor
1443 cursors[0] = SDL_CreateCursor(&empty_cursor_data,
1444 &empty_cursor_data,
1445 1, 1, 0, 0);
1448 static void SetSDLVideoDriver(void)
1450 // Allow a default value for the SDL video driver to be specified
1451 // in the configuration file.
1453 if (strcmp(video_driver, "") != 0)
1455 char *env_string;
1457 env_string = malloc(strlen(video_driver) + 30);
1458 sprintf(env_string, "SDL_VIDEODRIVER=%s", video_driver);
1459 putenv(env_string);
1460 free(env_string);
1463 #if defined(_WIN32) && !defined(_WIN32_WCE)
1465 // Allow -gdi as a shortcut for using the windib driver.
1468 // @category video
1469 // @platform windows
1471 // Use the Windows GDI driver instead of DirectX.
1474 if (M_CheckParm("-gdi") > 0)
1476 putenv("SDL_VIDEODRIVER=windib");
1479 // From the SDL 1.2.10 release notes:
1481 // > The "windib" video driver is the default now, to prevent
1482 // > problems with certain laptops, 64-bit Windows, and Windows
1483 // > Vista.
1485 // The hell with that.
1487 if (getenv("SDL_VIDEODRIVER") == NULL)
1489 putenv("SDL_VIDEODRIVER=directx");
1491 #endif
1494 static char *WindowBoxType(screen_mode_t *mode, int w, int h)
1496 if (mode->width != w && mode->height != h)
1498 return "Windowboxed";
1500 else if (mode->width == w)
1502 return "Letterboxed";
1504 else if (mode->height == h)
1506 return "Pillarboxed";
1508 else
1510 return "...";
1514 static void SetVideoMode(screen_mode_t *mode, int w, int h)
1516 byte *doompal;
1517 int flags = 0;
1519 doompal = W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE);
1521 // Generate lookup tables before setting the video mode.
1523 if (mode != NULL && mode->InitMode != NULL)
1525 mode->InitMode(doompal);
1528 // Set the video mode.
1530 flags |= SDL_SWSURFACE | SDL_HWPALETTE | SDL_DOUBLEBUF;
1532 if (fullscreen)
1534 flags |= SDL_FULLSCREEN;
1536 else
1538 flags |= SDL_RESIZABLE;
1541 screen = SDL_SetVideoMode(w, h, 8, flags);
1543 if (screen == NULL)
1545 I_Error("Error setting video mode: %s\n", SDL_GetError());
1548 // If mode was not set, it must be set now that we know the
1549 // screen size.
1551 if (mode == NULL)
1553 mode = I_FindScreenMode(screen->w, screen->h);
1555 if (mode == NULL)
1557 I_Error("I_InitGraphics: Unable to find a screen mode small "
1558 "enough for %ix%i", screen->w, screen->h);
1561 // Generate lookup tables before setting the video mode.
1563 if (mode->InitMode != NULL)
1565 mode->InitMode(doompal);
1569 // Save screen mode.
1571 screen_mode = mode;
1574 static void ApplyWindowResize(unsigned int w, unsigned int h)
1576 screen_mode_t *mode;
1578 // Find the biggest screen mode that will fall within these
1579 // dimensions, falling back to the smallest mode possible if
1580 // none is found.
1582 mode = I_FindScreenMode(w, h);
1584 if (mode == NULL)
1586 mode = I_FindScreenMode(SCREENWIDTH, SCREENHEIGHT);
1589 // Reset mode to resize window.
1591 printf("Resize to %ix%i\n", mode->width, mode->height);
1592 SetVideoMode(mode, mode->width, mode->height);
1594 // Save settings.
1596 screen_width = mode->width;
1597 screen_height = mode->height;
1600 void I_InitGraphics(void)
1602 SDL_Event dummy;
1603 byte *doompal;
1604 char *env;
1606 // Pass through the XSCREENSAVER_WINDOW environment variable to
1607 // SDL_WINDOWID, to embed the SDL window into the Xscreensaver
1608 // window.
1610 env = getenv("XSCREENSAVER_WINDOW");
1612 if (env != NULL)
1614 char winenv[30];
1615 int winid;
1617 sscanf(env, "0x%x", &winid);
1618 sprintf(winenv, "SDL_WINDOWID=%i", winid);
1620 putenv(winenv);
1623 SetSDLVideoDriver();
1625 if (SDL_Init(SDL_INIT_VIDEO) < 0)
1627 I_Error("Failed to initialize video: %s", SDL_GetError());
1630 // Check for command-line video-related parameters.
1632 CheckCommandLine();
1634 // Set up title and icon. Windows cares about the ordering; this
1635 // has to be done before the call to SDL_SetVideoMode.
1637 I_SetWindowCaption();
1638 #if !SDL_VERSION_ATLEAST(1, 3, 0)
1639 I_SetWindowIcon();
1640 #endif
1643 // Enter into graphics mode.
1645 // When in screensaver mode, run full screen and auto detect
1646 // screen dimensions (don't change video mode)
1649 if (screensaver_mode)
1651 SetVideoMode(NULL, 0, 0);
1653 else
1655 int w, h;
1657 if (autoadjust_video_settings)
1659 I_AutoAdjustSettings();
1662 w = screen_width;
1663 h = screen_height;
1665 screen_mode = I_FindScreenMode(w, h);
1667 if (screen_mode == NULL)
1669 I_Error("I_InitGraphics: Unable to find a screen mode small "
1670 "enough for %ix%i", w, h);
1673 if (w != screen_mode->width || h != screen_mode->height)
1675 printf("I_InitGraphics: %s (%ix%i within %ix%i)\n",
1676 WindowBoxType(screen_mode, w, h),
1677 screen_mode->width, screen_mode->height, w, h);
1680 SetVideoMode(screen_mode, w, h);
1683 // Start with a clear black screen
1684 // (screen will be flipped after we set the palette)
1686 if (SDL_LockSurface(screen) >= 0)
1688 byte *screenpixels;
1689 int y;
1691 screenpixels = (byte *) screen->pixels;
1693 for (y=0; y<screen->h; ++y)
1694 memset(screenpixels + screen->pitch * y, 0, screen->w);
1696 SDL_UnlockSurface(screen);
1699 // Set the palette
1701 doompal = W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE);
1702 I_SetPalette(doompal);
1703 SDL_SetColors(screen, palette, 0, 256);
1705 CreateCursors();
1707 UpdateFocus();
1708 UpdateGrab();
1710 // On some systems, it takes a second or so for the screen to settle
1711 // after changing modes. We include the option to add a delay when
1712 // setting the screen mode, so that the game doesn't start immediately
1713 // with the player unable to see anything.
1715 if (fullscreen && !screensaver_mode)
1717 SDL_Delay(startup_delay);
1720 // Check if we have a native surface we can use
1721 // If we have to lock the screen, draw to a buffer and copy
1722 // Likewise if the screen pitch is not the same as the width
1723 // If we have to multiply, drawing is done to a separate 320x200 buf
1725 native_surface = !SDL_MUSTLOCK(screen)
1726 && screen_mode == &mode_scale_1x
1727 && screen->pitch == SCREENWIDTH
1728 && aspect_ratio_correct;
1730 // If not, allocate a buffer and copy from that buffer to the
1731 // screen when we do an update
1733 if (native_surface)
1735 screens[0] = (unsigned char *) screen->pixels;
1737 screens[0] += (screen->h - SCREENHEIGHT) / 2;
1739 else
1741 screens[0] = (unsigned char *) Z_Malloc (SCREENWIDTH * SCREENHEIGHT,
1742 PU_STATIC, NULL);
1745 // "Loading from disk" icon
1747 LoadDiskImage();
1749 // Clear the screen to black.
1751 memset(screens[0], 0, SCREENWIDTH * SCREENHEIGHT);
1753 // We need SDL to give us translated versions of keys as well
1755 SDL_EnableUNICODE(1);
1757 // Repeat key presses - this is what Vanilla Doom does
1758 // Not sure about repeat rate - probably dependent on which DOS
1759 // driver is used. This is good enough though.
1761 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
1763 // clear out any events waiting at the start and center the mouse
1765 while (SDL_PollEvent(&dummy));
1767 if (usemouse && !nomouse && (fullscreen || grabmouse))
1769 CenterMouse();
1772 initialized = true;