doomgeneric/fbdoom/i_video.c

2213 lines
50 KiB
C

//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// DOOM graphics stuff for SDL.
//
#include "SDL/SDL.h"
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include "icon.c"
#include "config.h"
#include "deh_str.h"
#include "doomtype.h"
#include "doomkeys.h"
#include "i_joystick.h"
#include "i_system.h"
#include "i_swap.h"
#include "i_timer.h"
#include "i_video.h"
#include "i_scale.h"
#include "m_argv.h"
#include "m_config.h"
#include "m_misc.h"
#include "tables.h"
#include "v_video.h"
#include "w_wad.h"
#include "z_zone.h"
// Lookup table for mapping ASCII characters to their equivalent when
// shift is pressed on an American layout keyboard:
static const char shiftxform[] =
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, ' ', '!', '"', '#', '$', '%', '&',
'"', // shift-'
'(', ')', '*', '+',
'<', // shift-,
'_', // shift--
'>', // shift-.
'?', // shift-/
')', // shift-0
'!', // shift-1
'@', // shift-2
'#', // shift-3
'$', // shift-4
'%', // shift-5
'^', // shift-6
'&', // shift-7
'*', // shift-8
'(', // shift-9
':',
':', // shift-;
'<',
'+', // shift-=
'>', '?', '@',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'[', // shift-[
'!', // shift-backslash - OH MY GOD DOES WATCOM SUCK
']', // shift-]
'"', '_',
'\'', // shift-`
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'{', '|', '}', '~', 127
};
#define LOADING_DISK_W 16
#define LOADING_DISK_H 16
// Non aspect ratio-corrected modes (direct multiples of 320x200)
static screen_mode_t *screen_modes[] = {
&mode_scale_1x,
&mode_scale_2x,
&mode_scale_3x,
&mode_scale_4x,
&mode_scale_5x,
};
// Aspect ratio corrected modes (4:3 ratio)
static screen_mode_t *screen_modes_corrected[] = {
// Vertically stretched modes (320x200 -> 320x240 and multiples)
&mode_stretch_1x,
&mode_stretch_2x,
&mode_stretch_3x,
&mode_stretch_4x,
&mode_stretch_5x,
// Horizontally squashed modes (320x200 -> 256x200 and multiples)
&mode_squash_1x,
&mode_squash_2x,
&mode_squash_3x,
&mode_squash_4x,
&mode_squash_5x,
};
// SDL video driver name
char *video_driver = "";
// Window position:
static char *window_position = "";
// SDL surface for the screen.
static SDL_Surface *screen;
// Window title
static char *window_title = "";
// Intermediate 8-bit buffer that we draw to instead of 'screen'.
// This is used when we are rendering in 32-bit screen mode.
// When in a real 8-bit screen mode, screenbuffer == screen.
static SDL_Surface *screenbuffer = NULL;
// palette
static SDL_Color palette[256];
static boolean palette_to_set;
// display has been set up?
static boolean initialized = false;
// disable mouse?
static boolean nomouse = false;
int usemouse = 1;
// Bit mask of mouse button state.
static unsigned int mouse_button_state = 0;
// Disallow mouse and joystick movement to cause forward/backward
// motion. Specified with the '-novert' command line parameter.
// This is an int to allow saving to config file
int novert = 0;
// Save screenshots in PNG format.
int png_screenshots = 0;
// if true, I_VideoBuffer is screen->pixels
static boolean native_surface;
// Screen width and height, from configuration file.
int screen_width = SCREENWIDTH;
int screen_height = SCREENHEIGHT;
// Color depth.
int screen_bpp = 0;
// Automatically adjust video settings if the selected mode is
// not a valid video mode.
static int autoadjust_video_settings = 1;
// Run in full screen mode? (int type for config code)
int fullscreen = true;
// Aspect ratio correction mode
int aspect_ratio_correct = true;
// Time to wait for the screen to settle on startup before starting the
// game (ms)
static int startup_delay = 1000;
// Grab the mouse? (int type for config code)
static int grabmouse = true;
// The screen buffer; this is modified to draw things to the screen
byte *I_VideoBuffer = NULL;
// If true, game is running as a screensaver
boolean screensaver_mode = false;
// Flag indicating whether the screen is currently visible:
// when the screen isnt visible, don't render the screen
boolean screenvisible;
// If true, we display dots at the bottom of the screen to
// indicate FPS.
static boolean display_fps_dots;
// If this is true, the screen is rendered but not blitted to the
// video buffer.
static boolean noblit;
// Callback function to invoke to determine whether to grab the
// mouse pointer.
static grabmouse_callback_t grabmouse_callback = NULL;
// disk image data and background overwritten by the disk to be
// restored by EndRead
static byte *disk_image = NULL;
static byte *saved_background;
static boolean window_focused;
// Empty mouse cursor
static SDL_Cursor *cursors[2];
// The screen mode and scale functions being used
static screen_mode_t *screen_mode;
// Window resize state.
static boolean need_resize = false;
static unsigned int resize_w, resize_h;
static unsigned int last_resize_time;
// If true, keyboard mapping is ignored, like in Vanilla Doom.
// The sensible thing to do is to disable this if you have a non-US
// keyboard.
int vanilla_keyboard_mapping = true;
// Is the shift key currently down?
static int shiftdown = 0;
// Mouse acceleration
//
// This emulates some of the behavior of DOS mouse drivers by increasing
// the speed when the mouse is moved fast.
//
// The mouse input values are input directly to the game, but when
// the values exceed the value of mouse_threshold, they are multiplied
// by mouse_acceleration to increase the speed.
float mouse_acceleration = 2.0;
int mouse_threshold = 10;
// Gamma correction level to use
int usegamma = 0;
static void ApplyWindowResize(unsigned int w, unsigned int h);
static boolean MouseShouldBeGrabbed()
{
// never grab the mouse when in screensaver mode
if (screensaver_mode)
return false;
// if the window doesn't have focus, never grab it
if (!window_focused)
return false;
// always grab the mouse when full screen (dont want to
// see the mouse pointer)
if (fullscreen)
return true;
// Don't grab the mouse if mouse input is disabled
if (!usemouse || nomouse)
return false;
// if we specify not to grab the mouse, never grab
if (!grabmouse)
return false;
// Invoke the grabmouse callback function to determine whether
// the mouse should be grabbed
if (grabmouse_callback != NULL)
{
return grabmouse_callback();
}
else
{
return true;
}
}
void I_SetGrabMouseCallback(grabmouse_callback_t func)
{
grabmouse_callback = func;
}
// Set the variable controlling FPS dots.
void I_DisplayFPSDots(boolean dots_on)
{
display_fps_dots = dots_on;
}
// Update the value of window_focused when we get a focus event
//
// We try to make ourselves be well-behaved: the grab on the mouse
// is removed if we lose focus (such as a popup window appearing),
// and we dont move the mouse around if we aren't focused either.
static void UpdateFocus(void)
{
Uint8 state;
state = SDL_GetAppState();
// We should have input (keyboard) focus and be visible
// (not minimized)
window_focused = (state & SDL_APPINPUTFOCUS) && (state & SDL_APPACTIVE);
// Should the screen be grabbed?
screenvisible = (state & SDL_APPACTIVE) != 0;
}
// Show or hide the mouse cursor. We have to use different techniques
// depending on the OS.
static void SetShowCursor(boolean show)
{
// On Windows, using SDL_ShowCursor() adds lag to the mouse input,
// so work around this by setting an invisible cursor instead. On
// other systems, it isn't possible to change the cursor, so this
// hack has to be Windows-only. (Thanks to entryway for this)
#ifdef _WIN32
if (show)
{
SDL_SetCursor(cursors[1]);
}
else
{
SDL_SetCursor(cursors[0]);
}
#else
SDL_ShowCursor(show);
#endif
// When the cursor is hidden, grab the input.
if (!screensaver_mode)
{
SDL_WM_GrabInput(!show);
}
}
void I_EnableLoadingDisk(void)
{
patch_t *disk;
byte *tmpbuf;
char *disk_name;
int y;
char buf[20];
SDL_VideoDriverName(buf, 15);
if (!strcmp(buf, "Quartz"))
{
// MacOS Quartz gives us pageflipped graphics that screw up the
// display when we use the loading disk. Disable it.
// This is a gross hack.
return;
}
if (M_CheckParm("-cdrom") > 0)
disk_name = DEH_String("STCDROM");
else
disk_name = DEH_String("STDISK");
disk = W_CacheLumpName(disk_name, PU_STATIC);
// Draw the patch into a temporary buffer
tmpbuf = Z_Malloc(SCREENWIDTH * (disk->height + 1), PU_STATIC, NULL);
V_UseBuffer(tmpbuf);
// Draw the disk to the screen:
V_DrawPatch(0, 0, disk);
disk_image = Z_Malloc(LOADING_DISK_W * LOADING_DISK_H, PU_STATIC, NULL);
saved_background = Z_Malloc(LOADING_DISK_W * LOADING_DISK_H, PU_STATIC, NULL);
for (y=0; y<LOADING_DISK_H; ++y)
{
memcpy(disk_image + LOADING_DISK_W * y,
tmpbuf + SCREENWIDTH * y,
LOADING_DISK_W);
}
// All done - free the screen buffer and restore the normal
// video buffer.
W_ReleaseLumpName(disk_name);
V_RestoreBuffer();
Z_Free(tmpbuf);
}
//
// Translates the SDL key
//
static int TranslateKey(SDL_keysym *sym)
{
switch(sym->sym)
{
case SDLK_LEFT: return KEY_LEFTARROW;
case SDLK_RIGHT: return KEY_RIGHTARROW;
case SDLK_DOWN: return KEY_DOWNARROW;
case SDLK_UP: return KEY_UPARROW;
case SDLK_ESCAPE: return KEY_ESCAPE;
case SDLK_RETURN: return KEY_ENTER;
case SDLK_TAB: return KEY_TAB;
case SDLK_F1: return KEY_F1;
case SDLK_F2: return KEY_F2;
case SDLK_F3: return KEY_F3;
case SDLK_F4: return KEY_F4;
case SDLK_F5: return KEY_F5;
case SDLK_F6: return KEY_F6;
case SDLK_F7: return KEY_F7;
case SDLK_F8: return KEY_F8;
case SDLK_F9: return KEY_F9;
case SDLK_F10: return KEY_F10;
case SDLK_F11: return KEY_F11;
case SDLK_F12: return KEY_F12;
case SDLK_PRINT: return KEY_PRTSCR;
case SDLK_BACKSPACE: return KEY_BACKSPACE;
case SDLK_DELETE: return KEY_DEL;
case SDLK_PAUSE: return KEY_PAUSE;
#if !SDL_VERSION_ATLEAST(1, 3, 0)
case SDLK_EQUALS: return KEY_EQUALS;
#endif
case SDLK_MINUS: return KEY_MINUS;
case SDLK_LSHIFT:
case SDLK_RSHIFT:
return KEY_RSHIFT;
case SDLK_LCTRL:
case SDLK_RCTRL:
return KEY_RCTRL;
case SDLK_LALT:
case SDLK_RALT:
#if !SDL_VERSION_ATLEAST(1, 3, 0)
case SDLK_LMETA:
case SDLK_RMETA:
#endif
return KEY_RALT;
case SDLK_CAPSLOCK: return KEY_CAPSLOCK;
case SDLK_SCROLLOCK: return KEY_SCRLCK;
case SDLK_NUMLOCK: return KEY_NUMLOCK;
case SDLK_KP0: return KEYP_0;
case SDLK_KP1: return KEYP_1;
case SDLK_KP2: return KEYP_2;
case SDLK_KP3: return KEYP_3;
case SDLK_KP4: return KEYP_4;
case SDLK_KP5: return KEYP_5;
case SDLK_KP6: return KEYP_6;
case SDLK_KP7: return KEYP_7;
case SDLK_KP8: return KEYP_8;
case SDLK_KP9: return KEYP_9;
case SDLK_KP_PERIOD: return KEYP_PERIOD;
case SDLK_KP_MULTIPLY: return KEYP_MULTIPLY;
case SDLK_KP_PLUS: return KEYP_PLUS;
case SDLK_KP_MINUS: return KEYP_MINUS;
case SDLK_KP_DIVIDE: return KEYP_DIVIDE;
case SDLK_KP_EQUALS: return KEYP_EQUALS;
case SDLK_KP_ENTER: return KEYP_ENTER;
case SDLK_HOME: return KEY_HOME;
case SDLK_INSERT: return KEY_INS;
case SDLK_END: return KEY_END;
case SDLK_PAGEUP: return KEY_PGUP;
case SDLK_PAGEDOWN: return KEY_PGDN;
#ifdef SDL_HAVE_APP_KEYS
case SDLK_APP1: return KEY_F1;
case SDLK_APP2: return KEY_F2;
case SDLK_APP3: return KEY_F3;
case SDLK_APP4: return KEY_F4;
case SDLK_APP5: return KEY_F5;
case SDLK_APP6: return KEY_F6;
#endif
default:
return tolower(sym->sym);
}
}
void I_ShutdownGraphics(void)
{
if (initialized)
{
SetShowCursor(true);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
initialized = false;
}
}
//
// I_StartFrame
//
void I_StartFrame (void)
{
// er?
}
static void UpdateMouseButtonState(unsigned int button, boolean on)
{
event_t event;
if (button < SDL_BUTTON_LEFT || button > MAX_MOUSE_BUTTONS)
{
return;
}
// Note: button "0" is left, button "1" is right,
// button "2" is middle for Doom. This is different
// to how SDL sees things.
switch (button)
{
case SDL_BUTTON_LEFT:
button = 0;
break;
case SDL_BUTTON_RIGHT:
button = 1;
break;
case SDL_BUTTON_MIDDLE:
button = 2;
break;
default:
// SDL buttons are indexed from 1.
--button;
break;
}
// Turn bit representing this button on or off.
if (on)
{
mouse_button_state |= (1 << button);
}
else
{
mouse_button_state &= ~(1 << button);
}
// Post an event with the new button state.
event.type = ev_mouse;
event.data1 = mouse_button_state;
event.data2 = event.data3 = 0;
D_PostEvent(&event);
}
static int AccelerateMouse(int val)
{
if (val < 0)
return -AccelerateMouse(-val);
if (val > mouse_threshold)
{
return (int)((val - mouse_threshold) * mouse_acceleration + mouse_threshold);
}
else
{
return val;
}
}
// Get the equivalent ASCII (Unicode?) character for a keypress.
static int GetTypedChar(SDL_Event *event)
{
int key;
// If Vanilla keyboard mapping enabled, the keyboard
// scan code is used to give the character typed.
// This does not change depending on keyboard layout.
// If you have a German keyboard, pressing 'z' will
// give 'y', for example. It is desirable to be able
// to fix this so that people with non-standard
// keyboard mappings can type properly. If vanilla
// mode is disabled, use the properly translated
// version.
if (vanilla_keyboard_mapping)
{
key = TranslateKey(&event->key.keysym);
// Is shift held down? If so, perform a translation.
if (shiftdown > 0)
{
if (key >= 0 && key < arrlen(shiftxform))
{
key = shiftxform[key];
}
else
{
key = 0;
}
}
return key;
}
else
{
// Unicode value, from key layout.
return tolower(event->key.keysym.unicode);
}
}
static void UpdateShiftStatus(SDL_Event *event)
{
int change;
if (event->type == SDL_KEYDOWN)
{
change = 1;
}
else if (event->type == SDL_KEYUP)
{
change = -1;
}
else
{
return;
}
if (event->key.keysym.sym == SDLK_LSHIFT
|| event->key.keysym.sym == SDLK_RSHIFT)
{
shiftdown += change;
}
}
void I_GetEvent(void)
{
SDL_Event sdlevent;
event_t event;
// possibly not needed
SDL_PumpEvents();
// put event-grabbing stuff in here
while (SDL_PollEvent(&sdlevent))
{
// ignore mouse events when the window is not focused
if (!window_focused
&& (sdlevent.type == SDL_MOUSEMOTION
|| sdlevent.type == SDL_MOUSEBUTTONDOWN
|| sdlevent.type == SDL_MOUSEBUTTONUP))
{
continue;
}
if (screensaver_mode && sdlevent.type == SDL_QUIT)
{
I_Quit();
}
UpdateShiftStatus(&sdlevent);
// process event
switch (sdlevent.type)
{
case SDL_KEYDOWN:
// data1 has the key pressed, data2 has the character
// (shift-translated, etc)
event.type = ev_keydown;
event.data1 = TranslateKey(&sdlevent.key.keysym);
event.data2 = GetTypedChar(&sdlevent);
if (event.data1 != 0)
{
D_PostEvent(&event);
}
break;
case SDL_KEYUP:
event.type = ev_keyup;
event.data1 = TranslateKey(&sdlevent.key.keysym);
// data2 is just initialized to zero for ev_keyup.
// For ev_keydown it's the shifted Unicode character
// that was typed, but if something wants to detect
// key releases it should do so based on data1
// (key ID), not the printable char.
event.data2 = 0;
if (event.data1 != 0)
{
D_PostEvent(&event);
}
break;
/*
case SDL_MOUSEMOTION:
event.type = ev_mouse;
event.data1 = mouse_button_state;
event.data2 = AccelerateMouse(sdlevent.motion.xrel);
event.data3 = -AccelerateMouse(sdlevent.motion.yrel);
D_PostEvent(&event);
break;
*/
case SDL_MOUSEBUTTONDOWN:
if (usemouse && !nomouse)
{
UpdateMouseButtonState(sdlevent.button.button, true);
}
break;
case SDL_MOUSEBUTTONUP:
if (usemouse && !nomouse)
{
UpdateMouseButtonState(sdlevent.button.button, false);
}
break;
case SDL_QUIT:
event.type = ev_quit;
D_PostEvent(&event);
break;
case SDL_ACTIVEEVENT:
// need to update our focus state
UpdateFocus();
break;
case SDL_VIDEOEXPOSE:
palette_to_set = true;
break;
case SDL_RESIZABLE:
need_resize = true;
resize_w = sdlevent.resize.w;
resize_h = sdlevent.resize.h;
last_resize_time = SDL_GetTicks();
break;
default:
break;
}
}
}
// Warp the mouse back to the middle of the screen
static void CenterMouse(void)
{
// Warp the the screen center
SDL_WarpMouse(screen->w / 2, screen->h / 2);
// Clear any relative movement caused by warping
SDL_PumpEvents();
#if SDL_VERSION_ATLEAST(1, 3, 0)
SDL_GetRelativeMouseState(0, NULL, NULL);
#else
SDL_GetRelativeMouseState(NULL, NULL);
#endif
}
//
// Read the change in mouse state to generate mouse motion events
//
// This is to combine all mouse movement for a tic into one mouse
// motion event.
static void I_ReadMouse(void)
{
int x, y;
event_t ev;
#if SDL_VERSION_ATLEAST(1, 3, 0)
SDL_GetRelativeMouseState(0, &x, &y);
#else
SDL_GetRelativeMouseState(&x, &y);
#endif
if (x != 0 || y != 0)
{
ev.type = ev_mouse;
ev.data1 = mouse_button_state;
ev.data2 = AccelerateMouse(x);
if (!novert)
{
ev.data3 = -AccelerateMouse(y);
}
else
{
ev.data3 = 0;
}
D_PostEvent(&ev);
}
if (MouseShouldBeGrabbed())
{
CenterMouse();
}
}
//
// I_StartTic
//
void I_StartTic (void)
{
if (!initialized)
{
return;
}
I_GetEvent();
if (usemouse && !nomouse)
{
I_ReadMouse();
}
I_UpdateJoystick();
}
//
// I_UpdateNoBlit
//
void I_UpdateNoBlit (void)
{
// what is this?
}
static void UpdateGrab(void)
{
static boolean currently_grabbed = false;
boolean grab;
grab = MouseShouldBeGrabbed();
if (screensaver_mode)
{
// Hide the cursor in screensaver mode
SetShowCursor(false);
}
else if (grab && !currently_grabbed)
{
SetShowCursor(false);
CenterMouse();
}
else if (!grab && currently_grabbed)
{
SetShowCursor(true);
// When releasing the mouse from grab, warp the mouse cursor to
// the bottom-right of the screen. This is a minimally distracting
// place for it to appear - we may only have released the grab
// because we're at an end of level intermission screen, for
// example.
SDL_WarpMouse(screen->w - 16, screen->h - 16);
SDL_GetRelativeMouseState(NULL, NULL);
}
currently_grabbed = grab;
}
// Update a small portion of the screen
//
// Does stretching and buffer blitting if neccessary
//
// Return true if blit was successful.
static boolean BlitArea(int x1, int y1, int x2, int y2)
{
int x_offset, y_offset;
boolean result;
// No blit needed on native surface
if (native_surface)
{
return true;
}
x_offset = (screenbuffer->w - screen_mode->width) / 2;
y_offset = (screenbuffer->h - screen_mode->height) / 2;
if (SDL_LockSurface(screenbuffer) >= 0)
{
I_InitScale(I_VideoBuffer,
(byte *) screenbuffer->pixels
+ (y_offset * screenbuffer->pitch)
+ x_offset,
screenbuffer->pitch);
result = screen_mode->DrawScreen(x1, y1, x2, y2);
SDL_UnlockSurface(screenbuffer);
}
else
{
result = false;
}
return result;
}
static void UpdateRect(int x1, int y1, int x2, int y2)
{
int x1_scaled, x2_scaled, y1_scaled, y2_scaled;
// Do stretching and blitting
if (BlitArea(x1, y1, x2, y2))
{
// Update the area
x1_scaled = (x1 * screen_mode->width) / SCREENWIDTH;
y1_scaled = (y1 * screen_mode->height) / SCREENHEIGHT;
x2_scaled = (x2 * screen_mode->width) / SCREENWIDTH;
y2_scaled = (y2 * screen_mode->height) / SCREENHEIGHT;
SDL_UpdateRect(screen,
x1_scaled, y1_scaled,
x2_scaled - x1_scaled,
y2_scaled - y1_scaled);
}
}
void I_BeginRead(void)
{
byte *screenloc = I_VideoBuffer
+ (SCREENHEIGHT - LOADING_DISK_H) * SCREENWIDTH
+ (SCREENWIDTH - LOADING_DISK_W);
int y;
if (!initialized || disk_image == NULL)
return;
// save background and copy the disk image in
for (y=0; y<LOADING_DISK_H; ++y)
{
memcpy(saved_background + y * LOADING_DISK_W,
screenloc,
LOADING_DISK_W);
memcpy(screenloc,
disk_image + y * LOADING_DISK_W,
LOADING_DISK_W);
screenloc += SCREENWIDTH;
}
UpdateRect(SCREENWIDTH - LOADING_DISK_W, SCREENHEIGHT - LOADING_DISK_H,
SCREENWIDTH, SCREENHEIGHT);
}
void I_EndRead(void)
{
byte *screenloc = I_VideoBuffer
+ (SCREENHEIGHT - LOADING_DISK_H) * SCREENWIDTH
+ (SCREENWIDTH - LOADING_DISK_W);
int y;
if (!initialized || disk_image == NULL)
return;
// save background and copy the disk image in
for (y=0; y<LOADING_DISK_H; ++y)
{
memcpy(screenloc,
saved_background + y * LOADING_DISK_W,
LOADING_DISK_W);
screenloc += SCREENWIDTH;
}
UpdateRect(SCREENWIDTH - LOADING_DISK_W, SCREENHEIGHT - LOADING_DISK_H,
SCREENWIDTH, SCREENHEIGHT);
}
//
// I_FinishUpdate
//
void I_FinishUpdate (void)
{
static int lasttic;
int tics;
int i;
if (!initialized)
return;
if (noblit)
return;
if (need_resize && SDL_GetTicks() > last_resize_time + 500)
{
ApplyWindowResize(resize_w, resize_h);
need_resize = false;
palette_to_set = true;
}
UpdateGrab();
// Don't update the screen if the window isn't visible.
// Not doing this breaks under Windows when we alt-tab away
// while fullscreen.
if (!(SDL_GetAppState() & SDL_APPACTIVE))
return;
// draws little dots on the bottom of the screen
if (display_fps_dots)
{
i = I_GetTime();
tics = i - lasttic;
lasttic = i;
if (tics > 20) tics = 20;
for (i=0 ; i<tics*4 ; i+=4)
I_VideoBuffer[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = 0xff;
for ( ; i<20*4 ; i+=4)
I_VideoBuffer[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = 0x0;
}
// draw to screen
BlitArea(0, 0, SCREENWIDTH, SCREENHEIGHT);
if (palette_to_set)
{
SDL_SetColors(screenbuffer, palette, 0, 256);
palette_to_set = false;
// In native 8-bit mode, if we have a palette to set, the act
// of setting the palette updates the screen
if (screenbuffer == screen)
{
return;
}
}
// In 8in32 mode, we must blit from the fake 8-bit screen buffer
// to the real screen before doing a screen flip.
if (screenbuffer != screen)
{
SDL_Rect dst_rect;
// Center the buffer within the full screen space.
dst_rect.x = (screen->w - screenbuffer->w) / 2;
dst_rect.y = (screen->h - screenbuffer->h) / 2;
SDL_BlitSurface(screenbuffer, NULL, screen, &dst_rect);
}
SDL_Flip(screen);
}
//
// I_ReadScreen
//
void I_ReadScreen (byte* scr)
{
memcpy(scr, I_VideoBuffer, SCREENWIDTH*SCREENHEIGHT);
}
//
// I_SetPalette
//
void I_SetPalette (byte *doompalette)
{
int i;
for (i=0; i<256; ++i)
{
// Zero out the bottom two bits of each channel - the PC VGA
// controller only supports 6 bits of accuracy.
palette[i].r = gammatable[usegamma][*doompalette++] & ~3;
palette[i].g = gammatable[usegamma][*doompalette++] & ~3;
palette[i].b = gammatable[usegamma][*doompalette++] & ~3;
}
palette_to_set = true;
}
// Given an RGB value, find the closest matching palette index.
int I_GetPaletteIndex(int r, int g, int b)
{
int best, best_diff, diff;
int i;
best = 0; best_diff = INT_MAX;
for (i = 0; i < 256; ++i)
{
diff = (r - palette[i].r) * (r - palette[i].r)
+ (g - palette[i].g) * (g - palette[i].g)
+ (b - palette[i].b) * (b - palette[i].b);
if (diff < best_diff)
{
best = i;
best_diff = diff;
}
if (diff == 0)
{
break;
}
}
return best;
}
//
// Set the window title
//
void I_SetWindowTitle(char *title)
{
window_title = title;
}
//
// Call the SDL function to set the window title, based on
// the title set with I_SetWindowTitle.
//
void I_InitWindowTitle(void)
{
char *buf;
buf = M_StringJoin(window_title, " - ", PACKAGE_STRING, NULL);
SDL_WM_SetCaption(buf, NULL);
free(buf);
}
// Set the application icon
void I_InitWindowIcon(void)
{
SDL_Surface *surface;
Uint8 *mask;
int i;
// Generate the mask
mask = malloc(icon_w * icon_h / 8);
memset(mask, 0, icon_w * icon_h / 8);
for (i=0; i<icon_w * icon_h; ++i)
{
if (icon_data[i * 3] != 0x00
|| icon_data[i * 3 + 1] != 0x00
|| icon_data[i * 3 + 2] != 0x00)
{
mask[i / 8] |= 1 << (7 - i % 8);
}
}
surface = SDL_CreateRGBSurfaceFrom(icon_data,
icon_w,
icon_h,
24,
icon_w * 3,
0xff << 0,
0xff << 8,
0xff << 16,
0);
SDL_WM_SetIcon(surface, mask);
SDL_FreeSurface(surface);
free(mask);
}
// Pick the modes list to use:
static void GetScreenModes(screen_mode_t ***modes_list, int *num_modes)
{
if (aspect_ratio_correct)
{
*modes_list = screen_modes_corrected;
*num_modes = arrlen(screen_modes_corrected);
}
else
{
*modes_list = screen_modes;
*num_modes = arrlen(screen_modes);
}
}
// Find which screen_mode_t to use for the given width and height.
static screen_mode_t *I_FindScreenMode(int w, int h)
{
screen_mode_t **modes_list;
screen_mode_t *best_mode;
int modes_list_length;
int num_pixels;
int best_num_pixels;
int i;
// Special case: 320x200 and 640x400 are available even if aspect
// ratio correction is turned on. These modes have non-square
// pixels.
if (fullscreen)
{
if (w == SCREENWIDTH && h == SCREENHEIGHT)
{
return &mode_scale_1x;
}
else if (w == SCREENWIDTH*2 && h == SCREENHEIGHT*2)
{
return &mode_scale_2x;
}
}
GetScreenModes(&modes_list, &modes_list_length);
// Find the biggest screen_mode_t in the list that fits within these
// dimensions
best_mode = NULL;
best_num_pixels = 0;
for (i=0; i<modes_list_length; ++i)
{
// Will this fit within the dimensions? If not, ignore.
if (modes_list[i]->width > w || modes_list[i]->height > h)
{
continue;
}
num_pixels = modes_list[i]->width * modes_list[i]->height;
if (num_pixels > best_num_pixels)
{
// This is a better mode than the current one
best_mode = modes_list[i];
best_num_pixels = num_pixels;
}
}
return best_mode;
}
// Adjust to an appropriate fullscreen mode.
// Returns true if successful.
static boolean AutoAdjustFullscreen(void)
{
SDL_Rect **modes;
SDL_Rect *best_mode;
screen_mode_t *screen_mode;
int diff, best_diff;
int i;
modes = SDL_ListModes(NULL, SDL_FULLSCREEN);
// No fullscreen modes available at all?
if (modes == NULL || modes == (SDL_Rect **) -1 || *modes == NULL)
{
return false;
}
// Find the best mode that matches the mode specified in the
// configuration file
best_mode = NULL;
best_diff = INT_MAX;
for (i=0; modes[i] != NULL; ++i)
{
//printf("%ix%i?\n", modes[i]->w, modes[i]->h);
// What screen_mode_t would be used for this video mode?
screen_mode = I_FindScreenMode(modes[i]->w, modes[i]->h);
// Never choose a screen mode that we cannot run in, or
// is poor quality for fullscreen
if (screen_mode == NULL || screen_mode->poor_quality)
{
// printf("\tUnsupported / poor quality\n");
continue;
}
// Do we have the exact mode?
// If so, no autoadjust needed
if (screen_width == modes[i]->w && screen_height == modes[i]->h)
{
// printf("\tExact mode!\n");
return true;
}
// Is this mode better than the current mode?
diff = (screen_width - modes[i]->w) * (screen_width - modes[i]->w)
+ (screen_height - modes[i]->h) * (screen_height - modes[i]->h);
if (diff < best_diff)
{
// printf("\tA valid mode\n");
best_mode = modes[i];
best_diff = diff;
}
}
if (best_mode == NULL)
{
// Unable to find a valid mode!
return false;
}
printf("I_InitGraphics: %ix%i mode not supported on this machine.\n",
screen_width, screen_height);
screen_width = best_mode->w;
screen_height = best_mode->h;
return true;
}
// Auto-adjust to a valid windowed mode.
static void AutoAdjustWindowed(void)
{
screen_mode_t *best_mode;
// Find a screen_mode_t to fit within the current settings
best_mode = I_FindScreenMode(screen_width, screen_height);
if (best_mode == NULL)
{
// Nothing fits within the current settings.
// Pick the closest to 320x200 possible.
best_mode = I_FindScreenMode(SCREENWIDTH, SCREENHEIGHT_4_3);
}
// Switch to the best mode if necessary.
if (best_mode->width != screen_width || best_mode->height != screen_height)
{
printf("I_InitGraphics: Cannot run at specified mode: %ix%i\n",
screen_width, screen_height);
screen_width = best_mode->width;
screen_height = best_mode->height;
}
}
// Auto-adjust to a valid color depth.
static void AutoAdjustColorDepth(void)
{
SDL_Rect **modes;
SDL_PixelFormat format;
const SDL_VideoInfo *info;
int flags;
// If screen_bpp=0, we should use the current (default) pixel depth.
// Fetch it from SDL.
if (screen_bpp == 0)
{
info = SDL_GetVideoInfo();
if (info != NULL && info->vfmt != NULL)
{
screen_bpp = info->vfmt->BitsPerPixel;
}
}
if (fullscreen)
{
flags = SDL_FULLSCREEN;
}
else
{
flags = 0;
}
format.BitsPerPixel = screen_bpp;
format.BytesPerPixel = (screen_bpp + 7) / 8;
// Are any screen modes supported at the configured color depth?
modes = SDL_ListModes(&format, flags);
// If not, we must autoadjust to something sensible.
if (modes == NULL)
{
printf("I_InitGraphics: %ibpp color depth not supported.\n",
screen_bpp);
info = SDL_GetVideoInfo();
if (info != NULL && info->vfmt != NULL)
{
screen_bpp = info->vfmt->BitsPerPixel;
}
}
}
// If the video mode set in the configuration file is not available,
// try to choose a different mode.
static void I_AutoAdjustSettings(void)
{
int old_screen_w, old_screen_h, old_screen_bpp;
old_screen_w = screen_width;
old_screen_h = screen_height;
old_screen_bpp = screen_bpp;
// Possibly adjust color depth.
AutoAdjustColorDepth();
// If we are running fullscreen, try to autoadjust to a valid fullscreen
// mode. If this is impossible, switch to windowed.
if (fullscreen && !AutoAdjustFullscreen())
{
fullscreen = 0;
}
// If we are running windowed, pick a valid window size.
if (!fullscreen)
{
AutoAdjustWindowed();
}
// Have the settings changed? Show a message.
if (screen_width != old_screen_w || screen_height != old_screen_h
|| screen_bpp != old_screen_bpp)
{
printf("I_InitGraphics: Auto-adjusted to %ix%ix%ibpp.\n",
screen_width, screen_height, screen_bpp);
printf("NOTE: Your video settings have been adjusted. "
"To disable this behavior,\n"
"set autoadjust_video_settings to 0 in your "
"configuration file.\n");
}
}
// Set video size to a particular scale factor (1x, 2x, 3x, etc.)
static void SetScaleFactor(int factor)
{
int w, h;
// Pick 320x200 or 320x240, depending on aspect ratio correct
if (aspect_ratio_correct)
{
w = SCREENWIDTH;
h = SCREENHEIGHT_4_3;
}
else
{
w = SCREENWIDTH;
h = SCREENHEIGHT;
}
screen_width = w * factor;
screen_height = h * factor;
}
void I_GraphicsCheckCommandLine(void)
{
int i;
//!
// @vanilla
//
// Disable blitting the screen.
//
noblit = M_CheckParm ("-noblit");
//!
// @category video
//
// Grab the mouse when running in windowed mode.
//
if (M_CheckParm("-grabmouse"))
{
grabmouse = true;
}
//!
// @category video
//
// Don't grab the mouse when running in windowed mode.
//
if (M_CheckParm("-nograbmouse"))
{
grabmouse = false;
}
// default to fullscreen mode, allow override with command line
// nofullscreen because we love prboom
//!
// @category video
//
// Run in a window.
//
if (M_CheckParm("-window") || M_CheckParm("-nofullscreen"))
{
fullscreen = false;
}
//!
// @category video
//
// Run in fullscreen mode.
//
if (M_CheckParm("-fullscreen"))
{
fullscreen = true;
}
//!
// @category video
//
// Disable the mouse.
//
nomouse = M_CheckParm("-nomouse") > 0;
//!
// @category video
// @arg <x>
//
// Specify the screen width, in pixels.
//
i = M_CheckParmWithArgs("-width", 1);
if (i > 0)
{
screen_width = atoi(myargv[i + 1]);
}
//!
// @category video
// @arg <y>
//
// Specify the screen height, in pixels.
//
i = M_CheckParmWithArgs("-height", 1);
if (i > 0)
{
screen_height = atoi(myargv[i + 1]);
}
//!
// @category video
// @arg <bpp>
//
// Specify the color depth of the screen, in bits per pixel.
//
i = M_CheckParmWithArgs("-bpp", 1);
if (i > 0)
{
screen_bpp = atoi(myargv[i + 1]);
}
// Because we love Eternity:
//!
// @category video
//
// Set the color depth of the screen to 32 bits per pixel.
//
if (M_CheckParm("-8in32"))
{
screen_bpp = 32;
}
//!
// @category video
// @arg <WxY>[wf]
//
// Specify the dimensions of the window or fullscreen mode. An
// optional letter of w or f appended to the dimensions selects
// windowed or fullscreen mode.
i = M_CheckParmWithArgs("-geometry", 1);
if (i > 0)
{
int w, h, s;
char f;
s = sscanf(myargv[i + 1], "%ix%i%1c", &w, &h, &f);
if (s == 2 || s == 3)
{
screen_width = w;
screen_height = h;
if (s == 3 && f == 'f')
{
fullscreen = true;
}
else if (s == 3 && f == 'w')
{
fullscreen = false;
}
}
}
//!
// @category video
//
// Don't scale up the screen.
//
if (M_CheckParm("-1"))
{
SetScaleFactor(1);
}
//!
// @category video
//
// Double up the screen to 2x its normal size.
//
if (M_CheckParm("-2"))
{
SetScaleFactor(2);
}
//!
// @category video
//
// Double up the screen to 3x its normal size.
//
if (M_CheckParm("-3"))
{
SetScaleFactor(3);
}
//!
// @category video
//
// Disable vertical mouse movement.
//
if (M_CheckParm("-novert"))
{
novert = true;
}
//!
// @category video
//
// Enable vertical mouse movement.
//
if (M_CheckParm("-nonovert"))
{
novert = false;
}
}
// Check if we have been invoked as a screensaver by xscreensaver.
void I_CheckIsScreensaver(void)
{
char *env;
env = getenv("XSCREENSAVER_WINDOW");
if (env != NULL)
{
screensaver_mode = true;
}
}
static void CreateCursors(void)
{
static Uint8 empty_cursor_data = 0;
// Save the default cursor so it can be recalled later
cursors[1] = SDL_GetCursor();
// Create an empty cursor
cursors[0] = SDL_CreateCursor(&empty_cursor_data,
&empty_cursor_data,
1, 1, 0, 0);
}
static void SetSDLVideoDriver(void)
{
// Allow a default value for the SDL video driver to be specified
// in the configuration file.
if (strcmp(video_driver, "") != 0)
{
char *env_string;
env_string = M_StringJoin("SDL_VIDEODRIVER=", video_driver, NULL);
putenv(env_string);
free(env_string);
}
}
static void SetWindowPositionVars(void)
{
char buf[64];
int x, y;
if (window_position == NULL || !strcmp(window_position, ""))
{
return;
}
if (!strcmp(window_position, "center"))
{
putenv("SDL_VIDEO_CENTERED=1");
}
else if (sscanf(window_position, "%i,%i", &x, &y) == 2)
{
M_snprintf(buf, sizeof(buf), "SDL_VIDEO_WINDOW_POS=%i,%i", x, y);
putenv(buf);
}
}
static char *WindowBoxType(screen_mode_t *mode, int w, int h)
{
if (mode->width != w && mode->height != h)
{
return "Windowboxed";
}
else if (mode->width == w)
{
return "Letterboxed";
}
else if (mode->height == h)
{
return "Pillarboxed";
}
else
{
return "...";
}
}
static void SetVideoMode(screen_mode_t *mode, int w, int h)
{
byte *doompal;
int flags = 0;
doompal = W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE);
// If we are already running and in a true color mode, we need
// to free the screenbuffer surface before setting the new mode.
if (screenbuffer != NULL && screen != screenbuffer)
{
SDL_FreeSurface(screenbuffer);
}
// Generate lookup tables before setting the video mode.
if (mode != NULL && mode->InitMode != NULL)
{
mode->InitMode(doompal);
}
// Set the video mode.
flags |= SDL_SWSURFACE | SDL_DOUBLEBUF;
if (screen_bpp == 8)
{
flags |= SDL_HWPALETTE;
}
if (fullscreen)
{
flags |= SDL_FULLSCREEN;
}
else
{
// In windowed mode, the window can be resized while the game is
// running. This feature is disabled on OS X, as it adds an ugly
// scroll handle to the corner of the screen.
#ifndef __MACOSX__
flags |= SDL_RESIZABLE;
#endif
}
screen = SDL_SetVideoMode(w, h, screen_bpp, flags);
if (screen == NULL)
{
I_Error("Error setting video mode %ix%ix%ibpp: %s\n",
w, h, screen_bpp, SDL_GetError());
}
// Blank out the full screen area in case there is any junk in
// the borders that won't otherwise be overwritten.
SDL_FillRect(screen, NULL, 0);
// If mode was not set, it must be set now that we know the
// screen size.
if (mode == NULL)
{
mode = I_FindScreenMode(screen->w, screen->h);
if (mode == NULL)
{
I_Error("I_InitGraphics: Unable to find a screen mode small "
"enough for %ix%i", screen->w, screen->h);
}
// Generate lookup tables before setting the video mode.
if (mode->InitMode != NULL)
{
mode->InitMode(doompal);
}
}
// Create the screenbuffer surface; if we have a real 8-bit palettized
// screen, then we can use the screen as the screenbuffer.
if (screen->format->BitsPerPixel == 8)
{
screenbuffer = screen;
}
else
{
screenbuffer = SDL_CreateRGBSurface(SDL_SWSURFACE,
mode->width, mode->height, 8,
0, 0, 0, 0);
SDL_FillRect(screenbuffer, NULL, 0);
}
// Save screen mode.
screen_mode = mode;
}
static void ApplyWindowResize(unsigned int w, unsigned int h)
{
screen_mode_t *mode;
// Find the biggest screen mode that will fall within these
// dimensions, falling back to the smallest mode possible if
// none is found.
mode = I_FindScreenMode(w, h);
if (mode == NULL)
{
mode = I_FindScreenMode(SCREENWIDTH, SCREENHEIGHT);
}
// Reset mode to resize window.
printf("Resize to %ix%i\n", mode->width, mode->height);
SetVideoMode(mode, mode->width, mode->height);
// Save settings.
screen_width = mode->width;
screen_height = mode->height;
}
void I_InitGraphics(void)
{
SDL_Event dummy;
byte *doompal;
char *env;
// Pass through the XSCREENSAVER_WINDOW environment variable to
// SDL_WINDOWID, to embed the SDL window into the Xscreensaver
// window.
env = getenv("XSCREENSAVER_WINDOW");
if (env != NULL)
{
char winenv[30];
int winid;
sscanf(env, "0x%x", &winid);
M_snprintf(winenv, sizeof(winenv), "SDL_WINDOWID=%i", winid);
putenv(winenv);
}
SetSDLVideoDriver();
SetWindowPositionVars();
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
I_Error("Failed to initialize video: %s", SDL_GetError());
}
// Set up title and icon. Windows cares about the ordering; this
// has to be done before the call to SDL_SetVideoMode.
I_InitWindowTitle();
#if !SDL_VERSION_ATLEAST(1, 3, 0)
I_InitWindowIcon();
#endif
// Warning to OS X users... though they might never see it :(
#ifdef __MACOSX__
if (fullscreen)
{
printf("Some old versions of OS X might crash in fullscreen mode.\n"
"If this happens to you, switch back to windowed mode.\n");
}
#endif
//
// Enter into graphics mode.
//
// When in screensaver mode, run full screen and auto detect
// screen dimensions (don't change video mode)
//
if (screensaver_mode)
{
SetVideoMode(NULL, 0, 0);
}
else
{
int w, h;
if (autoadjust_video_settings)
{
I_AutoAdjustSettings();
}
w = screen_width;
h = screen_height;
screen_mode = I_FindScreenMode(w, h);
if (screen_mode == NULL)
{
I_Error("I_InitGraphics: Unable to find a screen mode small "
"enough for %ix%i", w, h);
}
if (w != screen_mode->width || h != screen_mode->height)
{
printf("I_InitGraphics: %s (%ix%i within %ix%i)\n",
WindowBoxType(screen_mode, w, h),
screen_mode->width, screen_mode->height, w, h);
}
SetVideoMode(screen_mode, w, h);
}
// Start with a clear black screen
// (screen will be flipped after we set the palette)
SDL_FillRect(screenbuffer, NULL, 0);
// Set the palette
doompal = W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE);
I_SetPalette(doompal);
SDL_SetColors(screenbuffer, palette, 0, 256);
CreateCursors();
UpdateFocus();
UpdateGrab();
// On some systems, it takes a second or so for the screen to settle
// after changing modes. We include the option to add a delay when
// setting the screen mode, so that the game doesn't start immediately
// with the player unable to see anything.
if (fullscreen && !screensaver_mode)
{
SDL_Delay(startup_delay);
}
// Check if we have a native surface we can use
// If we have to lock the screen, draw to a buffer and copy
// Likewise if the screen pitch is not the same as the width
// If we have to multiply, drawing is done to a separate 320x200 buf
native_surface = screen == screenbuffer
&& !SDL_MUSTLOCK(screen)
&& screen_mode == &mode_scale_1x
&& screen->pitch == SCREENWIDTH
&& aspect_ratio_correct;
// If not, allocate a buffer and copy from that buffer to the
// screen when we do an update
if (native_surface)
{
I_VideoBuffer = (unsigned char *) screen->pixels;
I_VideoBuffer += (screen->h - SCREENHEIGHT) / 2;
}
else
{
I_VideoBuffer = (unsigned char *) Z_Malloc (SCREENWIDTH * SCREENHEIGHT,
PU_STATIC, NULL);
}
V_RestoreBuffer();
// Clear the screen to black.
memset(I_VideoBuffer, 0, SCREENWIDTH * SCREENHEIGHT);
// We need SDL to give us translated versions of keys as well
SDL_EnableUNICODE(1);
// Repeat key presses - this is what Vanilla Doom does
// Not sure about repeat rate - probably dependent on which DOS
// driver is used. This is good enough though.
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
// clear out any events waiting at the start and center the mouse
while (SDL_PollEvent(&dummy));
initialized = true;
// Call I_ShutdownGraphics on quit
I_AtExit(I_ShutdownGraphics, true);
}
// Bind all variables controlling video options into the configuration
// file system.
void I_BindVideoVariables(void)
{
M_BindVariable("use_mouse", &usemouse);
M_BindVariable("autoadjust_video_settings", &autoadjust_video_settings);
M_BindVariable("fullscreen", &fullscreen);
M_BindVariable("aspect_ratio_correct", &aspect_ratio_correct);
M_BindVariable("startup_delay", &startup_delay);
M_BindVariable("screen_width", &screen_width);
M_BindVariable("screen_height", &screen_height);
M_BindVariable("screen_bpp", &screen_bpp);
M_BindVariable("grabmouse", &grabmouse);
M_BindVariable("mouse_acceleration", &mouse_acceleration);
M_BindVariable("mouse_threshold", &mouse_threshold);
M_BindVariable("video_driver", &video_driver);
M_BindVariable("window_position", &window_position);
M_BindVariable("usegamma", &usegamma);
M_BindVariable("vanilla_keyboard_mapping", &vanilla_keyboard_mapping);
M_BindVariable("novert", &novert);
M_BindVariable("png_screenshots", &png_screenshots);
// Windows Vista or later? Set screen color depth to
// 32 bits per pixel, as 8-bit palettized screen modes
// don't work properly in recent versions.
#if defined(_WIN32) && !defined(_WIN32_WCE)
{
OSVERSIONINFOEX version_info;
ZeroMemory(&version_info, sizeof(OSVERSIONINFOEX));
version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
GetVersionEx((OSVERSIONINFO *) &version_info);
if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT
&& version_info.dwMajorVersion >= 6)
{
screen_bpp = 32;
}
}
#endif
// Disable fullscreen by default on OS X, as there is an SDL bug
// where some old versions of OS X (<= Snow Leopard) crash.
#ifdef __MACOSX__
fullscreen = 0;
screen_width = 800;
screen_height = 600;
#endif
}