diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 97317cfd3..5fe9b18fe 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -987,6 +987,7 @@ set (PCH_SOURCES v_collection.cpp v_draw.cpp v_font.cpp + v_framebuffer.cpp v_palette.cpp v_paltest.cpp v_pfx.cpp diff --git a/src/d_main.cpp b/src/d_main.cpp index 168dce63d..7b22995b4 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -99,6 +99,7 @@ #include "r_data/r_vanillatrans.h" EXTERN_CVAR(Bool, hud_althud) +EXTERN_CVAR(Bool, fullscreen) void DrawHUD(); void D_DoAnonStats(); @@ -673,13 +674,9 @@ void D_Display () // fullscreen toggle has been requested if (setmodeneeded) { - // Change screen mode. - /* - if (Video->ToggleFullscreen()) - { - setsizeneeded = true; - } - */ + screen->ToggleFullscreen(fullscreen); + setsizeneeded = true; + setmodeneeded = false; } // change the view size if needed @@ -696,7 +693,6 @@ void D_Display () R_ExecuteSetViewSize (r_viewpoint, r_viewwindow); } } - setmodeneeded = false; // [RH] Allow temporarily disabling wipes if (NoWipe) diff --git a/src/gl/stereo3d/gl_interleaved3d.cpp b/src/gl/stereo3d/gl_interleaved3d.cpp index e1371bb6d..3ab0b309d 100644 --- a/src/gl/stereo3d/gl_interleaved3d.cpp +++ b/src/gl/stereo3d/gl_interleaved3d.cpp @@ -140,11 +140,13 @@ void CheckerInterleaved3D::Present() const int windowHOffset = 0; #ifdef _WIN32 + /* this needs to be done differently! if (!fullscreen) { I_SaveWindowedPos(); // update win_y CVAR windowHOffset = (win_x + WINDOW_BORDER_HORIZONTAL_PARITY) % 2; windowVOffset = (win_y + WINDOW_BORDER_VERTICAL_PARITY) % 2; } + */ #endif // _WIN32 GLRenderer->mPresent3dCheckerShader->Uniforms->WindowPositionParity = @@ -184,10 +186,12 @@ void ColumnInterleaved3D::Present() const int windowHOffset = 0; #ifdef _WIN32 + /* this needs to be done differently! if (!fullscreen) { I_SaveWindowedPos(); // update win_y CVAR windowHOffset = (win_x + WINDOW_BORDER_HORIZONTAL_PARITY) % 2; } + */ #endif // _WIN32 GLRenderer->mPresent3dColumnShader->Uniforms->WindowPositionParity = windowHOffset; @@ -207,10 +211,12 @@ void RowInterleaved3D::Present() const int windowVOffset = 0; #ifdef _WIN32 + /* this needs to be done differently! if (! fullscreen) { I_SaveWindowedPos(); // update win_y CVAR windowVOffset = (win_y + WINDOW_BORDER_VERTICAL_PARITY) % 2; } + */ #endif // _WIN32 GLRenderer->mPresent3dRowShader->Uniforms->WindowPositionParity = diff --git a/src/posix/cocoa/gl_sysfb.h b/src/posix/cocoa/gl_sysfb.h index 4370aa3c9..ddd000e5c 100644 --- a/src/posix/cocoa/gl_sysfb.h +++ b/src/posix/cocoa/gl_sysfb.h @@ -46,8 +46,9 @@ public: virtual bool IsFullscreen(); virtual void SetVSync(bool vsync); - int GetClientWidth(); - int GetClientHeight(); + int GetClientWidth() override; + int GetClientHeight() override; + void ToggleFullscreen(bool yes) override; protected: bool UpdatePending; diff --git a/src/posix/cocoa/i_video.mm b/src/posix/cocoa/i_video.mm index 7b4cdbf52..1ab1e565a 100644 --- a/src/posix/cocoa/i_video.mm +++ b/src/posix/cocoa/i_video.mm @@ -556,6 +556,12 @@ bool SystemFrameBuffer::IsFullscreen() return CocoaVideo::IsFullscreen(); } +void SystemFrameBuffer::ToggleFullscreen(bool yes) +{ + SetMode(...); // todo +} + + void SystemFrameBuffer::SetVSync(bool vsync) { const GLint value = vsync ? 1 : 0; diff --git a/src/posix/sdl/gl_sysfb.h b/src/posix/sdl/gl_sysfb.h index abb44c7b0..6282b4e40 100644 --- a/src/posix/sdl/gl_sysfb.h +++ b/src/posix/sdl/gl_sysfb.h @@ -23,8 +23,9 @@ public: friend class SDLGLVideo; - int GetClientWidth(); - int GetClientHeight(); + int GetClientWidth() override; + int GetClientHeight() override; + void ToggleFullscreen(bool yes) override; SDL_Window *GetSDLWindow() { return Screen; } diff --git a/src/posix/sdl/sdlglvideo.cpp b/src/posix/sdl/sdlglvideo.cpp index 6ad3420f2..ca3fe34d8 100644 --- a/src/posix/sdl/sdlglvideo.cpp +++ b/src/posix/sdl/sdlglvideo.cpp @@ -309,6 +309,11 @@ void SystemFrameBuffer::SwapBuffers() SDL_GL_SwapWindow (Screen); } +void SystemFrameBuffer::ToggleFullscreen(bool yes) +{ + ... // todo +} + int SystemFrameBuffer::GetClientWidth() { int width = 0; diff --git a/src/v_framebuffer.cpp b/src/v_framebuffer.cpp new file mode 100644 index 000000000..8f4aac5ec --- /dev/null +++ b/src/v_framebuffer.cpp @@ -0,0 +1,557 @@ +/* +** The base framebuffer class +** +**--------------------------------------------------------------------------- +** Copyright 1999-2016 Randy Heit +** Copyright 2005-2018 Christoph Oelckers +** 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. +**--------------------------------------------------------------------------- +** +*/ + +#include + +#include "i_system.h" +#include "x86.h" +#include "actor.h" + +#include "v_video.h" + +#include "c_dispatch.h" +#include "sbar.h" +#include "hardware.h" +#include "r_utility.h" +#include "r_renderer.h" +#include "vm.h" +#include "r_videoscale.h" +#include "i_time.h" + + +CVAR(Bool, gl_scale_viewport, true, CVAR_ARCHIVE); +CVAR(Bool, vid_fps, false, 0) +CVAR(Int, vid_showpalette, 0, 0) + +EXTERN_CVAR(Bool, ticker) +EXTERN_CVAR(Float, vid_brightness) +EXTERN_CVAR(Float, vid_contrast) +EXTERN_CVAR(Int, screenblocks) + +void V_DrawPaletteTester(int pal); + +//========================================================================== +// +// DCanvas :: CalcGamma +// +//========================================================================== + +void DFrameBuffer::CalcGamma (float gamma, uint8_t gammalookup[256]) +{ + // I found this formula on the web at + // , + // but that page no longer exits. + double invgamma = 1.f / gamma; + int i; + + for (i = 0; i < 256; i++) + { + gammalookup[i] = (uint8_t)(255.0 * pow (i / 255.0, invgamma) + 0.5); + } +} + +//========================================================================== +// +// DSimpleCanvas Constructor +// +// A simple canvas just holds a buffer in main memory. +// +//========================================================================== + +DSimpleCanvas::DSimpleCanvas (int width, int height, bool bgra) + : DCanvas (width, height, bgra) +{ + PixelBuffer = nullptr; + Resize(width, height); +} + +void DSimpleCanvas::Resize(int width, int height) +{ + Width = width; + Height = height; + + if (PixelBuffer != NULL) + { + delete[] PixelBuffer; + PixelBuffer = NULL; + } + + // Making the pitch a power of 2 is very bad for performance + // Try to maximize the number of cache lines that can be filled + // for each column drawing operation by making the pitch slightly + // longer than the width. The values used here are all based on + // empirical evidence. + + if (width <= 640) + { + // For low resolutions, just keep the pitch the same as the width. + // Some speedup can be seen using the technique below, but the speedup + // is so marginal that I don't consider it worthwhile. + Pitch = width; + } + else + { + // If we couldn't figure out the CPU's L1 cache line size, assume + // it's 32 bytes wide. + if (CPU.DataL1LineSize == 0) + { + CPU.DataL1LineSize = 32; + } + // The Athlon and P3 have very different caches, apparently. + // I am going to generalize the Athlon's performance to all AMD + // processors and the P3's to all non-AMD processors. I don't know + // how smart that is, but I don't have a vast plethora of + // processors to test with. + if (CPU.bIsAMD) + { + Pitch = width + CPU.DataL1LineSize; + } + else + { + Pitch = width + MAX(0, CPU.DataL1LineSize - 8); + } + } + int bytes_per_pixel = Bgra ? 4 : 1; + PixelBuffer = new uint8_t[Pitch * height * bytes_per_pixel]; + memset (PixelBuffer, 0, Pitch * height * bytes_per_pixel); +} + +//========================================================================== +// +// DSimpleCanvas Destructor +// +//========================================================================== + +DSimpleCanvas::~DSimpleCanvas () +{ + if (PixelBuffer != NULL) + { + delete[] PixelBuffer; + PixelBuffer = NULL; + } +} + +//========================================================================== +// +// DFrameBuffer Constructor +// +// A frame buffer canvas is the most common and represents the image that +// gets drawn to the screen. +// +//========================================================================== + +DFrameBuffer::DFrameBuffer (int width, int height) +{ + SetSize(width, height); +} + +void DFrameBuffer::SetSize(int width, int height) +{ + Width = ViewportScaledWidth(width, height); + Height = ViewportScaledHeight(width, height); +} + +//========================================================================== +// +// DFrameBuffer :: DrawRateStuff +// +// Draws the fps counter, dot ticker, and palette debug. +// +//========================================================================== + +void DFrameBuffer::DrawRateStuff () +{ + // Draws frame time and cumulative fps + if (vid_fps) + { + uint64_t ms = screen->FrameTime; + uint64_t howlong = ms - LastMS; + if ((signed)howlong >= 0) + { + char fpsbuff[40]; + int chars; + int rate_x; + + int textScale = active_con_scale(); + + chars = mysnprintf (fpsbuff, countof(fpsbuff), "%2llu ms (%3llu fps)", howlong, LastCount); + rate_x = Width / textScale - ConFont->StringWidth(&fpsbuff[0]); + Clear (rate_x * textScale, 0, Width, ConFont->GetHeight() * textScale, GPalette.BlackIndex, 0); + DrawText (ConFont, CR_WHITE, rate_x, 0, (char *)&fpsbuff[0], + DTA_VirtualWidth, screen->GetWidth() / textScale, + DTA_VirtualHeight, screen->GetHeight() / textScale, + DTA_KeepRatio, true, TAG_DONE); + + uint32_t thisSec = (uint32_t)(ms/1000); + if (LastSec < thisSec) + { + LastCount = FrameCount / (thisSec - LastSec); + LastSec = thisSec; + FrameCount = 0; + } + FrameCount++; + } + LastMS = ms; + } + + // draws little dots on the bottom of the screen + if (ticker) + { + int64_t t = I_GetTime(); + int64_t tics = t - LastTic; + + LastTic = t; + if (tics > 20) tics = 20; + + int i; + for (i = 0; i < tics*2; i += 2) Clear(i, Height-1, i+1, Height, 255, 0); + for ( ; i < 20*2; i += 2) Clear(i, Height-1, i+1, Height, 0, 0); + } + + // draws the palette for debugging + if (vid_showpalette) + { + V_DrawPaletteTester(vid_showpalette); + } +} + +//========================================================================== +// +// Palette stuff. +// +//========================================================================== + +void DFrameBuffer::GetFlashedPalette(PalEntry pal[256]) +{ + DoBlending(SourcePalette, pal, 256, Flash.r, Flash.g, Flash.b, Flash.a); +} + +PalEntry *DFrameBuffer::GetPalette() +{ + return SourcePalette; +} + +bool DFrameBuffer::SetFlash(PalEntry rgb, int amount) +{ + Flash = PalEntry(amount, rgb.r, rgb.g, rgb.b); + return true; +} + +void DFrameBuffer::GetFlash(PalEntry &rgb, int &amount) +{ + rgb = Flash; + rgb.a = 0; + amount = Flash.a; +} + + +//========================================================================== +// +// DFrameBuffer :: SetVSync +// +// Turns vertical sync on and off, if supported. +// +//========================================================================== + +void DFrameBuffer::SetVSync (bool vsync) +{ +} + +//========================================================================== +// +// DFrameBuffer :: WipeStartScreen +// +// Grabs a copy of the screen currently displayed to serve as the initial +// frame of a screen wipe. Also determines which screenwipe will be +// performed. +// +//========================================================================== + +bool DFrameBuffer::WipeStartScreen(int type) +{ + return false; +} + +//========================================================================== +// +// DFrameBuffer :: WipeEndScreen +// +// Grabs a copy of the most-recently drawn, but not yet displayed, screen +// to serve as the final frame of a screen wipe. +// +//========================================================================== + +void DFrameBuffer::WipeEndScreen() +{ +} + +//========================================================================== +// +// DFrameBuffer :: WipeDo +// +// Draws one frame of a screenwipe. Should be called no more than 35 +// times per second. If called less than that, ticks indicates how many +// ticks have passed since the last call. +// +//========================================================================== + +bool DFrameBuffer::WipeDo(int ticks) +{ + return false; +} + +//========================================================================== +// +// DFrameBuffer :: WipeCleanup +// +//========================================================================== + +void DFrameBuffer::WipeCleanup() +{ +} + +//========================================================================== +// +// DFrameBuffer :: InitPalette +// +//========================================================================== + +void DFrameBuffer::InitPalette() +{ + memcpy(SourcePalette, GPalette.BaseColors, sizeof(PalEntry) * 256); + UpdatePalette(); +} + +//========================================================================== +// +// +// +//========================================================================== + +void DFrameBuffer::BuildGammaTable(uint16_t *gammaTable) +{ + float gamma = clamp(Gamma, 0.1f, 4.f); + float contrast = clamp(vid_contrast, 0.1f, 3.f); + float bright = clamp(vid_brightness, -0.8f, 0.8f); + + double invgamma = 1 / gamma; + double norm = pow(255., invgamma - 1); + + for (int i = 0; i < 256; i++) + { + double val = i * contrast - (contrast - 1) * 127; + val += bright * 128; + if (gamma != 1) val = pow(val, invgamma) / norm; + + gammaTable[i] = gammaTable[i + 256] = gammaTable[i + 512] = (uint16_t)clamp(val * 256, 0, 0xffff); + } +} + +//========================================================================== +// +// DFrameBuffer :: GetCaps +// +//========================================================================== + +EXTERN_CVAR(Bool, r_drawvoxels) + +uint32_t DFrameBuffer::GetCaps() +{ + ActorRenderFeatureFlags FlagSet = 0; + + if (V_IsPolyRenderer()) + FlagSet |= RFF_POLYGONAL | RFF_TILTPITCH | RFF_SLOPE3DFLOORS; + else + { + FlagSet |= RFF_UNCLIPPEDTEX; + if (r_drawvoxels) + FlagSet |= RFF_VOXELS; + } + + if (V_IsTrueColor()) + FlagSet |= RFF_TRUECOLOR; + else + FlagSet |= RFF_COLORMAP; + + return (uint32_t)FlagSet; +} + +void DFrameBuffer::RenderTextureView(FCanvasTexture *tex, AActor *Viewpoint, double FOV) +{ + SWRenderer->RenderTextureView(tex, Viewpoint, FOV); +} + +void DFrameBuffer::WriteSavePic(player_t *player, FileWriter *file, int width, int height) +{ + SWRenderer->WriteSavePic(player, file, width, height); +} + + +//========================================================================== +// +// Calculates the viewport values needed for 2D and 3D operations +// +//========================================================================== + +void DFrameBuffer::SetViewportRects(IntRect *bounds) +{ + if (bounds) + { + mSceneViewport = *bounds; + mScreenViewport = *bounds; + mOutputLetterbox = *bounds; + return; + } + + // Special handling so the view with a visible status bar displays properly + int height, width; + if (screenblocks >= 10) + { + height = GetHeight(); + width = GetWidth(); + } + else + { + height = (screenblocks*GetHeight() / 10) & ~7; + width = (screenblocks*GetWidth() / 10); + } + + // Back buffer letterbox for the final output + int clientWidth = GetClientWidth(); + int clientHeight = GetClientHeight(); + if (clientWidth == 0 || clientHeight == 0) + { + // When window is minimized there may not be any client area. + // Pretend to the rest of the render code that we just have a very small window. + clientWidth = 160; + clientHeight = 120; + } + int screenWidth = GetWidth(); + int screenHeight = GetHeight(); + float scaleX, scaleY; + if (ViewportIsScaled43()) + { + scaleX = MIN(clientWidth / (float)screenWidth, clientHeight / (screenHeight * 1.2f)); + scaleY = scaleX * 1.2f; + } + else + { + scaleX = MIN(clientWidth / (float)screenWidth, clientHeight / (float)screenHeight); + scaleY = scaleX; + } + mOutputLetterbox.width = (int)round(screenWidth * scaleX); + mOutputLetterbox.height = (int)round(screenHeight * scaleY); + mOutputLetterbox.left = (clientWidth - mOutputLetterbox.width) / 2; + mOutputLetterbox.top = (clientHeight - mOutputLetterbox.height) / 2; + + // The entire renderable area, including the 2D HUD + mScreenViewport.left = 0; + mScreenViewport.top = 0; + mScreenViewport.width = screenWidth; + mScreenViewport.height = screenHeight; + + // Viewport for the 3D scene + mSceneViewport.left = viewwindowx; + mSceneViewport.top = screenHeight - (height + viewwindowy - ((height - viewheight) / 2)); + mSceneViewport.width = viewwidth; + mSceneViewport.height = height; + + // Scale viewports to fit letterbox + bool notScaled = ((mScreenViewport.width == ViewportScaledWidth(mScreenViewport.width, mScreenViewport.height)) && + (mScreenViewport.width == ViewportScaledHeight(mScreenViewport.width, mScreenViewport.height)) && + !ViewportIsScaled43()); + if (gl_scale_viewport && !IsFullscreen() && notScaled) + { + mScreenViewport.width = mOutputLetterbox.width; + mScreenViewport.height = mOutputLetterbox.height; + mSceneViewport.left = (int)round(mSceneViewport.left * scaleX); + mSceneViewport.top = (int)round(mSceneViewport.top * scaleY); + mSceneViewport.width = (int)round(mSceneViewport.width * scaleX); + mSceneViewport.height = (int)round(mSceneViewport.height * scaleY); + } +} + +//=========================================================================== +// +// Calculates the OpenGL window coordinates for a zdoom screen position +// +//=========================================================================== + +int DFrameBuffer::ScreenToWindowX(int x) +{ + return mScreenViewport.left + (int)round(x * mScreenViewport.width / (float)GetWidth()); +} + +int DFrameBuffer::ScreenToWindowY(int y) +{ + return mScreenViewport.top + mScreenViewport.height - (int)round(y * mScreenViewport.height / (float)GetHeight()); +} + +void DFrameBuffer::ScaleCoordsFromWindow(int16_t &x, int16_t &y) +{ + int letterboxX = mOutputLetterbox.left; + int letterboxY = mOutputLetterbox.top; + int letterboxWidth = mOutputLetterbox.width; + int letterboxHeight = mOutputLetterbox.height; + + x = int16_t((x - letterboxX) * Width / letterboxWidth); + y = int16_t((y - letterboxY) * Height / letterboxHeight); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +#define DBGBREAK assert(0) + +class DDummyFrameBuffer : public DFrameBuffer +{ + typedef DFrameBuffer Super; +public: + DDummyFrameBuffer(int width, int height) + : DFrameBuffer(0, 0) + { + SetVirtualSize(width, height); + } + // These methods should never be called. + void Update() { DBGBREAK; } + bool IsFullscreen() { DBGBREAK; return 0; } + int GetClientWidth() { DBGBREAK; return 0; } + int GetClientHeight() { DBGBREAK; return 0; } + + float Gamma; +}; + diff --git a/src/v_video.cpp b/src/v_video.cpp index c5d5b2320..fbb0109a2 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -1,5 +1,5 @@ /* -** +** Video basics and init code. ** **--------------------------------------------------------------------------- ** Copyright 1999-2016 Randy Heit @@ -31,13 +31,6 @@ **--------------------------------------------------------------------------- ** */ -//----------------------------------------------------------------------------- -// -// DESCRIPTION: -// Functions to draw patches (by post) directly to screen-> -// Functions to blit a block to the screen-> -// -//----------------------------------------------------------------------------- #include @@ -74,10 +67,6 @@ #include "i_time.h" EXTERN_CVAR(Bool, cl_capfps) -EXTERN_CVAR(Float, vid_brightness) -EXTERN_CVAR(Float, vid_contrast) -CVAR(Bool, gl_scale_viewport, true, CVAR_ARCHIVE); -EXTERN_CVAR(Int, screenblocks) CUSTOM_CVAR(Int, vid_maxfps, 200, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { @@ -156,10 +145,7 @@ DFrameBuffer *screen; CVAR (Int, vid_defwidth, 640, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Int, vid_defheight, 480, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Int, vid_defbits, 8, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Bool, vid_fps, false, 0) CVAR (Bool, ticker, false, 0) -CVAR (Int, vid_showpalette, 0, 0) CUSTOM_CVAR (Bool, vid_vsync, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) { @@ -171,10 +157,6 @@ CUSTOM_CVAR (Bool, vid_vsync, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // [RH] Set true when vid_setmode command has been executed bool setmodeneeded = false; -// [RH] Resolution to change to when setmodeneeded is true -int NewWidth, NewHeight, NewBits; - -void V_DrawPaletteTester(int pal); //========================================================================== // @@ -480,477 +462,6 @@ static void BuildTransTable (const PalEntry *palette) } } -//========================================================================== -// -// DCanvas :: CalcGamma -// -//========================================================================== - -void DFrameBuffer::CalcGamma (float gamma, uint8_t gammalookup[256]) -{ - // I found this formula on the web at - // , - // but that page no longer exits. - double invgamma = 1.f / gamma; - int i; - - for (i = 0; i < 256; i++) - { - gammalookup[i] = (uint8_t)(255.0 * pow (i / 255.0, invgamma) + 0.5); - } -} - -//========================================================================== -// -// DSimpleCanvas Constructor -// -// A simple canvas just holds a buffer in main memory. -// -//========================================================================== - -DSimpleCanvas::DSimpleCanvas (int width, int height, bool bgra) - : DCanvas (width, height, bgra) -{ - PixelBuffer = nullptr; - Resize(width, height); -} - -void DSimpleCanvas::Resize(int width, int height) -{ - Width = width; - Height = height; - - if (PixelBuffer != NULL) - { - delete[] PixelBuffer; - PixelBuffer = NULL; - } - - // Making the pitch a power of 2 is very bad for performance - // Try to maximize the number of cache lines that can be filled - // for each column drawing operation by making the pitch slightly - // longer than the width. The values used here are all based on - // empirical evidence. - - if (width <= 640) - { - // For low resolutions, just keep the pitch the same as the width. - // Some speedup can be seen using the technique below, but the speedup - // is so marginal that I don't consider it worthwhile. - Pitch = width; - } - else - { - // If we couldn't figure out the CPU's L1 cache line size, assume - // it's 32 bytes wide. - if (CPU.DataL1LineSize == 0) - { - CPU.DataL1LineSize = 32; - } - // The Athlon and P3 have very different caches, apparently. - // I am going to generalize the Athlon's performance to all AMD - // processors and the P3's to all non-AMD processors. I don't know - // how smart that is, but I don't have a vast plethora of - // processors to test with. - if (CPU.bIsAMD) - { - Pitch = width + CPU.DataL1LineSize; - } - else - { - Pitch = width + MAX(0, CPU.DataL1LineSize - 8); - } - } - int bytes_per_pixel = Bgra ? 4 : 1; - PixelBuffer = new uint8_t[Pitch * height * bytes_per_pixel]; - memset (PixelBuffer, 0, Pitch * height * bytes_per_pixel); -} - -//========================================================================== -// -// DSimpleCanvas Destructor -// -//========================================================================== - -DSimpleCanvas::~DSimpleCanvas () -{ - if (PixelBuffer != NULL) - { - delete[] PixelBuffer; - PixelBuffer = NULL; - } -} - -//========================================================================== -// -// DFrameBuffer Constructor -// -// A frame buffer canvas is the most common and represents the image that -// gets drawn to the screen. -// -//========================================================================== - -DFrameBuffer::DFrameBuffer (int width, int height) -{ - SetSize(width, height); -} - -void DFrameBuffer::SetSize(int width, int height) -{ - Width = ViewportScaledWidth(width, height); - Height = ViewportScaledHeight(width, height); -} - -//========================================================================== -// -// DFrameBuffer :: DrawRateStuff -// -// Draws the fps counter, dot ticker, and palette debug. -// -//========================================================================== - -void DFrameBuffer::DrawRateStuff () -{ - // Draws frame time and cumulative fps - if (vid_fps) - { - uint64_t ms = screen->FrameTime; - uint64_t howlong = ms - LastMS; - if ((signed)howlong >= 0) - { - char fpsbuff[40]; - int chars; - int rate_x; - - int textScale = active_con_scale(); - - chars = mysnprintf (fpsbuff, countof(fpsbuff), "%2" PRIu64 " ms (%3" PRIu64 " fps)", howlong, LastCount); - rate_x = Width / textScale - ConFont->StringWidth(&fpsbuff[0]); - Clear (rate_x * textScale, 0, Width, ConFont->GetHeight() * textScale, GPalette.BlackIndex, 0); - DrawText (ConFont, CR_WHITE, rate_x, 0, (char *)&fpsbuff[0], - DTA_VirtualWidth, screen->GetWidth() / textScale, - DTA_VirtualHeight, screen->GetHeight() / textScale, - DTA_KeepRatio, true, TAG_DONE); - - uint32_t thisSec = (uint32_t)(ms/1000); - if (LastSec < thisSec) - { - LastCount = FrameCount / (thisSec - LastSec); - LastSec = thisSec; - FrameCount = 0; - } - FrameCount++; - } - LastMS = ms; - } - - // draws little dots on the bottom of the screen - if (ticker) - { - int64_t t = I_GetTime(); - int64_t tics = t - LastTic; - - LastTic = t; - if (tics > 20) tics = 20; - - int i; - for (i = 0; i < tics*2; i += 2) Clear(i, Height-1, i+1, Height, 255, 0); - for ( ; i < 20*2; i += 2) Clear(i, Height-1, i+1, Height, 0, 0); - } - - // draws the palette for debugging - if (vid_showpalette) - { - V_DrawPaletteTester(vid_showpalette); - } -} - -//========================================================================== -// -// Palette stuff. -// -//========================================================================== - -void DFrameBuffer::GetFlashedPalette(PalEntry pal[256]) -{ - DoBlending(SourcePalette, pal, 256, Flash.r, Flash.g, Flash.b, Flash.a); -} - -PalEntry *DFrameBuffer::GetPalette() -{ - return SourcePalette; -} - -bool DFrameBuffer::SetFlash(PalEntry rgb, int amount) -{ - Flash = PalEntry(amount, rgb.r, rgb.g, rgb.b); - return true; -} - -void DFrameBuffer::GetFlash(PalEntry &rgb, int &amount) -{ - rgb = Flash; - rgb.a = 0; - amount = Flash.a; -} - - -//========================================================================== -// -// DFrameBuffer :: SetVSync -// -// Turns vertical sync on and off, if supported. -// -//========================================================================== - -void DFrameBuffer::SetVSync (bool vsync) -{ -} - -//========================================================================== -// -// DFrameBuffer :: WipeStartScreen -// -// Grabs a copy of the screen currently displayed to serve as the initial -// frame of a screen wipe. Also determines which screenwipe will be -// performed. -// -//========================================================================== - -bool DFrameBuffer::WipeStartScreen(int type) -{ - return false; -} - -//========================================================================== -// -// DFrameBuffer :: WipeEndScreen -// -// Grabs a copy of the most-recently drawn, but not yet displayed, screen -// to serve as the final frame of a screen wipe. -// -//========================================================================== - -void DFrameBuffer::WipeEndScreen() -{ -} - -//========================================================================== -// -// DFrameBuffer :: WipeDo -// -// Draws one frame of a screenwipe. Should be called no more than 35 -// times per second. If called less than that, ticks indicates how many -// ticks have passed since the last call. -// -//========================================================================== - -bool DFrameBuffer::WipeDo(int ticks) -{ - return false; -} - -//========================================================================== -// -// DFrameBuffer :: WipeCleanup -// -//========================================================================== - -void DFrameBuffer::WipeCleanup() -{ -} - -//========================================================================== -// -// DFrameBuffer :: InitPalette -// -//========================================================================== - -void DFrameBuffer::InitPalette() -{ - memcpy(SourcePalette, GPalette.BaseColors, sizeof(PalEntry) * 256); - UpdatePalette(); -} - -//========================================================================== -// -// -// -//========================================================================== - -void DFrameBuffer::BuildGammaTable(uint16_t *gammaTable) -{ - float gamma = clamp(Gamma, 0.1f, 4.f); - float contrast = clamp(vid_contrast, 0.1f, 3.f); - float bright = clamp(vid_brightness, -0.8f, 0.8f); - - double invgamma = 1 / gamma; - double norm = pow(255., invgamma - 1); - - for (int i = 0; i < 256; i++) - { - double val = i * contrast - (contrast - 1) * 127; - val += bright * 128; - if (gamma != 1) val = pow(val, invgamma) / norm; - - gammaTable[i] = gammaTable[i + 256] = gammaTable[i + 512] = (uint16_t)clamp(val * 256, 0, 0xffff); - } -} - -//========================================================================== -// -// DFrameBuffer :: GetCaps -// -//========================================================================== - -EXTERN_CVAR(Bool, r_drawvoxels) - -uint32_t DFrameBuffer::GetCaps() -{ - ActorRenderFeatureFlags FlagSet = 0; - - if (V_IsPolyRenderer()) - FlagSet |= RFF_POLYGONAL | RFF_TILTPITCH | RFF_SLOPE3DFLOORS; - else - { - FlagSet |= RFF_UNCLIPPEDTEX; - if (r_drawvoxels) - FlagSet |= RFF_VOXELS; - } - - if (V_IsTrueColor()) - FlagSet |= RFF_TRUECOLOR; - else - FlagSet |= RFF_COLORMAP; - - return (uint32_t)FlagSet; -} - -void DFrameBuffer::RenderTextureView(FCanvasTexture *tex, AActor *Viewpoint, double FOV) -{ - SWRenderer->RenderTextureView(tex, Viewpoint, FOV); -} - -void DFrameBuffer::WriteSavePic(player_t *player, FileWriter *file, int width, int height) -{ - SWRenderer->WriteSavePic(player, file, width, height); -} - - -//========================================================================== -// -// Calculates the viewport values needed for 2D and 3D operations -// -//========================================================================== - -void DFrameBuffer::SetViewportRects(IntRect *bounds) -{ - if (bounds) - { - mSceneViewport = *bounds; - mScreenViewport = *bounds; - mOutputLetterbox = *bounds; - return; - } - - // Special handling so the view with a visible status bar displays properly - int height, width; - if (screenblocks >= 10) - { - height = GetHeight(); - width = GetWidth(); - } - else - { - height = (screenblocks*GetHeight() / 10) & ~7; - width = (screenblocks*GetWidth() / 10); - } - - // Back buffer letterbox for the final output - int clientWidth = GetClientWidth(); - int clientHeight = GetClientHeight(); - if (clientWidth == 0 || clientHeight == 0) - { - // When window is minimized there may not be any client area. - // Pretend to the rest of the render code that we just have a very small window. - clientWidth = 160; - clientHeight = 120; - } - int screenWidth = GetWidth(); - int screenHeight = GetHeight(); - float scaleX, scaleY; - if (ViewportIsScaled43()) - { - scaleX = MIN(clientWidth / (float)screenWidth, clientHeight / (screenHeight * 1.2f)); - scaleY = scaleX * 1.2f; - } - else - { - scaleX = MIN(clientWidth / (float)screenWidth, clientHeight / (float)screenHeight); - scaleY = scaleX; - } - mOutputLetterbox.width = (int)round(screenWidth * scaleX); - mOutputLetterbox.height = (int)round(screenHeight * scaleY); - mOutputLetterbox.left = (clientWidth - mOutputLetterbox.width) / 2; - mOutputLetterbox.top = (clientHeight - mOutputLetterbox.height) / 2; - - // The entire renderable area, including the 2D HUD - mScreenViewport.left = 0; - mScreenViewport.top = 0; - mScreenViewport.width = screenWidth; - mScreenViewport.height = screenHeight; - - // Viewport for the 3D scene - mSceneViewport.left = viewwindowx; - mSceneViewport.top = screenHeight - (height + viewwindowy - ((height - viewheight) / 2)); - mSceneViewport.width = viewwidth; - mSceneViewport.height = height; - - // Scale viewports to fit letterbox - bool notScaled = ((mScreenViewport.width == ViewportScaledWidth(mScreenViewport.width, mScreenViewport.height)) && - (mScreenViewport.width == ViewportScaledHeight(mScreenViewport.width, mScreenViewport.height)) && - !ViewportIsScaled43()); - if (gl_scale_viewport && !IsFullscreen() && notScaled) - { - mScreenViewport.width = mOutputLetterbox.width; - mScreenViewport.height = mOutputLetterbox.height; - mSceneViewport.left = (int)round(mSceneViewport.left * scaleX); - mSceneViewport.top = (int)round(mSceneViewport.top * scaleY); - mSceneViewport.width = (int)round(mSceneViewport.width * scaleX); - mSceneViewport.height = (int)round(mSceneViewport.height * scaleY); - } -} - -//=========================================================================== -// -// Calculates the OpenGL window coordinates for a zdoom screen position -// -//=========================================================================== - -int DFrameBuffer::ScreenToWindowX(int x) -{ - return mScreenViewport.left + (int)round(x * mScreenViewport.width / (float)GetWidth()); -} - -int DFrameBuffer::ScreenToWindowY(int y) -{ - return mScreenViewport.top + mScreenViewport.height - (int)round(y * mScreenViewport.height / (float)GetHeight()); -} - -void DFrameBuffer::ScaleCoordsFromWindow(int16_t &x, int16_t &y) -{ - int letterboxX = mOutputLetterbox.left; - int letterboxY = mOutputLetterbox.top; - int letterboxWidth = mOutputLetterbox.width; - int letterboxHeight = mOutputLetterbox.height; - - x = int16_t((x - letterboxX) * Width / letterboxWidth); - y = int16_t((y - letterboxY) * Height / letterboxHeight); -} - - - - CCMD(clean) { Printf ("CleanXfac: %d\nCleanYfac: %d\n", CleanXfac, CleanYfac); @@ -1109,9 +620,6 @@ void V_Init (bool restart) if ( (i = Args->CheckValue ("-height")) ) height = atoi (i); - if ( (i = Args->CheckValue ("-bits")) ) - bits = atoi (i); - if (width == 0) { if (height == 0) @@ -1128,11 +636,10 @@ void V_Init (bool restart) { height = (width * 6) / 8; } + // Remember the passed arguments for the next time the game starts up windowed. + vid_defwidth = width; + vid_defheight = height; - if (bits == 0) - { - bits = vid_defbits; - } screen = new DDummyFrameBuffer (width, height); } // Update screen palette when restarting diff --git a/src/v_video.h b/src/v_video.h index 99199fb64..72208ec42 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -408,6 +408,7 @@ public: // Returns true if running fullscreen. virtual bool IsFullscreen () = 0; + virtual void ToggleFullscreen(bool yes) {} // Changes the vsync setting, if supported by the device. virtual void SetVSync (bool vsync); diff --git a/src/win32/gl_sysfb.h b/src/win32/gl_sysfb.h index a94592901..34dfeac42 100644 --- a/src/win32/gl_sysfb.h +++ b/src/win32/gl_sysfb.h @@ -7,6 +7,9 @@ class SystemFrameBuffer : public DFrameBuffer { typedef DFrameBuffer Super; + void SaveWindowedPos(); + void RestoreWindowedPos(); + public: SystemFrameBuffer() {} // Actually, hMonitor is a HMONITOR, but it's passed as a void * as there @@ -17,16 +20,17 @@ public: void SetVSync (bool vsync); void SwapBuffers(); - int GetClientWidth(); - int GetClientHeight(); + int GetClientWidth() override; + int GetClientHeight() override; - bool IsFullscreen(); + bool IsFullscreen() override; + void ToggleFullscreen(bool yes) override; void InitializeState(); protected: - void PositionWindow(); + void PositionWindow(bool fullscreen); void ResetGammaTable(); void SetGammaTable(uint16_t * tbl); diff --git a/src/win32/hardware.cpp b/src/win32/hardware.cpp index 6b0aff1d7..5c665e781 100644 --- a/src/win32/hardware.cpp +++ b/src/win32/hardware.cpp @@ -32,8 +32,10 @@ ** */ +#define _WIN32_WINNT 0x0501 #define WIN32_LEAN_AND_MEAN #include +#include #include "hardware.h" #include "c_dispatch.h" @@ -44,10 +46,7 @@ #include "swrenderer/r_swrenderer.h" EXTERN_CVAR (Bool, fullscreen) - -CVAR(Int, win_x, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -CVAR(Int, win_y, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -CVAR(Bool, win_maximized, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +EXTERN_CVAR(Int, vid_maxfps) extern HWND Window; @@ -87,10 +86,6 @@ CUSTOM_CVAR(Int, vid_gpuswitch, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINI } -CCMD (vid_restart) -{ -} - void I_ShutdownGraphics () { @@ -137,116 +132,87 @@ void I_InitGraphics () atterm (I_ShutdownGraphics); } -/** Remaining code is common to Win32 and Linux **/ -// VIDEO WRAPPERS --------------------------------------------------------- +static UINT FPSLimitTimer; +HANDLE FPSLimitEvent; +//========================================================================== +// +// SetFPSLimit +// +// Initializes an event timer to fire at a rate of /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. +// +//========================================================================== -static void GetCenteredPos (int &winx, int &winy, int &winw, int &winh, int &scrwidth, int &scrheight) +static void StopFPSLimit() { - DEVMODE displaysettings; - RECT rect; - int cx, cy; - - memset (&displaysettings, 0, sizeof(displaysettings)); - displaysettings.dmSize = sizeof(displaysettings); - EnumDisplaySettings (NULL, ENUM_CURRENT_SETTINGS, &displaysettings); - scrwidth = (int)displaysettings.dmPelsWidth; - scrheight = (int)displaysettings.dmPelsHeight; - GetWindowRect (Window, &rect); - cx = scrwidth / 2; - cy = scrheight / 2; - winx = cx - (winw = rect.right - rect.left) / 2; - winy = cy - (winh = rect.bottom - rect.top) / 2; + I_SetFPSLimit(0); } -static void KeepWindowOnScreen (int &winx, int &winy, int winw, int winh, int scrwidth, int scrheight) +void I_SetFPSLimit(int limit) { - // If the window is too large to fit entirely on the screen, at least - // keep its upperleft corner visible. - if (winx + winw > scrwidth) + if (limit < 0) { - winx = scrwidth - winw; + limit = vid_maxfps; } - if (winx < 0) + // Kill any leftover timer. + if (FPSLimitTimer != 0) { - winx = 0; + timeKillEvent(FPSLimitTimer); + FPSLimitTimer = 0; } - if (winy + winh > scrheight) - { - winy = scrheight - winh; - } - if (winy < 0) - { - winy = 0; - } -} - -void I_SaveWindowedPos () -{ - // Don't save if we were run with the -0 option. - if (Args->CheckParm ("-0")) - { - return; - } - // Make sure we only save the window position if it's not fullscreen. - static const int WINDOW_STYLE = WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX; - if ((GetWindowLong (Window, GWL_STYLE) & WINDOW_STYLE) == WINDOW_STYLE) - { - RECT wrect; - - if (GetWindowRect (Window, &wrect)) + if (limit == 0) + { // no limit + if (FPSLimitEvent != NULL) { - // If (win_x,win_y) specify to center the window, don't change them - // if the window is still centered. - if (win_x < 0 || win_y < 0) - { - int winx, winy, winw, winh, scrwidth, scrheight; - - GetCenteredPos (winx, winy, winw, winh, scrwidth, scrheight); - KeepWindowOnScreen (winx, winy, winw, winh, scrwidth, scrheight); - if (win_x < 0 && winx == wrect.left) - { - wrect.left = win_x; - } - if (win_y < 0 && winy == wrect.top) - { - wrect.top = win_y; - } - } - win_x = wrect.left; - win_y = wrect.top; + CloseHandle(FPSLimitEvent); + FPSLimitEvent = NULL; } - - win_maximized = IsZoomed(Window) == TRUE; - } -} - -void I_RestoreWindowedPos () -{ - int winx, winy, winw, winh, scrwidth, scrheight; - - GetCenteredPos (winx, winy, winw, winh, scrwidth, scrheight); - - // Just move to (0,0) if we were run with the -0 option. - if (Args->CheckParm ("-0")) - { - winx = winy = 0; + DPrintf(DMSG_NOTIFY, "FPS timer disabled\n"); } else { - if (win_x >= 0) + if (FPSLimitEvent == NULL) { - winx = win_x; + FPSLimitEvent = CreateEvent(NULL, FALSE, TRUE, NULL); + if (FPSLimitEvent == NULL) + { // Could not create event, so cannot use timer. + Printf(DMSG_WARNING, "Failed to create FPS limitter event\n"); + return; + } } - if (win_y >= 0) + 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) { - winy = win_y; + CloseHandle(FPSLimitEvent); + FPSLimitEvent = NULL; + Printf("Failed to create FPS limiter timer\n"); + return; } - KeepWindowOnScreen (winx, winy, winw, winh, scrwidth, scrheight); + DPrintf(DMSG_NOTIFY, "FPS timer set to %u ms\n", period); } - MoveWindow (Window, winx, winy, winw, winh, TRUE); - - if (win_maximized && !Args->CheckParm("-0")) - ShowWindow(Window, SW_MAXIMIZE); } + +//========================================================================== +// +// StopFPSLimit +// +// Used for cleanup during application shutdown. +// +//========================================================================== + +void I_FPSLimit() +{ + if (FPSLimitEvent != NULL) + { + WaitForSingleObject(FPSLimitEvent, 1000); + } +} + diff --git a/src/win32/hardware.h b/src/win32/hardware.h index a4f2f59e4..a4ab3bc00 100644 --- a/src/win32/hardware.h +++ b/src/win32/hardware.h @@ -37,9 +37,6 @@ #include "i_video.h" #include "v_video.h" -void I_SaveWindowedPos (); -void I_RestoreWindowedPos (); - void I_SetFPSLimit(int limit); void I_FPSLimit(); diff --git a/src/win32/win32gliface.cpp b/src/win32/win32gliface.cpp index 506001bf4..448d3374b 100644 --- a/src/win32/win32gliface.cpp +++ b/src/win32/win32gliface.cpp @@ -66,7 +66,11 @@ PFNWGLCHOOSEPIXELFORMATARBPROC myWglChoosePixelFormatARB; // = (PFNWGLCHOOSEPIXE PFNWGLCREATECONTEXTATTRIBSARBPROC myWglCreateContextAttribsARB; PFNWGLSWAPINTERVALEXTPROC myWglSwapIntervalExtProc; +CVAR(Int, vid_adapter, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Int, win_x, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Int, win_y, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, win_maximized, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CUSTOM_CVAR(Bool, gl_debug, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { @@ -652,19 +656,163 @@ void Win32GLVideo::Shutdown() } + + + +//========================================================================== +// +// Windows framebuffer +// +//========================================================================== + + //========================================================================== // // // //========================================================================== -void SystemFrameBuffer::PositionWindow() +static void GetCenteredPos(int &winx, int &winy, int &winw, int &winh, int &scrwidth, int &scrheight) +{ + DEVMODE displaysettings; + RECT rect; + int cx, cy; + + memset(&displaysettings, 0, sizeof(displaysettings)); + displaysettings.dmSize = sizeof(displaysettings); + EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &displaysettings); + scrwidth = (int)displaysettings.dmPelsWidth; + scrheight = (int)displaysettings.dmPelsHeight; + GetWindowRect(Window, &rect); + cx = scrwidth / 2; + cy = scrheight / 2; + winx = cx - (winw = rect.right - rect.left) / 2; + winy = cy - (winh = rect.bottom - rect.top) / 2; +} + +//========================================================================== +// +// +// +//========================================================================== + +static void KeepWindowOnScreen(int &winx, int &winy, int winw, int winh, int scrwidth, int scrheight) +{ + // If the window is too large to fit entirely on the screen, at least + // keep its upperleft corner visible. + if (winx + winw > scrwidth) + { + winx = scrwidth - winw; + } + if (winx < 0) + { + winx = 0; + } + if (winy + winh > scrheight) + { + winy = scrheight - winh; + } + if (winy < 0) + { + winy = 0; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void SystemFrameBuffer::SaveWindowedPos() +{ + // Don't save if we were run with the -0 option. + if (Args->CheckParm("-0")) + { + return; + } + // Make sure we only save the window position if it's not fullscreen. + static const int WINDOW_STYLE = WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX; + if ((GetWindowLong(Window, GWL_STYLE) & WINDOW_STYLE) == WINDOW_STYLE) + { + RECT wrect; + + if (GetWindowRect(Window, &wrect)) + { + // If (win_x,win_y) specify to center the window, don't change them + // if the window is still centered. + if (win_x < 0 || win_y < 0) + { + int winx, winy, winw, winh, scrwidth, scrheight; + + GetCenteredPos(winx, winy, winw, winh, scrwidth, scrheight); + KeepWindowOnScreen(winx, winy, winw, winh, scrwidth, scrheight); + if (win_x < 0 && winx == wrect.left) + { + wrect.left = win_x; + } + if (win_y < 0 && winy == wrect.top) + { + wrect.top = win_y; + } + } + win_x = wrect.left; + win_y = wrect.top; + } + + win_maximized = IsZoomed(Window) == TRUE; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void SystemFrameBuffer::RestoreWindowedPos() +{ + int winx, winy, winw, winh, scrwidth, scrheight; + + GetCenteredPos(winx, winy, winw, winh, scrwidth, scrheight); + + // Just move to (0,0) if we were run with the -0 option. + if (Args->CheckParm("-0")) + { + winx = winy = 0; + } + else + { + if (win_x >= 0) + { + winx = win_x; + } + if (win_y >= 0) + { + winy = win_y; + } + KeepWindowOnScreen(winx, winy, winw, winh, scrwidth, scrheight); + } + MoveWindow(Window, winx, winy, winw, winh, TRUE); + + if (win_maximized && !Args->CheckParm("-0")) + ShowWindow(Window, SW_MAXIMIZE); +} + +//========================================================================== +// +// +// +//========================================================================== + +void SystemFrameBuffer::PositionWindow(bool fullscreen) { RECT r; LONG style, exStyle; RECT monRect; + if (!m_Fullscreen) SaveWindowedPos(); if (m_Monitor) { MONITORINFOEX mi; @@ -712,8 +860,9 @@ void SystemFrameBuffer::PositionWindow() AdjustWindowRectEx(&windowRect, style, FALSE, exStyle); MoveWindow(Window, windowRect.left, windowRect.top, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, FALSE); - I_RestoreWindowedPos(); + RestoreWindowedPos(); } + m_Fullscreen = fullscreen; SetSize(GetClientWidth(), GetClientHeight()); } @@ -726,12 +875,8 @@ void SystemFrameBuffer::PositionWindow() SystemFrameBuffer::SystemFrameBuffer(void *hMonitor, bool fullscreen) : DFrameBuffer(vid_defwidth, vid_defheight) { m_Monitor = hMonitor; - m_Fullscreen = fullscreen; - - m_displayDeviceName = 0; - PositionWindow(); - + PositionWindow(fullscreen); if (!static_cast(Video)->InitHardware(Window, 0)) { @@ -778,7 +923,7 @@ SystemFrameBuffer::SystemFrameBuffer(void *hMonitor, bool fullscreen) : DFrameBu SystemFrameBuffer::~SystemFrameBuffer() { ResetGammaTable(); - I_SaveWindowedPos(); + SaveWindowedPos(); ShowWindow (Window, SW_SHOW); SetWindowLong(Window, GWL_STYLE, WS_VISIBLE | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW); @@ -837,6 +982,17 @@ bool SystemFrameBuffer::IsFullscreen() return m_Fullscreen; } +//========================================================================== +// +// +// +//========================================================================== + +void SystemFrameBuffer::ToggleFullscreen(bool yes) +{ + PositionWindow(yes); +} + //========================================================================== // // diff --git a/src/win32/win32video.cpp b/src/win32/win32video.cpp index 7473cd011..517c02c02 100644 --- a/src/win32/win32video.cpp +++ b/src/win32/win32video.cpp @@ -42,108 +42,3 @@ #include "c_cvars.h" #include "i_system.h" -// MACROS ------------------------------------------------------------------ - -// TYPES ------------------------------------------------------------------- - -// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- - -// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- - -static void StopFPSLimit(); - -// EXTERNAL DATA DECLARATIONS ---------------------------------------------- - -EXTERN_CVAR(Int, vid_maxfps) - -// PRIVATE DATA DEFINITIONS ------------------------------------------------ - -static UINT FPSLimitTimer; - -// PUBLIC DATA DEFINITIONS ------------------------------------------------- - -HANDLE FPSLimitEvent; - -CVAR (Int, vid_adapter, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) - - -//========================================================================== -// -// SetFPSLimit -// -// Initializes an event timer to fire at a rate of /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(DMSG_NOTIFY, "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(DMSG_WARNING, "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 limiter timer\n"); - return; - } - DPrintf(DMSG_NOTIFY, "FPS timer set to %u ms\n", period); - } -} - -//========================================================================== -// -// StopFPSLimit -// -// Used for cleanup during application shutdown. -// -//========================================================================== - -static void StopFPSLimit() -{ - I_SetFPSLimit(0); -} - -void I_FPSLimit() -{ - if (FPSLimitEvent != NULL) - { - WaitForSingleObject(FPSLimitEvent, 1000); - } -} -