1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
4 // Copyright(C) 1993-1996 Id Software, Inc.
5 // Copyright(C) 2005 Simon Howard
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
23 // DOOM graphics stuff for SDL.
25 //-----------------------------------------------------------------------------
40 #include "i_joystick.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
[] = {
66 // Aspect ratio corrected modes (4:3 ratio)
68 static screen_mode_t
*screen_modes_corrected
[] = {
70 // Vertically stretched modes (320x200 -> 320x240 and multiples)
78 // Horizontally squashed modes (320x200 -> 256x200 and multiples)
87 extern void M_QuitDOOM();
88 extern boolean advancedemo
;
90 // SDL video driver name
92 char *video_driver
= "";
94 static SDL_Surface
*screen
;
98 static SDL_Color palette
[256];
99 static boolean palette_to_set
;
101 // display has been set up?
103 static boolean initialized
= false;
107 static boolean nomouse
= false;
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
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
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
)
194 // if the window doesn't have focus, never grab it
199 // always grab the mouse when full screen (dont want to
200 // see the mouse pointer)
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.
216 // Don't grab the mouse if mouse input is disabled
218 if (!usemouse
|| nomouse
)
221 // Drone players don't need mouse focus
226 // if we specify not to grab the mouse, never grab
231 // when menu is active or game is paused, release the mouse
233 if (menuactive
|| paused
)
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)
253 state
= SDL_GetAppState();
255 // We should have input (keyboard) focus and be visible
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)
270 int xoffset
= SCREENWIDTH
- LOADING_DISK_W
;
271 int yoffset
= SCREENHEIGHT
- LOADING_DISK_H
;
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.
285 if (M_CheckParm("-cdrom") > 0)
286 disk_name
= DEH_String("STCDROM");
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
,
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
,
308 W_ReleaseLumpName(disk_name
);
312 // Translates the SDL key
315 static int TranslateKey(SDL_keysym
*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
;
348 case SDLK_MINUS
: return KEY_MINUS
;
360 #if !SDL_VERSION_ATLEAST(1, 3, 0)
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
;
404 return tolower(sym
->sym
);
408 void I_ShutdownGraphics(void)
412 SDL_SetCursor(cursors
[1]);
414 SDL_WM_GrabInput(SDL_GRAB_OFF
);
416 SDL_QuitSubSystem(SDL_INIT_VIDEO
);
427 void I_StartFrame (void)
433 static int MouseButtonState(void)
438 #if SDL_VERSION_ATLEAST(1, 3, 0)
439 state
= SDL_GetMouseState(0, NULL
, NULL
);
441 state
= SDL_GetMouseState(NULL
, NULL
);
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))
450 if (state
& SDL_BUTTON(3))
452 if (state
& SDL_BUTTON(2))
458 static int AccelerateMouse(int val
)
461 return -AccelerateMouse(-val
);
463 if (val
> mouse_threshold
)
465 return (int)((val
- mouse_threshold
) * mouse_acceleration
+ mouse_threshold
);
473 void I_GetEvent(void)
478 // possibly not needed
482 // put event-grabbing stuff in here
484 while (SDL_PollEvent(&sdlevent
))
486 // ignore mouse events when the window is not focused
489 && (sdlevent
.type
== SDL_MOUSEMOTION
490 || sdlevent
.type
== SDL_MOUSEBUTTONDOWN
491 || sdlevent
.type
== SDL_MOUSEBUTTONUP
))
496 if (screensaver_mode
&& sdlevent
.type
== SDL_QUIT
)
503 switch (sdlevent
.type
)
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
519 if (vanilla_keyboard_mapping
)
521 event
.data2
= event
.data1
;
525 event
.data2
= sdlevent
.key
.keysym
.unicode
;
528 if (event
.data1
!= 0)
535 event
.type
= ev_keyup
;
536 event
.data1
= TranslateKey(&sdlevent
.key
.keysym
);
538 if (event
.data1
!= 0)
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);
554 case SDL_MOUSEBUTTONDOWN
:
555 if (usemouse
&& !nomouse
)
557 event
.type
= ev_mouse
;
558 event
.data1
= MouseButtonState();
559 event
.data2
= event
.data3
= 0;
564 case SDL_MOUSEBUTTONUP
:
565 if (usemouse
&& !nomouse
)
567 event
.type
= ev_mouse
;
568 event
.data1
= MouseButtonState();
569 event
.data2
= event
.data3
= 0;
575 // bring up the "quit doom?" prompt
576 S_StartSound(NULL
,sfx_swtchn
);
580 case SDL_ACTIVEEVENT
:
581 // need to update our focus state
585 case SDL_VIDEOEXPOSE
:
586 palette_to_set
= true;
591 resize_w
= sdlevent
.resize
.w
;
592 resize_h
= sdlevent
.resize
.h
;
593 last_resize_time
= SDL_GetTicks();
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
613 #if SDL_VERSION_ATLEAST(1, 3, 0)
614 SDL_GetRelativeMouseState(0, NULL
, NULL
);
616 SDL_GetRelativeMouseState(NULL
, NULL
);
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
626 static void I_ReadMouse(void)
631 #if SDL_VERSION_ATLEAST(1, 3, 0)
632 SDL_GetRelativeMouseState(0, &x
, &y
);
634 SDL_GetRelativeMouseState(&x
, &y
);
637 if (x
!= 0 || y
!= 0)
640 ev
.data1
= MouseButtonState();
641 ev
.data2
= AccelerateMouse(x
);
642 ev
.data3
= -AccelerateMouse(y
);
647 if (MouseShouldBeGrabbed())
656 void I_StartTic (void)
665 if (usemouse
&& !nomouse
)
677 void I_UpdateNoBlit (void)
682 static void UpdateGrab(void)
684 static boolean currently_grabbed
= false;
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
;
721 // No blit needed on native surface
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
)
737 result
= screen_mode
->DrawScreen(x1
, y1
, x2
, y2
);
738 SDL_UnlockSurface(screen
);
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
))
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
);
777 if (!initialized
|| disk_image
== NULL
)
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
,
788 disk_image
+ y
* LOADING_DISK_W
,
791 screenloc
+= SCREENWIDTH
;
794 UpdateRect(SCREENWIDTH
- LOADING_DISK_W
, SCREENHEIGHT
- LOADING_DISK_H
,
795 SCREENWIDTH
, SCREENHEIGHT
);
800 byte
*screenloc
= screens
[0]
801 + (SCREENHEIGHT
- LOADING_DISK_H
) * SCREENWIDTH
802 + (SCREENWIDTH
- LOADING_DISK_W
);
805 if (!initialized
|| disk_image
== NULL
)
808 // save background and copy the disk image in
810 for (y
=0; y
<LOADING_DISK_H
; ++y
)
813 saved_background
+ y
* LOADING_DISK_W
,
816 screenloc
+= SCREENWIDTH
;
819 UpdateRect(SCREENWIDTH
- LOADING_DISK_W
, SCREENHEIGHT
- LOADING_DISK_H
,
820 SCREENWIDTH
, SCREENHEIGHT
);
826 void I_FinishUpdate (void)
838 if (need_resize
&& SDL_GetTicks() > last_resize_time
+ 500)
840 ApplyWindowResize(resize_w
, resize_h
);
842 palette_to_set
= true;
847 // Don't update the screen if the window isn't visible.
848 // Not doing this breaks under Windows when we alt-tab away
851 if (!(SDL_GetAppState() & SDL_APPACTIVE
))
854 // draws little dots on the bottom of the screen
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;
872 BlitArea(0, 0, SCREENWIDTH
, SCREENHEIGHT
);
874 // If we have a palette to set, the act of setting the palette
875 // updates the screen
879 SDL_SetColors(screen
, palette
, 0, 256);
880 palette_to_set
= false;
892 void I_ReadScreen (byte
* scr
)
894 memcpy (scr
, screens
[0], SCREENWIDTH
*SCREENHEIGHT
);
901 void I_SetPalette (byte
*doompalette
)
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)
923 buf
= Z_Malloc(strlen(gamedescription
) + strlen(PACKAGE_STRING
) + 10,
925 sprintf(buf
, "%s - %s", gamedescription
, PACKAGE_STRING
);
927 SDL_WM_SetCaption(buf
, NULL
);
932 // Set the application icon
934 void I_SetWindowIcon(void)
936 SDL_Surface
*surface
;
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
,
965 SDL_WM_SetIcon(surface
, mask
);
966 SDL_FreeSurface(surface
);
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
);
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
;
997 // Special case: 320x200 and 640x400 are available even if aspect
998 // ratio correction is turned on. These modes have non-square
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
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
)
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
;
1044 // Adjust to an appropriate fullscreen mode.
1045 // Returns true if successful.
1047 static boolean
AutoAdjustFullscreen(void)
1050 SDL_Rect
*best_mode
;
1051 screen_mode_t
*screen_mode
;
1052 int target_pixels
, diff
, best_diff
;
1055 modes
= SDL_ListModes(NULL
, SDL_FULLSCREEN
);
1057 // No fullscreen modes available at all?
1059 if (modes
== NULL
|| modes
== (SDL_Rect
**) -1 || *modes
== NULL
)
1064 // Find the best mode that matches the mode specified in the
1065 // configuration file
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");
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");
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
];
1110 if (best_mode
== NULL
)
1112 // Unable to find a valid mode!
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
;
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())
1174 // If we are running windowed, pick a valid window size.
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
)
1201 // In fullscreen, find a mode that will provide this scale factor
1204 SDL_Rect
*best_mode
;
1205 screen_mode_t
*scrmode
;
1206 int best_num_pixels
, num_pixels
;
1209 modes
= SDL_ListModes(NULL
, SDL_FULLSCREEN
);
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
)
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
)
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
;
1260 // Pick 320x200 or 320x240, depending on aspect ratio correct
1262 if (aspect_ratio_correct
)
1265 h
= SCREENHEIGHT_4_3
;
1273 screen_width
= w
* factor
;
1274 screen_height
= h
* factor
;
1278 static void CheckCommandLine(void)
1285 // Grab the mouse when running in windowed mode.
1288 if (M_CheckParm("-grabmouse"))
1296 // Don't grab the mouse when running in windowed mode.
1299 if (M_CheckParm("-nograbmouse"))
1304 // default to fullscreen mode, allow override with command line
1305 // nofullscreen because we love prboom
1313 if (M_CheckParm("-window") || M_CheckParm("-nofullscreen"))
1321 // Run in fullscreen mode.
1324 if (M_CheckParm("-fullscreen"))
1332 // Disable the mouse.
1335 nomouse
= M_CheckParm("-nomouse") > 0;
1341 // Specify the screen width, in pixels.
1344 i
= M_CheckParm("-width");
1348 screen_width
= atoi(myargv
[i
+ 1]);
1355 // Specify the screen height, in pixels.
1358 i
= M_CheckParm("-height");
1362 screen_height
= atoi(myargv
[i
+ 1]);
1369 // Specify the screen mode (when running fullscreen) or the window
1370 // dimensions (when running in windowed mode).
1372 i
= M_CheckParm("-geometry");
1378 if (sscanf(myargv
[i
+ 1], "%ix%i", &w
, &h
) == 2)
1388 // Don't scale up the screen.
1391 if (M_CheckParm("-1"))
1399 // Double up the screen to 2x its normal size.
1402 if (M_CheckParm("-2"))
1410 // Double up the screen to 3x its normal size.
1413 if (M_CheckParm("-3"))
1419 // Check if we have been invoked as a screensaver by xscreensaver.
1421 void I_CheckIsScreensaver(void)
1425 env
= getenv("XSCREENSAVER_WINDOW");
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
,
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)
1457 env_string
= malloc(strlen(video_driver
) + 30);
1458 sprintf(env_string
, "SDL_VIDEODRIVER=%s", video_driver
);
1463 #if defined(_WIN32) && !defined(_WIN32_WCE)
1465 // Allow -gdi as a shortcut for using the windib driver.
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
1485 // The hell with that.
1487 if (getenv("SDL_VIDEODRIVER") == NULL
)
1489 putenv("SDL_VIDEODRIVER=directx");
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";
1514 static void SetVideoMode(screen_mode_t
*mode
, int w
, int h
)
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
;
1534 flags
|= SDL_FULLSCREEN
;
1538 flags
|= SDL_RESIZABLE
;
1541 screen
= SDL_SetVideoMode(w
, h
, 8, flags
);
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
1553 mode
= I_FindScreenMode(screen
->w
, screen
->h
);
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.
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
1582 mode
= I_FindScreenMode(w
, h
);
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
);
1596 screen_width
= mode
->width
;
1597 screen_height
= mode
->height
;
1600 void I_InitGraphics(void)
1606 // Pass through the XSCREENSAVER_WINDOW environment variable to
1607 // SDL_WINDOWID, to embed the SDL window into the Xscreensaver
1610 env
= getenv("XSCREENSAVER_WINDOW");
1617 sscanf(env
, "0x%x", &winid
);
1618 sprintf(winenv
, "SDL_WINDOWID=%i", winid
);
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.
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)
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);
1657 if (autoadjust_video_settings
)
1659 I_AutoAdjustSettings();
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)
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
);
1701 doompal
= W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE
);
1702 I_SetPalette(doompal
);
1703 SDL_SetColors(screen
, palette
, 0, 256);
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
1735 screens
[0] = (unsigned char *) screen
->pixels
;
1737 screens
[0] += (screen
->h
- SCREENHEIGHT
) / 2;
1741 screens
[0] = (unsigned char *) Z_Malloc (SCREENWIDTH
* SCREENHEIGHT
,
1745 // "Loading from disk" icon
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
))