qzdoom/src/win32/win32video.cpp

809 lines
20 KiB
C++
Raw Normal View History

/*
** win32video.cpp
2006-11-19 02:10:25 +00:00
** Code to let ZDoom draw to the screen
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
2006-11-19 02:10:25 +00:00
#ifdef _DEBUG
#define D3D_DEBUG_INFO
#endif
#define DIRECTDRAW_VERSION 0x0300
#define DIRECT3D_VERSION 0x0900
#define _WIN32_WINNT 0x0501
2006-11-19 02:10:25 +00:00
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <mmsystem.h>
2006-11-19 02:10:25 +00:00
#include <ddraw.h>
#include <d3d9.h>
// HEADER FILES ------------------------------------------------------------
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <ddraw.h>
2006-11-19 02:10:25 +00:00
#include <d3d9.h>
#include <stdio.h>
#include <ctype.h>
#define USE_WINDOWS_DWORD
#include "doomtype.h"
#include "c_dispatch.h"
#include "templates.h"
#include "i_system.h"
#include "i_video.h"
#include "v_video.h"
#include "v_pfx.h"
#include "stats.h"
#include "doomerrors.h"
2008-09-15 00:47:31 +00:00
#include "m_argv.h"
#include "r_defs.h"
#include "v_text.h"
#include "r_swrenderer.h"
#include "win32iface.h"
// MACROS ------------------------------------------------------------------
// TYPES -------------------------------------------------------------------
2006-11-19 02:10:25 +00:00
IMPLEMENT_ABSTRACT_CLASS(BaseWinFB)
typedef IDirect3D9 *(WINAPI *DIRECT3DCREATE9FUNC)(UINT SDKVersion);
typedef HRESULT (WINAPI *DIRECTDRAWCREATEFUNC)(GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter);
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
void DoBlending (const PalEntry *from, PalEntry *to, int count, int r, int g, int b, int a);
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
static void StopFPSLimit();
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern HWND Window;
extern IVideo *Video;
extern BOOL AppActive;
extern int SessionState;
extern bool FullscreenReset;
extern bool VidResizing;
EXTERN_CVAR (Bool, fullscreen)
EXTERN_CVAR (Float, Gamma)
EXTERN_CVAR (Bool, cl_capfps)
// PRIVATE DATA DEFINITIONS ------------------------------------------------
2006-11-19 02:10:25 +00:00
static HMODULE D3D9_dll;
static HMODULE DDraw_dll;
static UINT FPSLimitTimer;
// PUBLIC DATA DEFINITIONS -------------------------------------------------
2006-11-19 02:10:25 +00:00
IDirectDraw2 *DDraw;
IDirect3D9 *D3D;
IDirect3DDevice9 *D3Device;
HANDLE FPSLimitEvent;
2006-11-19 02:10:25 +00:00
CVAR (Bool, vid_forceddraw, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR (Int, vid_adapter, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CUSTOM_CVAR (Int, vid_maxfps, 200, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (vid_maxfps < TICRATE && vid_maxfps != 0)
{
vid_maxfps = TICRATE;
}
else if (vid_maxfps > 1000)
{
vid_maxfps = 1000;
}
else if (cl_capfps == 0)
{
I_SetFPSLimit(vid_maxfps);
}
}
2006-11-19 02:10:25 +00:00
#if VID_FILE_DEBUG
FILE *dbg;
#endif
2006-11-19 02:10:25 +00:00
// CODE --------------------------------------------------------------------
Win32Video::Win32Video (int parm)
: m_Modes (NULL),
m_IsFullscreen (false),
m_Adapter (D3DADAPTER_DEFAULT)
{
I_SetWndProc();
2006-11-19 02:10:25 +00:00
if (!InitD3D9())
{
2006-11-19 02:10:25 +00:00
InitDDraw();
}
}
2006-11-19 02:10:25 +00:00
Win32Video::~Win32Video ()
{
2006-11-19 02:10:25 +00:00
FreeModes ();
if (DDraw != NULL)
{
if (m_IsFullscreen)
{
DDraw->SetCooperativeLevel (NULL, DDSCL_NORMAL);
}
DDraw->Release();
DDraw = NULL;
}
if (D3D != NULL)
{
2006-11-19 02:10:25 +00:00
D3D->Release();
D3D = NULL;
}
2006-11-19 02:10:25 +00:00
STOPLOG;
}
2006-11-19 02:10:25 +00:00
bool Win32Video::InitD3D9 ()
{
2006-11-19 02:10:25 +00:00
DIRECT3DCREATE9FUNC direct3d_create_9;
if (vid_forceddraw)
{
2006-11-19 02:10:25 +00:00
return false;
}
2006-11-19 02:10:25 +00:00
// Load the Direct3D 9 library.
if ((D3D9_dll = LoadLibraryA ("d3d9.dll")) == NULL)
{
return false;
}
// Obtain an IDirect3D interface.
2006-11-19 02:10:25 +00:00
if ((direct3d_create_9 = (DIRECT3DCREATE9FUNC)GetProcAddress (D3D9_dll, "Direct3DCreate9")) == NULL)
{
goto closelib;
}
if ((D3D = direct3d_create_9 (D3D_SDK_VERSION)) == NULL)
{
goto closelib;
}
// Select adapter.
m_Adapter = (vid_adapter < 1 || (UINT)vid_adapter > D3D->GetAdapterCount())
? D3DADAPTER_DEFAULT : (UINT)vid_adapter - 1u;
2006-11-19 02:10:25 +00:00
// Check that we have at least PS 1.4 available.
D3DCAPS9 devcaps;
if (FAILED(D3D->GetDeviceCaps (m_Adapter, D3DDEVTYPE_HAL, &devcaps)))
2006-11-19 02:10:25 +00:00
{
goto d3drelease;
}
if ((devcaps.PixelShaderVersion & 0xFFFF) < 0x104)
{
goto d3drelease;
}
if (!(devcaps.Caps2 & D3DCAPS2_DYNAMICTEXTURES))
{
goto d3drelease;
}
// Enumerate available display modes.
FreeModes ();
AddD3DModes (m_Adapter, D3DFMT_X8R8G8B8);
AddD3DModes (m_Adapter, D3DFMT_R5G6B5);
if (Args->CheckParm ("-2"))
{ // Force all modes to be pixel-doubled.
ScaleModes (1);
}
else if (Args->CheckParm ("-4"))
{ // Force all modes to be pixel-quadrupled.
ScaleModes (2);
}
else
{
AddLowResModes ();
}
2006-11-19 02:10:25 +00:00
AddLetterboxModes ();
if (m_Modes == NULL)
{ // Too bad. We didn't find any modes for D3D9. We probably won't find any
// for DDraw either...
goto d3drelease;
}
return true;
d3drelease:
D3D->Release();
D3D = NULL;
closelib:
FreeLibrary (D3D9_dll);
return false;
}
void Win32Video::InitDDraw ()
{
DIRECTDRAWCREATEFUNC directdraw_create;
LPDIRECTDRAW ddraw1;
STARTLOG;
HRESULT dderr;
// Load the DirectDraw library.
if ((DDraw_dll = LoadLibraryA ("ddraw.dll")) == NULL)
{
I_FatalError ("Could not load ddraw.dll");
}
// Obtain an IDirectDraw interface.
if ((directdraw_create = (DIRECTDRAWCREATEFUNC)GetProcAddress (DDraw_dll, "DirectDrawCreate")) == NULL)
{
I_FatalError ("The system file ddraw.dll is missing the DirectDrawCreate export");
}
dderr = directdraw_create (NULL, &ddraw1, NULL);
if (FAILED(dderr))
I_FatalError ("Could not create DirectDraw object: %08lx", dderr);
dderr = ddraw1->QueryInterface (IID_IDirectDraw2, (LPVOID*)&DDraw);
if (FAILED(dderr))
{
ddraw1->Release ();
DDraw = NULL;
I_FatalError ("Could not initialize IDirectDraw2 interface: %08lx", dderr);
}
// Okay, we have the IDirectDraw2 interface now, so we can release the
// really old-fashioned IDirectDraw one.
ddraw1->Release ();
DDraw->SetCooperativeLevel (Window, DDSCL_NORMAL);
FreeModes ();
dderr = DDraw->EnumDisplayModes (0, NULL, this, EnumDDModesCB);
if (FAILED(dderr))
{
DDraw->Release ();
DDraw = NULL;
I_FatalError ("Could not enumerate display modes: %08lx", dderr);
}
if (m_Modes == NULL)
{
DDraw->Release ();
DDraw = NULL;
I_FatalError ("DirectDraw returned no display modes.\n\n"
"If you started " GAMENAME " from a fullscreen DOS box, run it from "
"a DOS window instead. If that does not work, you may need to reboot.");
}
if (Args->CheckParm ("-2"))
{ // Force all modes to be pixel-doubled.
ScaleModes(1);
}
else if (Args->CheckParm ("-4"))
{ // Force all modes to be pixel-quadrupled.
ScaleModes(2);
}
else
{
if (OSPlatform == os_Win95)
{
// Windows 95 will let us use Mode X. If we didn't find any linear
// modes in the loop above, add the Mode X modes here.
AddMode (320, 200, 8, 200, 0);
AddMode (320, 240, 8, 240, 0);
}
AddLowResModes ();
}
2006-11-19 02:10:25 +00:00
AddLetterboxModes ();
}
// Returns true if fullscreen, false otherwise
bool Win32Video::GoFullscreen (bool yes)
{
static const char *const yestypes[2] = { "windowed", "fullscreen" };
HRESULT hr[2];
int count;
2006-11-19 02:10:25 +00:00
// FIXME: Do this right for D3D. (This function is only called by the movie player when using D3D.)
if (D3D != NULL)
{
return yes;
}
if (m_IsFullscreen == yes)
return yes;
for (count = 0; count < 2; ++count)
{
LOG1 ("fullscreen: %d\n", yes);
hr[count] = DDraw->SetCooperativeLevel (Window, !yes ? DDSCL_NORMAL :
DDSCL_ALLOWMODEX | DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
if (SUCCEEDED(hr[count]))
{
if (count != 0)
{
// Ack! Cannot print because the screen does not exist right now.
// Printf ("Setting %s mode failed. Error %08lx\n",
// yestypes[!yes], hr[0]);
}
m_IsFullscreen = yes;
return yes;
}
yes = !yes;
}
I_FatalError ("Could not set %s mode: %08lx\n"
"Could not set %s mode: %08lx\n",
yestypes[yes], hr[0], yestypes[!yes], hr[1]);
// Appease the compiler, even though we never return if we get here.
return false;
}
// Flips to the GDI surface and clears it; used by the movie player
void Win32Video::BlankForGDI ()
{
2006-11-19 02:10:25 +00:00
static_cast<BaseWinFB *> (screen)->Blank ();
}
//==========================================================================
//
// Win32Video :: DumpAdapters
//
// Dumps the list of display adapters to the console. Only meaningful for
// Direct3D.
//
//==========================================================================
void Win32Video::DumpAdapters()
{
if (D3D == NULL)
{
Printf("Multi-monitor support requires Direct3D.\n");
return;
}
UINT num_adapters = D3D->GetAdapterCount();
for (UINT i = 0; i < num_adapters; ++i)
{
D3DADAPTER_IDENTIFIER9 ai;
char moreinfo[64] = "";
if (FAILED(D3D->GetAdapterIdentifier(i, 0, &ai)))
{
continue;
}
// Strip trailing whitespace from adapter description.
for (char *p = ai.Description + strlen(ai.Description) - 1;
p >= ai.Description && isspace(*p);
--p)
{
*p = '\0';
}
HMONITOR hm = D3D->GetAdapterMonitor(i);
MONITORINFOEX mi;
mi.cbSize = sizeof(mi);
if (GetMonitorInfo(hm, &mi))
{
mysnprintf(moreinfo, countof(moreinfo), " [%ldx%ld @ (%ld,%ld)]%s",
mi.rcMonitor.right - mi.rcMonitor.left,
mi.rcMonitor.bottom - mi.rcMonitor.top,
mi.rcMonitor.left, mi.rcMonitor.top,
mi.dwFlags & MONITORINFOF_PRIMARY ? " (Primary)" : "");
}
Printf("%s%u. %s%s\n",
i == m_Adapter ? TEXTCOLOR_BOLD : "",
i + 1, ai.Description, moreinfo);
}
}
// Mode enumeration --------------------------------------------------------
HRESULT WINAPI Win32Video::EnumDDModesCB (LPDDSURFACEDESC desc, void *data)
{
((Win32Video *)data)->AddMode (desc->dwWidth, desc->dwHeight, 8, desc->dwHeight, 0);
2006-11-19 02:10:25 +00:00
return DDENUMRET_OK;
}
void Win32Video::AddD3DModes (UINT adapter, D3DFORMAT format)
2006-11-19 02:10:25 +00:00
{
UINT modecount, i;
D3DDISPLAYMODE mode;
modecount = D3D->GetAdapterModeCount (adapter, format);
2006-11-19 02:10:25 +00:00
for (i = 0; i < modecount; ++i)
{
if (D3D_OK == D3D->EnumAdapterModes (adapter, format, i, &mode))
2006-11-19 02:10:25 +00:00
{
AddMode (mode.Width, mode.Height, 8, mode.Height, 0);
}
}
}
//==========================================================================
//
// Win32Video :: AddLowResModes
//
// Recent NVidia drivers no longer support resolutions below 640x480, even
// if you try to add them as a custom resolution. With D3DFB, pixel doubling
// is quite easy to do and hardware-accelerated. If you have 1280x800, then
// you can have 320x200, but don't be surprised if it shows up as widescreen
// on a widescreen monitor, since that's what it is.
//
//==========================================================================
void Win32Video::AddLowResModes()
{
ModeInfo *mode, *nextmode;
for (mode = m_Modes; mode != NULL; mode = nextmode)
{
nextmode = mode->next;
if (mode->realheight == mode->height &&
mode->doubling == 0 &&
mode->height >= 200*2 &&
mode->height <= 480*2 &&
mode->width >= 320*2 &&
mode->width <= 640*2)
{
AddMode (mode->width / 2, mode->height / 2, mode->bits, mode->height / 2, 1);
}
}
for (mode = m_Modes; mode != NULL; mode = nextmode)
{
nextmode = mode->next;
if (mode->realheight == mode->height &&
mode->doubling == 0 &&
mode->height >= 200*4 &&
mode->height <= 480*4 &&
mode->width >= 320*4 &&
mode->width <= 640*4)
{
AddMode (mode->width / 4, mode->height / 4, mode->bits, mode->height / 4, 2);
2006-11-19 02:10:25 +00:00
}
}
2006-11-19 02:10:25 +00:00
}
2006-11-19 02:10:25 +00:00
// Add 16:9 and 16:10 resolutions you can use in a window or letterboxed
void Win32Video::AddLetterboxModes ()
{
ModeInfo *mode, *nextmode;
for (mode = m_Modes; mode != NULL; mode = nextmode)
{
nextmode = mode->next;
if (mode->realheight == mode->height && mode->height * 4/3 == mode->width)
{
if (mode->width >= 360)
{
AddMode (mode->width, mode->width * 9/16, mode->bits, mode->height, mode->doubling);
2006-11-19 02:10:25 +00:00
}
if (mode->width > 640)
{
AddMode (mode->width, mode->width * 10/16, mode->bits, mode->height, mode->doubling);
2006-11-19 02:10:25 +00:00
}
}
}
}
void Win32Video::AddMode (int x, int y, int bits, int y2, int doubling)
{
2006-11-19 02:10:25 +00:00
// Reject modes that do not meet certain criteria.
if ((x & 1) != 0 ||
2006-11-19 02:10:25 +00:00
y > MAXHEIGHT ||
x > MAXWIDTH ||
y < 200 ||
x < 320)
{
return;
}
ModeInfo **probep = &m_Modes;
ModeInfo *probe = m_Modes;
// This mode may have been already added to the list because it is
// enumerated multiple times at different refresh rates. If it's
// not present, add it to the right spot in the list; otherwise, do nothing.
// Modes are sorted first by width, then by height, then by depth. In each
// case the order is ascending.
for (; probe != 0; probep = &probe->next, probe = probe->next)
{
if (probe->width > x) break;
if (probe->width < x) continue;
// Width is equal
if (probe->height > y) break;
if (probe->height < y) continue;
// Height is equal
if (probe->bits > bits) break;
if (probe->bits < bits) continue;
// Bits is equal
return;
}
*probep = new ModeInfo (x, y, bits, y2, doubling);
(*probep)->next = probe;
}
void Win32Video::FreeModes ()
{
ModeInfo *mode = m_Modes;
while (mode)
{
ModeInfo *tempmode = mode;
mode = mode->next;
delete tempmode;
}
m_Modes = NULL;
}
// For every mode, set its scaling factor. Modes that end up with too
// small a display area are discarded.
void Win32Video::ScaleModes (int doubling)
{
ModeInfo *mode, **prev;
prev = &m_Modes;
mode = m_Modes;
while (mode != NULL)
{
assert(mode->doubling == 0);
mode->width >>= doubling;
mode->height >>= doubling;
mode->realheight >>= doubling;
mode->doubling = doubling;
if ((mode->width & 7) != 0 || mode->width < 320 || mode->height < 200)
{ // Mode became too small. Delete it.
*prev = mode->next;
delete mode;
}
else
{
prev = &mode->next;
}
mode = *prev;
}
}
Note: I have not tried compiling these recent changes under Linux. I wouldn't be surprised if it doesn't work. - Reorganized the network startup loops so now they are event driven. There is a single function that gets called to drive it, and it uses callbacks to perform the different stages of the synchronization. This lets me have a nice, responsive abort button instead of the previous unannounced hit-escape-to- abort behavior, and I think the rearranged code is slightly easier to understand too. - Increased the number of bytes for version info during D_ArbitrateNetStart(), in preparation for the day when NETGAMEVERSION requires more than one byte. - I noticed an issue with Vista RC1 and the new fatal error setup. Even after releasing a DirectDraw or Direct3D interface, the DWM can still use the last image drawn using them when it composites the window. It doesn't always do it but it does often enough that it is a real problem. At this point, I don't know if it's a problem with the release version of Vista or not. After messing around, I discovered the problem was caused by ~Win32Video() hiding the window and then having it immediately shown soon after. The DWM kept an image of the window to do the transition effect with, and then when it didn't get a chance to do the transition, it didn't properly forget about its saved image and kept plastering it on top of everything else underneath. - Added a network synchronization panel to the window during netgame startup. - Fixed: PClass::CreateDerivedClass() must initialize StateList to NULL. Otherwise, classic DECORATE definitions generate a big, fat crash. - Resurrected the R_Init progress bar, now as a standard Windows control. - Removed the sound failure dialog. The FMOD setup already defaulted to no sound if initialization failed, so this only applies when snd_output is set to "alternate" which now also falls back to no sound. In addition, it wasn't working right, and I didn't feel like fixing it for the probably 0% of users it affected. - Fixed: The edit control used for logging output added text in reverse order on Win9x. - Went back to the roots and made graphics initialization one of the last things to happen during setup. Now the startup text is visible again. More importantly, the main window is no longer created invisible, which seems to cause trouble with it not always appearing in the taskbar. The fatal error dialog is now also embedded in the main window instead of being a separate modal dialog, so you can play with the log window to see any problems that might be reported there. Rather than completely restoring the original startup order, I tried to keep things as close to the way they were with early graphics startup. In particular, V_Init() now creates a dummy screen so that things that need screen dimensions can get them. It gets replaced by the real screen later in I_InitGraphics(). Will need to check this under Linux to make sure it didn't cause any problems there. - Removed the following stubs that just called functions in Video: - I_StartModeIterator() - I_NextMode() - I_DisplayType() I_FullscreenChanged() was also removed, and a new fullscreen parameter was added to IVideo::StartModeIterator(), since that's all it controlled. - Renamed I_InitHardware() back to I_InitGraphics(), since that's all it's initialized post-1.22. SVN r416 (trunk)
2006-12-19 04:09:10 +00:00
void Win32Video::StartModeIterator (int bits, bool fs)
{
m_IteratorMode = m_Modes;
m_IteratorBits = bits;
Note: I have not tried compiling these recent changes under Linux. I wouldn't be surprised if it doesn't work. - Reorganized the network startup loops so now they are event driven. There is a single function that gets called to drive it, and it uses callbacks to perform the different stages of the synchronization. This lets me have a nice, responsive abort button instead of the previous unannounced hit-escape-to- abort behavior, and I think the rearranged code is slightly easier to understand too. - Increased the number of bytes for version info during D_ArbitrateNetStart(), in preparation for the day when NETGAMEVERSION requires more than one byte. - I noticed an issue with Vista RC1 and the new fatal error setup. Even after releasing a DirectDraw or Direct3D interface, the DWM can still use the last image drawn using them when it composites the window. It doesn't always do it but it does often enough that it is a real problem. At this point, I don't know if it's a problem with the release version of Vista or not. After messing around, I discovered the problem was caused by ~Win32Video() hiding the window and then having it immediately shown soon after. The DWM kept an image of the window to do the transition effect with, and then when it didn't get a chance to do the transition, it didn't properly forget about its saved image and kept plastering it on top of everything else underneath. - Added a network synchronization panel to the window during netgame startup. - Fixed: PClass::CreateDerivedClass() must initialize StateList to NULL. Otherwise, classic DECORATE definitions generate a big, fat crash. - Resurrected the R_Init progress bar, now as a standard Windows control. - Removed the sound failure dialog. The FMOD setup already defaulted to no sound if initialization failed, so this only applies when snd_output is set to "alternate" which now also falls back to no sound. In addition, it wasn't working right, and I didn't feel like fixing it for the probably 0% of users it affected. - Fixed: The edit control used for logging output added text in reverse order on Win9x. - Went back to the roots and made graphics initialization one of the last things to happen during setup. Now the startup text is visible again. More importantly, the main window is no longer created invisible, which seems to cause trouble with it not always appearing in the taskbar. The fatal error dialog is now also embedded in the main window instead of being a separate modal dialog, so you can play with the log window to see any problems that might be reported there. Rather than completely restoring the original startup order, I tried to keep things as close to the way they were with early graphics startup. In particular, V_Init() now creates a dummy screen so that things that need screen dimensions can get them. It gets replaced by the real screen later in I_InitGraphics(). Will need to check this under Linux to make sure it didn't cause any problems there. - Removed the following stubs that just called functions in Video: - I_StartModeIterator() - I_NextMode() - I_DisplayType() I_FullscreenChanged() was also removed, and a new fullscreen parameter was added to IVideo::StartModeIterator(), since that's all it controlled. - Renamed I_InitHardware() back to I_InitGraphics(), since that's all it's initialized post-1.22. SVN r416 (trunk)
2006-12-19 04:09:10 +00:00
m_IteratorFS = fs;
}
bool Win32Video::NextMode (int *width, int *height, bool *letterbox)
{
if (m_IteratorMode)
{
while (m_IteratorMode && m_IteratorMode->bits != m_IteratorBits)
{
m_IteratorMode = m_IteratorMode->next;
}
if (m_IteratorMode)
{
*width = m_IteratorMode->width;
*height = m_IteratorMode->height;
if (letterbox != NULL) *letterbox = m_IteratorMode->realheight != m_IteratorMode->height;
m_IteratorMode = m_IteratorMode->next;
return true;
}
}
return false;
}
DFrameBuffer *Win32Video::CreateFrameBuffer (int width, int height, bool fullscreen, DFrameBuffer *old)
{
static int retry = 0;
static int owidth, oheight;
2006-11-19 02:10:25 +00:00
BaseWinFB *fb;
PalEntry flashColor;
int flashAmount;
LOG4 ("CreateFB %d %d %d %p\n", width, height, fullscreen, old);
if (old != NULL)
{ // Reuse the old framebuffer if its attributes are the same
BaseWinFB *fb = static_cast<BaseWinFB *> (old);
if (fb->Width == width &&
fb->Height == height &&
fb->Windowed == !fullscreen)
{
return old;
}
old->GetFlash (flashColor, flashAmount);
old->ObjectFlags |= OF_YesReallyDelete;
if (old == screen) screen = NULL;
delete old;
}
else
{
flashColor = 0;
flashAmount = 0;
}
2006-11-19 02:10:25 +00:00
if (D3D != NULL)
{
fb = new D3DFB (m_Adapter, width, height, fullscreen);
2006-11-19 02:10:25 +00:00
}
else
{
fb = new DDrawFB (width, height, fullscreen);
}
LOG1 ("New fb created @ %p\n", fb);
// If we could not create the framebuffer, try again with slightly
// different parameters in this order:
// 1. Try with the closest size
// 2. Try in the opposite screen mode with the original size
// 3. Try in the opposite screen mode with the closest size
// This is a somewhat confusing mass of recursion here.
while (fb == NULL || !fb->IsValid ())
{
static HRESULT hr;
if (fb != NULL)
{
if (retry == 0)
{
hr = fb->GetHR ();
}
fb->ObjectFlags |= OF_YesReallyDelete;
delete fb;
LOG1 ("fb is bad: %08lx\n", hr);
}
else
{
LOG ("Could not create fb at all\n");
}
screen = NULL;
LOG1 ("Retry number %d\n", retry);
switch (retry)
{
case 0:
owidth = width;
oheight = height;
case 2:
// Try a different resolution. Hopefully that will work.
I_ClosestResolution (&width, &height, 8);
LOG2 ("Retry with size %d,%d\n", width, height);
break;
case 1:
// Try changing fullscreen mode. Maybe that will work.
width = owidth;
height = oheight;
fullscreen = !fullscreen;
LOG1 ("Retry with fullscreen %d\n", fullscreen);
break;
default:
// I give up!
LOG3 ("Could not create new screen (%d x %d): %08lx", owidth, oheight, hr);
I_FatalError ("Could not create new screen (%d x %d): %08lx", owidth, oheight, hr);
}
++retry;
fb = static_cast<DDrawFB *>(CreateFrameBuffer (width, height, fullscreen, NULL));
}
retry = 0;
fb->SetFlash (flashColor, flashAmount);
return fb;
}
void Win32Video::SetWindowedScale (float scale)
{
// FIXME
}
//==========================================================================
//
// SetFPSLimit
//
// Initializes an event timer to fire at a rate of <limit>/sec. The video
// update will wait for this timer to trigger before updating.
//
// Pass 0 as the limit for unlimited.
// Pass a negative value for the limit to use the value of vid_maxfps.
//
//==========================================================================
void I_SetFPSLimit(int limit)
{
if (limit < 0)
{
limit = vid_maxfps;
}
// Kill any leftover timer.
if (FPSLimitTimer != 0)
{
timeKillEvent(FPSLimitTimer);
FPSLimitTimer = 0;
}
if (limit == 0)
{ // no limit
if (FPSLimitEvent != NULL)
{
CloseHandle(FPSLimitEvent);
FPSLimitEvent = NULL;
}
DPrintf("FPS timer disabled\n");
}
else
{
if (FPSLimitEvent == NULL)
{
FPSLimitEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
if (FPSLimitEvent == NULL)
{ // Could not create event, so cannot use timer.
Printf("Failed to create FPS limitter event\n");
return;
}
}
atterm(StopFPSLimit);
// Set timer event as close as we can to limit/sec, in milliseconds.
UINT period = 1000 / limit;
FPSLimitTimer = timeSetEvent(period, 0, (LPTIMECALLBACK)FPSLimitEvent, 0, TIME_PERIODIC | TIME_CALLBACK_EVENT_SET);
if (FPSLimitTimer == 0)
{
CloseHandle(FPSLimitEvent);
FPSLimitEvent = NULL;
Printf("Failed to create FPS limitter timer\n");
return;
}
DPrintf("FPS timer set to %u ms\n", period);
}
}
//==========================================================================
//
// StopFPSLimit
//
// Used for cleanup during application shutdown.
//
//==========================================================================
static void StopFPSLimit()
{
I_SetFPSLimit(0);
}