2216 lines
50 KiB
C
2216 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;
|
|
}
|
|
}
|
|
|
|
|
|
void __attribute__ ((weak)) I_GetEvent(void)
|
|
{
|
|
|
|
}
|
|
|
|
//
|
|
// I_StartFrame
|
|
//
|
|
void I_StartFrame (void)
|
|
{
|
|
I_GetEvent();
|
|
}
|
|
|
|
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
|
|
}
|