Create a MainWindow class that manages the main win32 window of the application

This commit is contained in:
Magnus Norddahl 2022-05-14 02:25:56 +02:00 committed by Christoph Oelckers
parent 3ad6793ba3
commit 018fb2917f
24 changed files with 1187 additions and 1108 deletions

View file

@ -446,6 +446,7 @@ set( PLAT_WIN32_SOURCES
common/platform/win32/i_rawps2.cpp common/platform/win32/i_rawps2.cpp
common/platform/win32/i_xinput.cpp common/platform/win32/i_xinput.cpp
common/platform/win32/i_main.cpp common/platform/win32/i_main.cpp
common/platform/win32/i_mainwindow.cpp
common/platform/win32/i_system.cpp common/platform/win32/i_system.cpp
common/platform/win32/i_specialpaths.cpp common/platform/win32/i_specialpaths.cpp
common/platform/win32/st_start.cpp common/platform/win32/st_start.cpp

View file

@ -77,7 +77,6 @@ public:
void NetDone(); void NetDone();
bool NetLoop(bool (*timer_callback)(void*), void* userdata); bool NetLoop(bool (*timer_callback)(void*), void* userdata);
protected: protected:
long long NetMarqueeMode;
int NetMaxPos, NetCurPos; int NetMaxPos, NetCurPos;
}; };

View file

@ -50,9 +50,7 @@
#include "base_sysfb.h" #include "base_sysfb.h"
#include "win32basevideo.h" #include "win32basevideo.h"
#include "c_dispatch.h" #include "c_dispatch.h"
#include "i_mainwindow.h"
extern HWND Window;
extern "C" { extern "C" {
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
@ -85,7 +83,7 @@ EXTERN_CVAR(Int, vid_defheight)
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &displaysettings); EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &displaysettings);
scrwidth = (int)displaysettings.dmPelsWidth; scrwidth = (int)displaysettings.dmPelsWidth;
scrheight = (int)displaysettings.dmPelsHeight; scrheight = (int)displaysettings.dmPelsHeight;
GetWindowRect(Window, &rect); GetWindowRect(mainwindow.GetHandle(), &rect);
cx = scrwidth / 2; cx = scrwidth / 2;
cy = scrheight / 2; cy = scrheight / 2;
if (in_w > 0) winw = in_w; if (in_w > 0) winw = in_w;
@ -139,11 +137,11 @@ void SystemBaseFrameBuffer::SaveWindowedPos()
} }
// Make sure we only save the window position if it's not fullscreen. // Make sure we only save the window position if it's not fullscreen.
static const int WINDOW_STYLE = WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX; static const int WINDOW_STYLE = WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX;
if ((GetWindowLong(Window, GWL_STYLE) & WINDOW_STYLE) == WINDOW_STYLE) if ((GetWindowLong(mainwindow.GetHandle(), GWL_STYLE) & WINDOW_STYLE) == WINDOW_STYLE)
{ {
RECT wrect; RECT wrect;
if (GetWindowRect(Window, &wrect)) if (GetWindowRect(mainwindow.GetHandle(), &wrect))
{ {
// If (win_x,win_y) specify to center the window, don't change them // If (win_x,win_y) specify to center the window, don't change them
// if the window is still centered. // if the window is still centered.
@ -171,7 +169,7 @@ void SystemBaseFrameBuffer::SaveWindowedPos()
win_h = wrect.bottom - wrect.top; win_h = wrect.bottom - wrect.top;
} }
win_maximized = IsZoomed(Window) == TRUE; win_maximized = IsZoomed(mainwindow.GetHandle()) == TRUE;
} }
} }
@ -204,10 +202,10 @@ void SystemBaseFrameBuffer::RestoreWindowedPos()
} }
KeepWindowOnScreen(winx, winy, winw, winh, scrwidth, scrheight); KeepWindowOnScreen(winx, winy, winw, winh, scrwidth, scrheight);
} }
SetWindowPos(Window, nullptr, winx, winy, winw, winh, SWP_NOZORDER | SWP_FRAMECHANGED); SetWindowPos(mainwindow.GetHandle(), nullptr, winx, winy, winw, winh, SWP_NOZORDER | SWP_FRAMECHANGED);
if (win_maximized && !Args->CheckParm("-0")) if (win_maximized && !Args->CheckParm("-0"))
ShowWindow(Window, SW_MAXIMIZE); ShowWindow(mainwindow.GetHandle(), SW_MAXIMIZE);
} }
//========================================================================== //==========================================================================
@ -226,8 +224,8 @@ void SystemBaseFrameBuffer::SetWindowSize(int w, int h)
{ {
LONG style = WS_VISIBLE | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW; LONG style = WS_VISIBLE | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW;
LONG exStyle = WS_EX_WINDOWEDGE; LONG exStyle = WS_EX_WINDOWEDGE;
SetWindowLong(Window, GWL_STYLE, style); SetWindowLong(mainwindow.GetHandle(), GWL_STYLE, style);
SetWindowLong(Window, GWL_EXSTYLE, exStyle); SetWindowLong(mainwindow.GetHandle(), GWL_EXSTYLE, exStyle);
int winx, winy, winw, winh, scrwidth, scrheight; int winx, winy, winw, winh, scrwidth, scrheight;
@ -250,8 +248,8 @@ void SystemBaseFrameBuffer::SetWindowSize(int w, int h)
if (!vid_fullscreen) if (!vid_fullscreen)
{ {
ShowWindow(Window, SW_SHOWNORMAL); ShowWindow(mainwindow.GetHandle(), SW_SHOWNORMAL);
SetWindowPos(Window, nullptr, winx, winy, winw, winh, SWP_NOZORDER | SWP_FRAMECHANGED); SetWindowPos(mainwindow.GetHandle(), nullptr, winx, winy, winw, winh, SWP_NOZORDER | SWP_FRAMECHANGED);
win_maximized = false; win_maximized = false;
SetSize(GetClientWidth(), GetClientHeight()); SetSize(GetClientWidth(), GetClientHeight());
SaveWindowedPos(); SaveWindowedPos();
@ -302,9 +300,9 @@ void SystemBaseFrameBuffer::PositionWindow(bool fullscreen, bool initialcall)
} }
} }
ShowWindow(Window, SW_SHOW); ShowWindow(mainwindow.GetHandle(), SW_SHOW);
GetWindowRect(Window, &r); GetWindowRect(mainwindow.GetHandle(), &r);
style = WS_VISIBLE | WS_CLIPSIBLINGS; style = WS_VISIBLE | WS_CLIPSIBLINGS;
exStyle = 0; exStyle = 0;
@ -316,13 +314,13 @@ void SystemBaseFrameBuffer::PositionWindow(bool fullscreen, bool initialcall)
exStyle |= WS_EX_WINDOWEDGE; exStyle |= WS_EX_WINDOWEDGE;
} }
SetWindowLong(Window, GWL_STYLE, style); SetWindowLong(mainwindow.GetHandle(), GWL_STYLE, style);
SetWindowLong(Window, GWL_EXSTYLE, exStyle); SetWindowLong(mainwindow.GetHandle(), GWL_EXSTYLE, exStyle);
if (fullscreen) if (fullscreen)
{ {
SetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); SetWindowPos(mainwindow.GetHandle(), 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
MoveWindow(Window, monRect.left, monRect.top, monRect.right-monRect.left, monRect.bottom-monRect.top, FALSE); MoveWindow(mainwindow.GetHandle(), monRect.left, monRect.top, monRect.right-monRect.left, monRect.bottom-monRect.top, FALSE);
// And now, seriously, it IS in the right place. Promise. // And now, seriously, it IS in the right place. Promise.
} }
@ -352,9 +350,9 @@ SystemBaseFrameBuffer::SystemBaseFrameBuffer(void *hMonitor, bool fullscreen) :
m_displayDeviceName = 0; m_displayDeviceName = 0;
PositionWindow(fullscreen, true); PositionWindow(fullscreen, true);
HDC hDC = GetDC(Window); HDC hDC = GetDC(mainwindow.GetHandle());
ReleaseDC(Window, hDC); ReleaseDC(mainwindow.GetHandle(), hDC);
} }
//========================================================================== //==========================================================================
@ -367,10 +365,10 @@ SystemBaseFrameBuffer::~SystemBaseFrameBuffer()
{ {
if (!m_Fullscreen) SaveWindowedPos(); if (!m_Fullscreen) SaveWindowedPos();
ShowWindow (Window, SW_SHOW); ShowWindow (mainwindow.GetHandle(), SW_SHOW);
SetWindowLong(Window, GWL_STYLE, WS_VISIBLE | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW); SetWindowLong(mainwindow.GetHandle(), GWL_STYLE, WS_VISIBLE | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW);
SetWindowLong(Window, GWL_EXSTYLE, WS_EX_WINDOWEDGE); SetWindowLong(mainwindow.GetHandle(), GWL_EXSTYLE, WS_EX_WINDOWEDGE);
SetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); SetWindowPos(mainwindow.GetHandle(), 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
static_cast<Win32BaseVideo *>(Video)->Shutdown(); static_cast<Win32BaseVideo *>(Video)->Shutdown();
} }
@ -407,13 +405,13 @@ void SystemBaseFrameBuffer::ToggleFullscreen(bool yes)
int SystemBaseFrameBuffer::GetClientWidth() int SystemBaseFrameBuffer::GetClientWidth()
{ {
RECT rect = { 0 }; RECT rect = { 0 };
GetClientRect(Window, &rect); GetClientRect(mainwindow.GetHandle(), &rect);
return rect.right - rect.left; return rect.right - rect.left;
} }
int SystemBaseFrameBuffer::GetClientHeight() int SystemBaseFrameBuffer::GetClientHeight()
{ {
RECT rect = { 0 }; RECT rect = { 0 };
GetClientRect(Window, &rect); GetClientRect(mainwindow.GetHandle(), &rect);
return rect.bottom - rect.top; return rect.bottom - rect.top;
} }

View file

@ -49,8 +49,7 @@
#include "m_argv.h" #include "m_argv.h"
#include "engineerrors.h" #include "engineerrors.h"
#include "win32glvideo.h" #include "win32glvideo.h"
#include "i_mainwindow.h"
extern HWND Window;
extern "C" PROC zd_wglGetProcAddress(LPCSTR name); extern "C" PROC zd_wglGetProcAddress(LPCSTR name);
@ -71,13 +70,13 @@ PFNWGLSWAPINTERVALEXTPROC myWglSwapIntervalExtProc;
SystemGLFrameBuffer::SystemGLFrameBuffer(void *hMonitor, bool fullscreen) : SystemBaseFrameBuffer(hMonitor, fullscreen) SystemGLFrameBuffer::SystemGLFrameBuffer(void *hMonitor, bool fullscreen) : SystemBaseFrameBuffer(hMonitor, fullscreen)
{ {
if (!static_cast<Win32GLVideo*>(Video)->InitHardware(Window, 0)) if (!static_cast<Win32GLVideo*>(Video)->InitHardware(mainwindow.GetHandle(), 0))
{ {
I_FatalError("Unable to initialize OpenGL"); I_FatalError("Unable to initialize OpenGL");
return; return;
} }
HDC hDC = GetDC(Window); HDC hDC = GetDC(mainwindow.GetHandle());
const char *wglext = nullptr; const char *wglext = nullptr;
myWglSwapIntervalExtProc = (PFNWGLSWAPINTERVALEXTPROC)zd_wglGetProcAddress("wglSwapIntervalEXT"); myWglSwapIntervalExtProc = (PFNWGLSWAPINTERVALEXTPROC)zd_wglGetProcAddress("wglSwapIntervalEXT");
@ -102,7 +101,7 @@ SystemGLFrameBuffer::SystemGLFrameBuffer(void *hMonitor, bool fullscreen) : Syst
SwapInterval = -1; SwapInterval = -1;
} }
} }
ReleaseDC(Window, hDC); ReleaseDC(mainwindow.GetHandle(), hDC);
} }
//========================================================================== //==========================================================================

View file

@ -52,11 +52,10 @@
#endif #endif
#include "engineerrors.h" #include "engineerrors.h"
#include "i_system.h" #include "i_system.h"
#include "i_mainwindow.h"
EXTERN_CVAR(Int, vid_preferbackend) EXTERN_CVAR(Int, vid_preferbackend)
extern HWND Window;
IVideo *Video; IVideo *Video;
// do not include GL headers here, only declare the necessary functions. // do not include GL headers here, only declare the necessary functions.
@ -117,12 +116,12 @@ void I_InitGraphics ()
// If the focus window is destroyed, it doesn't go back to the active window. // If the focus window is destroyed, it doesn't go back to the active window.
// (e.g. because the net pane was up, and a button on it had focus) // (e.g. because the net pane was up, and a button on it had focus)
if (GetFocus() == NULL && GetActiveWindow() == Window) if (GetFocus() == NULL && GetActiveWindow() == mainwindow.GetHandle())
{ {
// Make sure it's in the foreground and focused. (It probably is // Make sure it's in the foreground and focused. (It probably is
// already foregrounded but may not be focused.) // already foregrounded but may not be focused.)
SetForegroundWindow(Window); SetForegroundWindow(mainwindow.GetHandle());
SetFocus(Window); SetFocus(mainwindow.GetHandle());
// Note that when I start a 2-player game on the same machine, the // Note that when I start a 2-player game on the same machine, the
// window for the game that isn't focused, active, or foregrounded // window for the game that isn't focused, active, or foregrounded
// still receives a WM_ACTIVATEAPP message telling it that it's the // still receives a WM_ACTIVATEAPP message telling it that it's the

View file

@ -65,6 +65,7 @@
#include "zstring.h" #include "zstring.h"
#include "printf.h" #include "printf.h"
#include "cmdlib.h" #include "cmdlib.h"
#include "i_mainwindow.h"
#include <time.h> #include <time.h>
#include <zlib.h> #include <zlib.h>
@ -212,9 +213,6 @@ struct MiniDumpThreadData
// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
void I_FlushBufferedConsoleStuff();
// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
static void AddFile (HANDLE file, const char *filename); static void AddFile (HANDLE file, const char *filename);
@ -445,41 +443,6 @@ void Writef (HANDLE file, const char *format, ...)
// //
//========================================================================== //==========================================================================
static DWORD CALLBACK WriteLogFileStreamer(DWORD_PTR cookie, LPBYTE buffer, LONG cb, LONG *pcb)
{
DWORD didwrite;
LONG p, pp;
// Replace gray foreground color with black.
static const char *badfg = "\\red223\\green223\\blue223;";
// 4321098 765432109 876543210
// 2 1 0
for (p = pp = 0; p < cb; ++p)
{
if (buffer[p] == badfg[pp])
{
++pp;
if (pp == 25)
{
buffer[p - 1] = buffer[p - 2] = buffer[p - 3] =
buffer[p - 9] = buffer[p -10] = buffer[p -11] =
buffer[p -18] = buffer[p -19] = buffer[p -20] = '0';
break;
}
}
else
{
pp = 0;
}
}
if (!WriteFile((HANDLE)cookie, buffer, cb, &didwrite, NULL))
{
return 1;
}
*pcb = didwrite;
return 0;
}
//========================================================================== //==========================================================================
// //
@ -489,15 +452,21 @@ static DWORD CALLBACK WriteLogFileStreamer(DWORD_PTR cookie, LPBYTE buffer, LONG
// //
//========================================================================== //==========================================================================
HANDLE WriteLogFile(HWND edit) HANDLE WriteLogFile()
{ {
HANDLE file; HANDLE file;
file = CreateTempFile(); file = CreateTempFile();
if (file != INVALID_HANDLE_VALUE) if (file != INVALID_HANDLE_VALUE)
{ {
EDITSTREAM streamer = { (DWORD_PTR)file, 0, WriteLogFileStreamer }; auto writeFile = [&](const void* data, uint32_t size, uint32_t& written) -> bool
SendMessage(edit, EM_STREAMOUT, SF_RTF, (LPARAM)&streamer); {
DWORD tmp = 0;
BOOL result = WriteFile(file, data, size, &tmp, nullptr);
written = tmp;
return result == TRUE;
};
mainwindow.GetLog(writeFile);
} }
return file; return file;
} }
@ -510,7 +479,7 @@ HANDLE WriteLogFile(HWND edit)
// //
//========================================================================== //==========================================================================
void CreateCrashLog (const char *custominfo, DWORD customsize, HWND richlog) void CreateCrashLog (const char *custominfo, DWORD customsize)
{ {
// Do not collect information more than once. // Do not collect information more than once.
if (NumFiles != 0) if (NumFiles != 0)
@ -560,11 +529,7 @@ void CreateCrashLog (const char *custominfo, DWORD customsize, HWND richlog)
AddFile (file, "local.txt"); AddFile (file, "local.txt");
} }
} }
if (richlog != NULL) AddFile (WriteLogFile(), "log.rtf");
{
I_FlushBufferedConsoleStuff();
AddFile (WriteLogFile(richlog), "log.rtf");
}
CloseHandle (DbgProcess); CloseHandle (DbgProcess);
} }

View file

@ -53,6 +53,8 @@
#include "keydef.h" #include "keydef.h"
#include "printf.h" #include "printf.h"
#include "i_mainwindow.h"
#define SAFE_RELEASE(x) { if (x != NULL) { x->Release(); x = NULL; } } #define SAFE_RELEASE(x) { if (x != NULL) { x->Release(); x = NULL; } }
// WBEMIDL BITS -- because w32api doesn't have this, either ----------------- // WBEMIDL BITS -- because w32api doesn't have this, either -----------------
@ -266,7 +268,6 @@ protected:
// EXTERNAL DATA DECLARATIONS ---------------------------------------------- // EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern LPDIRECTINPUT8 g_pdi; extern LPDIRECTINPUT8 g_pdi;
extern HWND Window;
// PUBLIC DATA DEFINITIONS ------------------------------------------------- // PUBLIC DATA DEFINITIONS -------------------------------------------------
@ -372,7 +373,7 @@ bool FDInputJoystick::GetDevice()
Printf(TEXTCOLOR_ORANGE "Setting data format for %s failed.\n", Name.GetChars()); Printf(TEXTCOLOR_ORANGE "Setting data format for %s failed.\n", Name.GetChars());
return false; return false;
} }
hr = Device->SetCooperativeLevel(Window, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND); hr = Device->SetCooperativeLevel(mainwindow.GetHandle(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
if (FAILED(hr)) if (FAILED(hr))
{ {
Printf(TEXTCOLOR_ORANGE "Setting cooperative level for %s failed.\n", Name.GetChars()); Printf(TEXTCOLOR_ORANGE "Setting cooperative level for %s failed.\n", Name.GetChars());

View file

@ -86,6 +86,7 @@
#include "printf.h" #include "printf.h"
#include "c_buttons.h" #include "c_buttons.h"
#include "cmdlib.h" #include "cmdlib.h"
#include "i_mainwindow.h"
// Compensate for w32api's lack // Compensate for w32api's lack
#ifndef GET_XBUTTON_WPARAM #ifndef GET_XBUTTON_WPARAM
@ -116,7 +117,6 @@ extern bool ToggleFullscreen;
bool VidResizing; bool VidResizing;
extern BOOL vidactive; extern BOOL vidactive;
extern HWND Window, ConWindow;
EXTERN_CVAR (String, language) EXTERN_CVAR (String, language)
EXTERN_CVAR (Bool, lookstrafe) EXTERN_CVAR (Bool, lookstrafe)
@ -160,7 +160,7 @@ static void I_CheckGUICapture ()
void I_SetMouseCapture() void I_SetMouseCapture()
{ {
SetCapture(Window); SetCapture(mainwindow.GetHandle());
} }
void I_ReleaseMouseCapture() void I_ReleaseMouseCapture()
@ -444,7 +444,7 @@ LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
break; break;
case WM_SIZE: case WM_SIZE:
InvalidateRect (Window, NULL, FALSE); InvalidateRect (hWnd, NULL, FALSE);
break; break;
case WM_KEYDOWN: case WM_KEYDOWN:
@ -820,7 +820,7 @@ IJoystickConfig *I_UpdateDeviceList()
void I_PutInClipboard (const char *str) void I_PutInClipboard (const char *str)
{ {
if (str == NULL || !OpenClipboard (Window)) if (str == NULL || !OpenClipboard (mainwindow.GetHandle()))
return; return;
EmptyClipboard (); EmptyClipboard ();
@ -842,7 +842,7 @@ FString I_GetFromClipboard (bool return_nothing)
HGLOBAL cliphandle; HGLOBAL cliphandle;
wchar_t *clipstr; wchar_t *clipstr;
if (return_nothing || !IsClipboardFormatAvailable (CF_UNICODETEXT) || !OpenClipboard (Window)) if (return_nothing || !IsClipboardFormatAvailable (CF_UNICODETEXT) || !OpenClipboard (mainwindow.GetHandle()))
return retstr; return retstr;
cliphandle = GetClipboardData (CF_UNICODETEXT); cliphandle = GetClipboardData (CF_UNICODETEXT);

View file

@ -40,6 +40,7 @@
#include "i_input.h" #include "i_input.h"
#include "d_eventbase.h" #include "d_eventbase.h"
#include "i_mainwindow.h"
// MACROS ------------------------------------------------------------------ // MACROS ------------------------------------------------------------------
@ -87,7 +88,6 @@ protected:
// EXTERNAL DATA DECLARATIONS ---------------------------------------------- // EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern HWND Window;
extern LPDIRECTINPUT8 g_pdi; extern LPDIRECTINPUT8 g_pdi;
extern LPDIRECTINPUT g_pdi3; extern LPDIRECTINPUT g_pdi3;
extern bool GUICapture; extern bool GUICapture;
@ -347,7 +347,7 @@ ufailit:
return false; return false;
} }
hr = Device->SetCooperativeLevel(Window, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); hr = Device->SetCooperativeLevel(mainwindow.GetHandle(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
if (FAILED(hr)) if (FAILED(hr))
{ {
goto ufailit; goto ufailit;
@ -379,7 +379,7 @@ void FDInputKeyboard::ProcessInput()
DIDEVICEOBJECTDATA od; DIDEVICEOBJECTDATA od;
DWORD dwElements; DWORD dwElements;
HRESULT hr; HRESULT hr;
bool foreground = (GetForegroundWindow() == Window); bool foreground = (GetForegroundWindow() == mainwindow.GetHandle());
for (;;) for (;;)
{ {
@ -448,7 +448,7 @@ bool FRawKeyboard::GetDevice()
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE; rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_KEYBOARD; rid.usUsage = HID_GDP_KEYBOARD;
rid.dwFlags = RIDEV_INPUTSINK; rid.dwFlags = RIDEV_INPUTSINK;
rid.hwndTarget = Window; rid.hwndTarget = mainwindow.GetHandle();
if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
{ {
return false; return false;

View file

@ -80,6 +80,8 @@
#include "startupinfo.h" #include "startupinfo.h"
#include "printf.h" #include "printf.h"
#include "i_mainwindow.h"
// MACROS ------------------------------------------------------------------ // MACROS ------------------------------------------------------------------
// The main window's title. // The main window's title.
@ -94,9 +96,8 @@
// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
void CreateCrashLog (const char *custominfo, DWORD customsize, HWND richedit); void CreateCrashLog (const char *custominfo, DWORD customsize);
void DisplayCrashLog (); void DisplayCrashLog ();
void I_FlushBufferedConsoleStuff();
void DestroyCustomCursor(); void DestroyCustomCursor();
int GameMain(); int GameMain();
@ -120,19 +121,6 @@ HANDLE MainThread;
DWORD MainThreadID; DWORD MainThreadID;
HANDLE StdOut; HANDLE StdOut;
bool FancyStdOut, AttachedStdOut; bool FancyStdOut, AttachedStdOut;
bool ConWindowHidden;
// The main window
HWND Window;
// The subwindows used for startup and error output
HWND ConWindow, GameTitleWindow;
HWND ErrorPane, ProgressBar, NetStartPane, StartupScreen, ErrorIcon;
HFONT GameTitleFont;
LONG GameTitleFontHeight;
LONG DefaultGUIFontHeight;
LONG ErrorIconChar;
FModule Kernel32Module{"Kernel32"}; FModule Kernel32Module{"Kernel32"};
FModule Shell32Module{"Shell32"}; FModule Shell32Module{"Shell32"};
@ -140,7 +128,6 @@ FModule User32Module{"User32"};
// PRIVATE DATA DEFINITIONS ------------------------------------------------ // PRIVATE DATA DEFINITIONS ------------------------------------------------
static const WCHAR WinClassName[] = WGAMENAME "MainWindow";
static HMODULE hwtsapi32; // handle to wtsapi32.dll static HMODULE hwtsapi32; // handle to wtsapi32.dll
// CODE -------------------------------------------------------------------- // CODE --------------------------------------------------------------------
@ -174,155 +161,13 @@ static void UnWTS (void)
ursn unreg = (ursn)GetProcAddress (hwtsapi32, "WTSUnRegisterSessionNotification"); ursn unreg = (ursn)GetProcAddress (hwtsapi32, "WTSUnRegisterSessionNotification");
if (unreg != 0) if (unreg != 0)
{ {
unreg (Window); unreg (mainwindow.GetHandle());
} }
FreeLibrary (hwtsapi32); FreeLibrary (hwtsapi32);
hwtsapi32 = 0; hwtsapi32 = 0;
} }
} }
//==========================================================================
//
// LayoutErrorPane
//
// Lays out the error pane to the desired width, returning the required
// height.
//
//==========================================================================
static int LayoutErrorPane (HWND pane, int w)
{
HWND ctl, ctl_two;
RECT rectc, rectc_two;
// Right-align the Quit button.
ctl = GetDlgItem (pane, IDOK);
GetClientRect (ctl, &rectc); // Find out how big it is.
MoveWindow (ctl, w - rectc.right - 1, 1, rectc.right, rectc.bottom, TRUE);
// Second-right-align the Restart button
ctl_two = GetDlgItem (pane, IDC_BUTTON1);
GetClientRect (ctl_two, &rectc_two); // Find out how big it is.
MoveWindow (ctl_two, w - rectc.right - rectc_two.right - 2, 1, rectc.right, rectc.bottom, TRUE);
InvalidateRect (ctl, NULL, TRUE);
InvalidateRect (ctl_two, NULL, TRUE);
// Return the needed height for this layout
return rectc.bottom + 2;
}
//==========================================================================
//
// LayoutNetStartPane
//
// Lays out the network startup pane to the specified width, returning
// its required height.
//
//==========================================================================
int LayoutNetStartPane (HWND pane, int w)
{
HWND ctl;
RECT margin, rectc;
int staticheight, barheight;
// Determine margin sizes.
SetRect (&margin, 7, 7, 0, 0);
MapDialogRect (pane, &margin);
// Stick the message text in the upper left corner.
ctl = GetDlgItem (pane, IDC_NETSTARTMESSAGE);
GetClientRect (ctl, &rectc);
MoveWindow (ctl, margin.left, margin.top, rectc.right, rectc.bottom, TRUE);
// Stick the count text in the upper right corner.
ctl = GetDlgItem (pane, IDC_NETSTARTCOUNT);
GetClientRect (ctl, &rectc);
MoveWindow (ctl, w - rectc.right - margin.left, margin.top, rectc.right, rectc.bottom, TRUE);
staticheight = rectc.bottom;
// Stretch the progress bar to fill the entire width.
ctl = GetDlgItem (pane, IDC_NETSTARTPROGRESS);
barheight = GetSystemMetrics (SM_CYVSCROLL);
MoveWindow (ctl, margin.left, margin.top*2 + staticheight, w - margin.left*2, barheight, TRUE);
// Center the abort button underneath the progress bar.
ctl = GetDlgItem (pane, IDCANCEL);
GetClientRect (ctl, &rectc);
MoveWindow (ctl, (w - rectc.right) / 2, margin.top*3 + staticheight + barheight, rectc.right, rectc.bottom, TRUE);
return margin.top*4 + staticheight + barheight + rectc.bottom;
}
//==========================================================================
//
// LayoutMainWindow
//
// Lays out the main window with the game title and log controls and
// possibly the error pane and progress bar.
//
//==========================================================================
void LayoutMainWindow (HWND hWnd, HWND pane)
{
RECT rect;
int errorpaneheight = 0;
int bannerheight = 0;
int progressheight = 0;
int netpaneheight = 0;
int leftside = 0;
int w, h;
GetClientRect (hWnd, &rect);
w = rect.right;
h = rect.bottom;
if (GameStartupInfo.Name.IsNotEmpty() && GameTitleWindow != NULL)
{
bannerheight = GameTitleFontHeight + 5;
MoveWindow (GameTitleWindow, 0, 0, w, bannerheight, TRUE);
InvalidateRect (GameTitleWindow, NULL, FALSE);
}
if (ProgressBar != NULL)
{
// Base the height of the progress bar on the height of a scroll bar
// arrow, just as in the progress bar example.
progressheight = GetSystemMetrics (SM_CYVSCROLL);
MoveWindow (ProgressBar, 0, h - progressheight, w, progressheight, TRUE);
}
if (NetStartPane != NULL)
{
netpaneheight = LayoutNetStartPane (NetStartPane, w);
SetWindowPos (NetStartPane, HWND_TOP, 0, h - progressheight - netpaneheight, w, netpaneheight, SWP_SHOWWINDOW);
}
if (pane != NULL)
{
errorpaneheight = LayoutErrorPane (pane, w);
SetWindowPos (pane, HWND_TOP, 0, h - progressheight - netpaneheight - errorpaneheight, w, errorpaneheight, 0);
}
if (ErrorIcon != NULL)
{
leftside = GetSystemMetrics (SM_CXICON) + 6;
MoveWindow (ErrorIcon, 0, bannerheight, leftside, h - bannerheight - errorpaneheight - progressheight - netpaneheight, TRUE);
}
// If there is a startup screen, it covers the log window
if (StartupScreen != NULL)
{
SetWindowPos (StartupScreen, HWND_TOP, leftside, bannerheight, w - leftside,
h - bannerheight - errorpaneheight - progressheight - netpaneheight, SWP_SHOWWINDOW);
InvalidateRect (StartupScreen, NULL, FALSE);
MoveWindow (ConWindow, 0, 0, 0, 0, TRUE);
}
else
{
// The log window uses whatever space is left.
MoveWindow (ConWindow, leftside, bannerheight, w - leftside,
h - bannerheight - errorpaneheight - progressheight - netpaneheight, TRUE);
}
}
//========================================================================== //==========================================================================
// //
// I_SetIWADInfo // I_SetIWADInfo
@ -332,431 +177,7 @@ void LayoutMainWindow (HWND hWnd, HWND pane)
void I_SetIWADInfo() void I_SetIWADInfo()
{ {
// Make the startup banner show itself // Make the startup banner show itself
LayoutMainWindow(Window, NULL); mainwindow.UpdateLayout();
}
//==========================================================================
//
// LConProc
//
// The main window's WndProc during startup. During gameplay, the WndProc
// in i_input.cpp is used instead.
//
//==========================================================================
LRESULT CALLBACK LConProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HWND view;
HDC hdc;
HBRUSH hbr;
HGDIOBJ oldfont;
RECT rect;
SIZE size;
LOGFONT lf;
TEXTMETRIC tm;
HINSTANCE inst = (HINSTANCE)(LONG_PTR)GetWindowLongPtr(hWnd, GWLP_HINSTANCE);
DRAWITEMSTRUCT *drawitem;
CHARFORMAT2W format;
switch (msg)
{
case WM_CREATE:
// Create game title static control
memset (&lf, 0, sizeof(lf));
hdc = GetDC (hWnd);
lf.lfHeight = -MulDiv(12, GetDeviceCaps(hdc, LOGPIXELSY), 72);
lf.lfCharSet = ANSI_CHARSET;
lf.lfWeight = FW_BOLD;
lf.lfPitchAndFamily = VARIABLE_PITCH | FF_ROMAN;
wcscpy (lf.lfFaceName, L"Trebuchet MS");
GameTitleFont = CreateFontIndirect (&lf);
oldfont = SelectObject (hdc, GetStockObject (DEFAULT_GUI_FONT));
GetTextMetrics (hdc, &tm);
DefaultGUIFontHeight = tm.tmHeight;
if (GameTitleFont == NULL)
{
GameTitleFontHeight = DefaultGUIFontHeight;
}
else
{
SelectObject (hdc, GameTitleFont);
GetTextMetrics (hdc, &tm);
GameTitleFontHeight = tm.tmHeight;
}
SelectObject (hdc, oldfont);
// Create log read-only edit control
view = CreateWindowExW (WS_EX_NOPARENTNOTIFY, L"RichEdit20W", nullptr,
WS_CHILD | WS_VISIBLE | WS_VSCROLL |
ES_LEFT | ES_MULTILINE | WS_CLIPSIBLINGS,
0, 0, 0, 0,
hWnd, NULL, inst, NULL);
HRESULT hr;
hr = GetLastError();
if (view == NULL)
{
ReleaseDC (hWnd, hdc);
return -1;
}
SendMessage (view, EM_SETREADONLY, TRUE, 0);
SendMessage (view, EM_EXLIMITTEXT, 0, 0x7FFFFFFE);
SendMessage (view, EM_SETBKGNDCOLOR, 0, RGB(70,70,70));
// Setup default font for the log.
//SendMessage (view, WM_SETFONT, (WPARAM)GetStockObject (DEFAULT_GUI_FONT), FALSE);
format.cbSize = sizeof(format);
format.dwMask = CFM_BOLD | CFM_COLOR | CFM_FACE | CFM_SIZE | CFM_CHARSET;
format.dwEffects = 0;
format.yHeight = 200;
format.crTextColor = RGB(223,223,223);
format.bCharSet = ANSI_CHARSET;
format.bPitchAndFamily = FF_SWISS | VARIABLE_PITCH;
wcscpy(format.szFaceName, L"DejaVu Sans"); // At least I have it. :p
SendMessageW(view, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&format);
ConWindow = view;
ReleaseDC (hWnd, hdc);
view = CreateWindowExW (WS_EX_NOPARENTNOTIFY, L"STATIC", NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SS_OWNERDRAW, 0, 0, 0, 0, hWnd, nullptr, inst, nullptr);
if (view == nullptr)
{
return -1;
}
SetWindowLong (view, GWL_ID, IDC_STATIC_TITLE);
GameTitleWindow = view;
return 0;
case WM_SIZE:
if (wParam != SIZE_MAXHIDE && wParam != SIZE_MAXSHOW)
{
LayoutMainWindow (hWnd, ErrorPane);
}
return 0;
case WM_DRAWITEM:
// Draw title banner.
if (wParam == IDC_STATIC_TITLE && GameStartupInfo.Name.IsNotEmpty())
{
const PalEntry *c;
// Draw the game title strip at the top of the window.
drawitem = (LPDRAWITEMSTRUCT)lParam;
// Draw the background.
rect = drawitem->rcItem;
rect.bottom -= 1;
c = (const PalEntry *)&GameStartupInfo.BkColor;
hbr = CreateSolidBrush (RGB(c->r,c->g,c->b));
FillRect (drawitem->hDC, &drawitem->rcItem, hbr);
DeleteObject (hbr);
// Calculate width of the title string.
SetTextAlign (drawitem->hDC, TA_TOP);
oldfont = SelectObject (drawitem->hDC, GameTitleFont != NULL ? GameTitleFont : (HFONT)GetStockObject (DEFAULT_GUI_FONT));
auto widename = GameStartupInfo.Name.WideString();
GetTextExtentPoint32W (drawitem->hDC, widename.c_str(), (int)widename.length(), &size);
// Draw the title.
c = (const PalEntry *)&GameStartupInfo.FgColor;
SetTextColor (drawitem->hDC, RGB(c->r,c->g,c->b));
SetBkMode (drawitem->hDC, TRANSPARENT);
TextOutW (drawitem->hDC, rect.left + (rect.right - rect.left - size.cx) / 2, 2, widename.c_str(), (int)widename.length());
SelectObject (drawitem->hDC, oldfont);
return TRUE;
}
// Draw startup screen
else if (wParam == IDC_STATIC_STARTUP)
{
if (StartupScreen != NULL)
{
drawitem = (LPDRAWITEMSTRUCT)lParam;
rect = drawitem->rcItem;
// Windows expects DIBs to be bottom-up but ours is top-down,
// so flip it vertically while drawing it.
StretchDIBits (drawitem->hDC, rect.left, rect.bottom - 1, rect.right - rect.left, rect.top - rect.bottom,
0, 0, StartupBitmap->bmiHeader.biWidth, StartupBitmap->bmiHeader.biHeight,
ST_Util_BitsForBitmap(StartupBitmap), reinterpret_cast<const BITMAPINFO*>(StartupBitmap), DIB_RGB_COLORS, SRCCOPY);
// If the title banner is gone, then this is an ENDOOM screen, so draw a short prompt
// where the command prompt would have been in DOS.
if (GameTitleWindow == NULL)
{
auto quitmsg = WideString(GStrings("TXT_QUITENDOOM"));
SetTextColor (drawitem->hDC, RGB(240,240,240));
SetBkMode (drawitem->hDC, TRANSPARENT);
oldfont = SelectObject (drawitem->hDC, (HFONT)GetStockObject (DEFAULT_GUI_FONT));
TextOutW (drawitem->hDC, 3, drawitem->rcItem.bottom - DefaultGUIFontHeight - 3, quitmsg.c_str(), (int)quitmsg.length());
SelectObject (drawitem->hDC, oldfont);
}
return TRUE;
}
}
// Draw stop icon.
else if (wParam == IDC_ICONPIC)
{
HICON icon;
POINTL char_pos;
drawitem = (LPDRAWITEMSTRUCT)lParam;
// This background color should match the edit control's.
hbr = CreateSolidBrush (RGB(70,70,70));
FillRect (drawitem->hDC, &drawitem->rcItem, hbr);
DeleteObject (hbr);
// Draw the icon aligned with the first line of error text.
SendMessage (ConWindow, EM_POSFROMCHAR, (WPARAM)&char_pos, ErrorIconChar);
icon = (HICON)LoadImage (0, IDI_ERROR, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
DrawIcon (drawitem->hDC, 6, char_pos.y, icon);
return TRUE;
}
return FALSE;
case WM_COMMAND:
if (ErrorIcon != NULL && (HWND)lParam == ConWindow && HIWORD(wParam) == EN_UPDATE)
{
// Be sure to redraw the error icon if the edit control changes.
InvalidateRect (ErrorIcon, NULL, TRUE);
return 0;
}
break;
case WM_CLOSE:
PostQuitMessage (0);
break;
case WM_DESTROY:
if (GameTitleFont != NULL)
{
DeleteObject (GameTitleFont);
}
break;
}
return DefWindowProc (hWnd, msg, wParam, lParam);
}
//==========================================================================
//
// ErrorPaneProc
//
// DialogProc for the error pane.
//
//==========================================================================
bool restartrequest;
void CheckForRestart()
{
if (restartrequest)
{
HMODULE hModule = GetModuleHandleW(NULL);
WCHAR path[MAX_PATH];
GetModuleFileNameW(hModule, path, MAX_PATH);
ShellExecuteW(NULL, L"open", path, GetCommandLineW(), NULL, SW_SHOWNORMAL);
}
restartrequest = false;
}
INT_PTR CALLBACK ErrorPaneProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_INITDIALOG:
// Appear in the main window.
LayoutMainWindow (GetParent (hDlg), hDlg);
return TRUE;
case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED)
{
if (LOWORD(wParam) == IDC_BUTTON1) // we pressed the restart button, so run GZDoom again
{
restartrequest = true;
}
PostQuitMessage (0);
return TRUE;
}
break;
}
return FALSE;
}
//==========================================================================
//
// I_SetWndProc
//
// Sets the main WndProc, hides all the child windows, and starts up
// in-game input.
//
//==========================================================================
void I_SetWndProc()
{
if (GetWindowLongPtr (Window, GWLP_USERDATA) == 0)
{
SetWindowLongPtr (Window, GWLP_USERDATA, 1);
SetWindowLongPtr (Window, GWLP_WNDPROC, (WLONG_PTR)WndProc);
ShowWindow (ConWindow, SW_HIDE);
ShowWindow(ProgressBar, SW_HIDE);
ConWindowHidden = true;
ShowWindow (GameTitleWindow, SW_HIDE);
I_InitInput (Window);
}
}
//==========================================================================
//
// RestoreConView
//
// Returns the main window to its startup state.
//
//==========================================================================
void RestoreConView()
{
HDC screenDC = GetDC(0);
int dpi = GetDeviceCaps(screenDC, LOGPIXELSX);
ReleaseDC(0, screenDC);
int width = (512 * dpi + 96 / 2) / 96;
int height = (384 * dpi + 96 / 2) / 96;
// Make sure the window has a frame in case it was fullscreened.
SetWindowLongPtr (Window, GWL_STYLE, WS_VISIBLE|WS_OVERLAPPEDWINDOW);
if (GetWindowLong (Window, GWL_EXSTYLE) & WS_EX_TOPMOST)
{
SetWindowPos (Window, HWND_BOTTOM, 0, 0, width, height,
SWP_DRAWFRAME | SWP_NOCOPYBITS | SWP_NOMOVE);
SetWindowPos (Window, HWND_TOP, 0, 0, 0, 0, SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOSIZE);
}
else
{
SetWindowPos (Window, NULL, 0, 0, width, height,
SWP_DRAWFRAME | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOZORDER);
}
SetWindowLongPtr (Window, GWLP_WNDPROC, (WLONG_PTR)LConProc);
ShowWindow (ConWindow, SW_SHOW);
ConWindowHidden = false;
ShowWindow (GameTitleWindow, SW_SHOW);
I_ShutdownInput (); // Make sure the mouse pointer is available.
// Make sure the progress bar isn't visible.
DeleteStartupScreen();
}
//==========================================================================
//
// ShowErrorPane
//
// Shows an error message, preferably in the main window, but it can
// use a normal message box too.
//
//==========================================================================
void ShowErrorPane(const char *text)
{
auto widetext = WideString(text);
if (Window == nullptr || ConWindow == nullptr)
{
if (text != NULL)
{
MessageBoxW (Window, widetext.c_str(),
WGAMENAME " Fatal Error", MB_OK|MB_ICONSTOP|MB_TASKMODAL);
}
return;
}
if (StartScreen != NULL) // Ensure that the network pane is hidden.
{
StartScreen->NetDone();
}
if (text != NULL)
{
FStringf caption("Fatal Error - " GAMENAME " %s " X64 " (%s)", GetVersionString(), GetGitTime());
auto wcaption = caption.WideString();
SetWindowTextW (Window, wcaption.c_str());
ErrorIcon = CreateWindowExW (WS_EX_NOPARENTNOTIFY, L"STATIC", NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SS_OWNERDRAW, 0, 0, 0, 0, Window, NULL, g_hInst, NULL);
if (ErrorIcon != NULL)
{
SetWindowLong (ErrorIcon, GWL_ID, IDC_ICONPIC);
}
}
ErrorPane = CreateDialogParam (g_hInst, MAKEINTRESOURCE(IDD_ERRORPANE), Window, ErrorPaneProc, (LONG_PTR)NULL);
if (text != NULL)
{
CHARRANGE end;
CHARFORMAT2 oldformat, newformat;
PARAFORMAT2 paraformat;
// Append the error message to the log.
end.cpMax = end.cpMin = GetWindowTextLength (ConWindow);
SendMessage (ConWindow, EM_EXSETSEL, 0, (LPARAM)&end);
// Remember current charformat.
oldformat.cbSize = sizeof(oldformat);
SendMessage (ConWindow, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&oldformat);
// Use bigger font and standout colors.
newformat.cbSize = sizeof(newformat);
newformat.dwMask = CFM_BOLD | CFM_COLOR | CFM_SIZE;
newformat.dwEffects = CFE_BOLD;
newformat.yHeight = oldformat.yHeight * 5 / 4;
newformat.crTextColor = RGB(255,170,170);
SendMessage (ConWindow, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&newformat);
// Indent the rest of the text to make the error message stand out a little more.
paraformat.cbSize = sizeof(paraformat);
paraformat.dwMask = PFM_STARTINDENT | PFM_OFFSETINDENT | PFM_RIGHTINDENT;
paraformat.dxStartIndent = paraformat.dxOffset = paraformat.dxRightIndent = 120;
SendMessage (ConWindow, EM_SETPARAFORMAT, 0, (LPARAM)&paraformat);
SendMessageW (ConWindow, EM_REPLACESEL, FALSE, (LPARAM)L"\n");
// Find out where the error lines start for the error icon display control.
SendMessage (ConWindow, EM_EXGETSEL, 0, (LPARAM)&end);
ErrorIconChar = end.cpMax;
// Now start adding the actual error message.
SendMessageW (ConWindow, EM_REPLACESEL, FALSE, (LPARAM)L"Execution could not continue.\n\n");
// Restore old charformat but with light yellow text.
oldformat.crTextColor = RGB(255,255,170);
SendMessage (ConWindow, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&oldformat);
// Add the error text.
SendMessageW (ConWindow, EM_REPLACESEL, FALSE, (LPARAM)widetext.c_str());
// Make sure the error text is not scrolled below the window.
SendMessage (ConWindow, EM_LINESCROLL, 0, SendMessage (ConWindow, EM_GETLINECOUNT, 0, 0));
// The above line scrolled everything off the screen, so pretend to move the scroll
// bar thumb, which clamps to not show any extra lines if it doesn't need to.
SendMessage (ConWindow, EM_SCROLL, SB_PAGEDOWN, 0);
}
BOOL bRet;
MSG msg;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (bRet == -1)
{
MessageBoxW (Window, widetext.c_str(), WGAMENAME " Fatal Error", MB_OK|MB_ICONSTOP|MB_TASKMODAL);
return;
}
else if (!IsDialogMessage (ErrorPane, &msg))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
}
}
void PeekThreadedErrorPane()
{
// Allow SendMessage from another thread to call its message handler so that it can display the crash dialog
MSG msg;
PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE);
} }
static void UnTbp() static void UnTbp()
@ -904,44 +325,9 @@ int DoMain (HINSTANCE hInstance)
x = y = 0; x = y = 0;
} }
WNDCLASS WndClass;
WndClass.style = 0;
WndClass.lpfnWndProc = LConProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(IDI_ICON1));
WndClass.hCursor = LoadCursor (NULL, IDC_ARROW);
WndClass.hbrBackground = NULL;
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = WinClassName;
/* register this new class with Windows */
if (!RegisterClass((LPWNDCLASS)&WndClass))
{
MessageBoxA(nullptr, "Could not register window class", "Fatal", MB_ICONEXCLAMATION|MB_OK);
exit(-1);
}
/* create window */ /* create window */
FStringf caption("" GAMENAME " %s " X64 " (%s)", GetVersionString(), GetGitTime()); FStringf caption("" GAMENAME " %s " X64 " (%s)", GetVersionString(), GetGitTime());
std::wstring wcaption = caption.WideString(); mainwindow.Create(caption, x, y, width, height);
Window = CreateWindowExW(
WS_EX_APPWINDOW,
WinClassName,
wcaption.c_str(),
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
x, y, width, height,
(HWND) NULL,
(HMENU) NULL,
hInstance,
NULL);
if (!Window)
{
MessageBoxA(nullptr, "Unable to create main window", "Fatal", MB_ICONEXCLAMATION|MB_OK);
exit(-1);
}
if (kernel != NULL) if (kernel != NULL)
{ {
@ -957,7 +343,7 @@ int DoMain (HINSTANCE hInstance)
if (hwtsapi32 != 0) if (hwtsapi32 != 0)
{ {
FARPROC reg = GetProcAddress (hwtsapi32, "WTSRegisterSessionNotification"); FARPROC reg = GetProcAddress (hwtsapi32, "WTSRegisterSessionNotification");
if (reg == 0 || !((BOOL(WINAPI *)(HWND, DWORD))reg) (Window, NOTIFY_FOR_THIS_SESSION)) if (reg == 0 || !((BOOL(WINAPI *)(HWND, DWORD))reg) (mainwindow.GetHandle(), NOTIFY_FOR_THIS_SESSION))
{ {
FreeLibrary (hwtsapi32); FreeLibrary (hwtsapi32);
hwtsapi32 = 0; hwtsapi32 = 0;
@ -970,7 +356,7 @@ int DoMain (HINSTANCE hInstance)
} }
} }
GetClientRect (Window, &cRect); GetClientRect (mainwindow.GetHandle(), &cRect);
WinWidth = cRect.right; WinWidth = cRect.right;
WinHeight = cRect.bottom; WinHeight = cRect.bottom;
@ -979,7 +365,7 @@ int DoMain (HINSTANCE hInstance)
atexit (UnCOM); atexit (UnCOM);
int ret = GameMain (); int ret = GameMain ();
CheckForRestart(); mainwindow.CheckForRestart();
DestroyCustomCursor(); DestroyCustomCursor();
if (ret == 1337) // special exit code for 'norun'. if (ret == 1337) // special exit code for 'norun'.
@ -991,7 +377,7 @@ int DoMain (HINSTANCE hInstance)
DWORD bytes; DWORD bytes;
HANDLE stdinput = GetStdHandle(STD_INPUT_HANDLE); HANDLE stdinput = GetStdHandle(STD_INPUT_HANDLE);
ShowWindow(Window, SW_HIDE); ShowWindow(mainwindow.GetHandle(), SW_HIDE);
WriteFile(StdOut, "Press any key to exit...", 24, &bytes, NULL); WriteFile(StdOut, "Press any key to exit...", 24, &bytes, NULL);
FlushConsoleInputBuffer(stdinput); FlushConsoleInputBuffer(stdinput);
SetConsoleMode(stdinput, 0); SetConsoleMode(stdinput, 0);
@ -999,7 +385,7 @@ int DoMain (HINSTANCE hInstance)
} }
else if (StdOut == NULL) else if (StdOut == NULL)
{ {
ShowErrorPane(NULL); mainwindow.ShowErrorPane(nullptr);
} }
} }
} }
@ -1009,9 +395,8 @@ int DoMain (HINSTANCE hInstance)
void I_ShowFatalError(const char *msg) void I_ShowFatalError(const char *msg)
{ {
I_ShutdownGraphics (); I_ShutdownGraphics ();
RestoreConView (); mainwindow.RestoreConView();
S_StopMusic(true); S_StopMusic(true);
I_FlushBufferedConsoleStuff();
if (CVMAbortException::stacktrace.IsNotEmpty()) if (CVMAbortException::stacktrace.IsNotEmpty())
{ {
@ -1020,7 +405,7 @@ void I_ShowFatalError(const char *msg)
if (!batchrun) if (!batchrun)
{ {
ShowErrorPane(msg); mainwindow.ShowErrorPane(msg);
} }
else else
{ {
@ -1087,7 +472,7 @@ void CALLBACK ExitFatally (ULONG_PTR dummy)
{ {
SetUnhandledExceptionFilter (ExitMessedUp); SetUnhandledExceptionFilter (ExitMessedUp);
I_ShutdownGraphics (); I_ShutdownGraphics ();
RestoreConView (); mainwindow.RestoreConView ();
DisplayCrashLog (); DisplayCrashLog ();
exit(-1); exit(-1);
} }
@ -1122,7 +507,7 @@ LONG WINAPI CatchAllExceptions (LPEXCEPTION_POINTERS info)
CrashPointers = *info; CrashPointers = *info;
if (sysCallbacks.CrashInfo && custominfo) sysCallbacks.CrashInfo(custominfo, 16384, "\r\n"); if (sysCallbacks.CrashInfo && custominfo) sysCallbacks.CrashInfo(custominfo, 16384, "\r\n");
CreateCrashLog (custominfo, (DWORD)strlen(custominfo), ConWindow); CreateCrashLog (custominfo, (DWORD)strlen(custominfo));
// If the main thread crashed, then make it clean up after itself. // If the main thread crashed, then make it clean up after itself.
// Otherwise, put the crashing thread to sleep and signal the main thread to clean up. // Otherwise, put the crashing thread to sleep and signal the main thread to clean up.
@ -1208,7 +593,7 @@ int WINAPI wWinMain (HINSTANCE hInstance, HINSTANCE nothing, LPWSTR cmdline, int
*(int *)0 = 0; *(int *)0 = 0;
} }
__except(CrashPointers = *GetExceptionInformation(), __except(CrashPointers = *GetExceptionInformation(),
CreateCrashLog ("TestCrash", 9, NULL), EXCEPTION_EXECUTE_HANDLER) CreateCrashLog ("TestCrash", 9), EXCEPTION_EXECUTE_HANDLER)
{ {
} }
DisplayCrashLog (); DisplayCrashLog ();
@ -1221,7 +606,7 @@ int WINAPI wWinMain (HINSTANCE hInstance, HINSTANCE nothing, LPWSTR cmdline, int
infiniterecursion(1); infiniterecursion(1);
} }
__except(CrashPointers = *GetExceptionInformation(), __except(CrashPointers = *GetExceptionInformation(),
CreateCrashLog ("TestStackCrash", 14, NULL), EXCEPTION_EXECUTE_HANDLER) CreateCrashLog ("TestStackCrash", 14), EXCEPTION_EXECUTE_HANDLER)
{ {
} }
DisplayCrashLog (); DisplayCrashLog ();
@ -1287,5 +672,5 @@ void I_SetWindowTitle(const char* caption)
{ {
widecaption = WideString(caption); widecaption = WideString(caption);
} }
SetWindowText(Window, widecaption.c_str()); SetWindowText(mainwindow.GetHandle(), widecaption.c_str());
} }

View file

@ -0,0 +1,935 @@
#include "i_mainwindow.h"
#include "resource.h"
#include "startupinfo.h"
#include "gstrings.h"
#include "palentry.h"
#include "st_start.h"
#include "i_input.h"
#include "version.h"
#include "utf8.h"
#include "v_font.h"
#include <richedit.h>
#include <shellapi.h>
#include <commctrl.h>
#ifdef _M_X64
#define X64 " 64-bit"
#else
#define X64 ""
#endif
MainWindow mainwindow;
void MainWindow::Create(const FString& caption, int x, int y, int width, int height)
{
static const WCHAR WinClassName[] = L"MainWindow";
HINSTANCE hInstance = GetModuleHandle(0);
WNDCLASS WndClass;
WndClass.style = 0;
WndClass.lpfnWndProc = LConProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = NULL;
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = WinClassName;
/* register this new class with Windows */
if (!RegisterClass((LPWNDCLASS)&WndClass))
{
MessageBoxA(nullptr, "Could not register window class", "Fatal", MB_ICONEXCLAMATION | MB_OK);
exit(-1);
}
std::wstring wcaption = caption.WideString();
Window = CreateWindowExW(
WS_EX_APPWINDOW,
WinClassName,
wcaption.c_str(),
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
x, y, width, height,
(HWND)NULL,
(HMENU)NULL,
hInstance,
NULL);
if (!Window)
{
MessageBoxA(nullptr, "Unable to create main window", "Fatal", MB_ICONEXCLAMATION | MB_OK);
exit(-1);
}
}
void MainWindow::HideGameTitleWindow()
{
if (GameTitleWindow != 0)
{
DestroyWindow(GameTitleWindow);
GameTitleWindow = 0;
}
}
int MainWindow::GetGameTitleWindowHeight()
{
if (GameTitleWindow != 0)
{
RECT rect;
GetClientRect(GameTitleWindow, &rect);
return rect.bottom;
}
else
{
return 0;
}
}
// Sets the main WndProc, hides all the child windows, and starts up in-game input.
void MainWindow::ShowGameView()
{
if (GetWindowLongPtr(Window, GWLP_USERDATA) == 0)
{
SetWindowLongPtr(Window, GWLP_USERDATA, 1);
SetWindowLongPtr(Window, GWLP_WNDPROC, (LONG_PTR)WndProc);
ShowWindow(ConWindow, SW_HIDE);
ShowWindow(ProgressBar, SW_HIDE);
ConWindowHidden = true;
ShowWindow(GameTitleWindow, SW_HIDE);
I_InitInput(Window);
}
}
// Returns the main window to its startup state.
void MainWindow::RestoreConView()
{
HDC screenDC = GetDC(0);
int dpi = GetDeviceCaps(screenDC, LOGPIXELSX);
ReleaseDC(0, screenDC);
int width = (512 * dpi + 96 / 2) / 96;
int height = (384 * dpi + 96 / 2) / 96;
// Make sure the window has a frame in case it was fullscreened.
SetWindowLongPtr(Window, GWL_STYLE, WS_VISIBLE | WS_OVERLAPPEDWINDOW);
if (GetWindowLong(Window, GWL_EXSTYLE) & WS_EX_TOPMOST)
{
SetWindowPos(Window, HWND_BOTTOM, 0, 0, width, height, SWP_DRAWFRAME | SWP_NOCOPYBITS | SWP_NOMOVE);
SetWindowPos(Window, HWND_TOP, 0, 0, 0, 0, SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOSIZE);
}
else
{
SetWindowPos(Window, NULL, 0, 0, width, height, SWP_DRAWFRAME | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOZORDER);
}
SetWindowLongPtr(Window, GWLP_WNDPROC, (LONG_PTR)LConProc);
ShowWindow(ConWindow, SW_SHOW);
ConWindowHidden = false;
ShowWindow(GameTitleWindow, SW_SHOW);
I_ShutdownInput(); // Make sure the mouse pointer is available.
// Make sure the progress bar isn't visible.
DeleteStartupScreen();
FlushBufferedConsoleStuff();
}
// Shows an error message, preferably in the main window, but it can use a normal message box too.
void MainWindow::ShowErrorPane(const char* text)
{
auto widetext = WideString(text);
if (Window == nullptr || ConWindow == nullptr)
{
if (text != NULL)
{
FStringf caption("Fatal Error - " GAMENAME " %s " X64 " (%s)", GetVersionString(), GetGitTime());
MessageBoxW(Window, widetext.c_str(),caption.WideString().c_str(), MB_OK | MB_ICONSTOP | MB_TASKMODAL);
}
return;
}
if (StartScreen != NULL) // Ensure that the network pane is hidden.
{
StartScreen->NetDone();
}
if (text != NULL)
{
FStringf caption("Fatal Error - " GAMENAME " %s " X64 " (%s)", GetVersionString(), GetGitTime());
auto wcaption = caption.WideString();
SetWindowTextW(Window, wcaption.c_str());
ErrorIcon = CreateWindowExW(WS_EX_NOPARENTNOTIFY, L"STATIC", NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SS_OWNERDRAW, 0, 0, 0, 0, Window, NULL, GetModuleHandle(0), NULL);
if (ErrorIcon != NULL)
{
SetWindowLong(ErrorIcon, GWL_ID, IDC_ICONPIC);
}
}
ErrorPane = CreateDialogParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_ERRORPANE), Window, ErrorPaneProc, (LONG_PTR)NULL);
if (text != NULL)
{
CHARRANGE end;
CHARFORMAT2 oldformat, newformat;
PARAFORMAT2 paraformat;
// Append the error message to the log.
end.cpMax = end.cpMin = GetWindowTextLength(ConWindow);
SendMessage(ConWindow, EM_EXSETSEL, 0, (LPARAM)&end);
// Remember current charformat.
oldformat.cbSize = sizeof(oldformat);
SendMessage(ConWindow, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&oldformat);
// Use bigger font and standout colors.
newformat.cbSize = sizeof(newformat);
newformat.dwMask = CFM_BOLD | CFM_COLOR | CFM_SIZE;
newformat.dwEffects = CFE_BOLD;
newformat.yHeight = oldformat.yHeight * 5 / 4;
newformat.crTextColor = RGB(255, 170, 170);
SendMessage(ConWindow, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&newformat);
// Indent the rest of the text to make the error message stand out a little more.
paraformat.cbSize = sizeof(paraformat);
paraformat.dwMask = PFM_STARTINDENT | PFM_OFFSETINDENT | PFM_RIGHTINDENT;
paraformat.dxStartIndent = paraformat.dxOffset = paraformat.dxRightIndent = 120;
SendMessage(ConWindow, EM_SETPARAFORMAT, 0, (LPARAM)&paraformat);
SendMessageW(ConWindow, EM_REPLACESEL, FALSE, (LPARAM)L"\n");
// Find out where the error lines start for the error icon display control.
SendMessage(ConWindow, EM_EXGETSEL, 0, (LPARAM)&end);
ErrorIconChar = end.cpMax;
// Now start adding the actual error message.
SendMessageW(ConWindow, EM_REPLACESEL, FALSE, (LPARAM)L"Execution could not continue.\n\n");
// Restore old charformat but with light yellow text.
oldformat.crTextColor = RGB(255, 255, 170);
SendMessage(ConWindow, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&oldformat);
// Add the error text.
SendMessageW(ConWindow, EM_REPLACESEL, FALSE, (LPARAM)widetext.c_str());
// Make sure the error text is not scrolled below the window.
SendMessage(ConWindow, EM_LINESCROLL, 0, SendMessage(ConWindow, EM_GETLINECOUNT, 0, 0));
// The above line scrolled everything off the screen, so pretend to move the scroll
// bar thumb, which clamps to not show any extra lines if it doesn't need to.
SendMessage(ConWindow, EM_SCROLL, SB_PAGEDOWN, 0);
}
BOOL bRet;
MSG msg;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (bRet == -1)
{
MessageBoxW(Window, widetext.c_str(), WGAMENAME " Fatal Error", MB_OK | MB_ICONSTOP | MB_TASKMODAL);
return;
}
else if (!IsDialogMessage(ErrorPane, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
void MainWindow::ShowProgressBar(int maxpos)
{
if (ProgressBar == 0)
ProgressBar = CreateWindowEx(0, PROGRESS_CLASS, 0, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, 0, 0, 0, 0, Window, 0, GetModuleHandle(0), 0);
SendMessage(ProgressBar, PBM_SETRANGE, 0, MAKELPARAM(0, maxpos));
LayoutMainWindow(Window, 0);
}
void MainWindow::HideProgressBar()
{
if (ProgressBar != 0)
{
DestroyWindow(ProgressBar);
ProgressBar = 0;
LayoutMainWindow(Window, 0);
}
}
void MainWindow::SetProgressPos(int pos)
{
if (ProgressBar != 0)
SendMessage(ProgressBar, PBM_SETPOS, pos, 0);
}
// DialogProc for the network startup pane. It just waits for somebody to click a button, and the only button available is the abort one.
static INT_PTR CALLBACK NetStartPaneProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL)
{
PostQuitMessage(0);
return TRUE;
}
return FALSE;
}
void MainWindow::ShowNetStartPane(const char* message, int maxpos)
{
// Create the dialog child window
if (NetStartPane == NULL)
{
NetStartPane = CreateDialogParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_NETSTARTPANE), Window, NetStartPaneProc, 0);
if (ProgressBar != 0)
{
DestroyWindow(ProgressBar);
ProgressBar = 0;
}
RECT winrect;
GetWindowRect(Window, &winrect);
SetWindowPos(Window, NULL, 0, 0,
winrect.right - winrect.left, winrect.bottom - winrect.top + LayoutNetStartPane(NetStartPane, 0),
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
LayoutMainWindow(Window, NULL);
SetFocus(NetStartPane);
}
// Set the message text
std::wstring wmessage = WideString(message);
SetDlgItemTextW(NetStartPane, IDC_NETSTARTMESSAGE, wmessage.c_str());
// Set the progress bar range
NetStartMaxPos = maxpos;
HWND ctl = GetDlgItem(NetStartPane, IDC_NETSTARTPROGRESS);
if (maxpos == 0)
{
SendMessage(ctl, PBM_SETMARQUEE, TRUE, 100);
SetWindowLong(ctl, GWL_STYLE, GetWindowLong(ctl, GWL_STYLE) | PBS_MARQUEE);
SetDlgItemTextW(NetStartPane, IDC_NETSTARTCOUNT, L"");
}
else
{
SendMessage(ctl, PBM_SETMARQUEE, FALSE, 0);
SetWindowLong(ctl, GWL_STYLE, GetWindowLong(ctl, GWL_STYLE) & (~PBS_MARQUEE));
SendMessage(ctl, PBM_SETRANGE, 0, MAKELPARAM(0, maxpos));
if (maxpos == 1)
{
SendMessage(ctl, PBM_SETPOS, 1, 0);
SetDlgItemTextW(NetStartPane, IDC_NETSTARTCOUNT, L"");
}
}
}
void MainWindow::HideNetStartPane()
{
if (NetStartPane != 0)
{
DestroyWindow(NetStartPane);
NetStartPane = 0;
LayoutMainWindow(Window, 0);
}
}
void MainWindow::SetNetStartProgress(int pos)
{
if (NetStartPane != 0 && NetStartMaxPos > 1)
{
char buf[16];
mysnprintf(buf, countof(buf), "%d/%d", pos, NetStartMaxPos);
SetDlgItemTextA(NetStartPane, IDC_NETSTARTCOUNT, buf);
SendDlgItemMessage(NetStartPane, IDC_NETSTARTPROGRESS, PBM_SETPOS, min(pos, NetStartMaxPos), 0);
}
}
bool MainWindow::RunMessageLoop(bool (*timer_callback)(void*), void* userdata)
{
BOOL bRet;
MSG msg;
if (SetTimer(Window, 1337, 500, NULL) == 0)
{
I_FatalError("Could not set network synchronization timer.");
}
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (bRet == -1)
{
KillTimer(Window, 1337);
return false;
}
else
{
if (msg.message == WM_TIMER && msg.hwnd == Window && msg.wParam == 1337)
{
if (timer_callback(userdata))
{
KillTimer(Window, 1337);
return true;
}
}
if (!IsDialogMessage(NetStartPane, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
KillTimer(Window, 1337);
return false;
}
void MainWindow::ShowStartupScreen()
{
StartupScreen = CreateWindowEx(WS_EX_NOPARENTNOTIFY, L"STATIC", 0, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SS_OWNERDRAW, 0, 0, 0, 0, Window, 0, GetModuleHandle(0), 0);
if (StartupScreen != 0)
SetWindowLong(StartupScreen, GWL_ID, IDC_STATIC_STARTUP);
}
void MainWindow::HideStartupScreen()
{
if (StartupScreen != 0)
{
DestroyWindow(StartupScreen);
StartupScreen = 0;
}
}
void MainWindow::InvalidateStartupScreen()
{
InvalidateRect(StartupScreen, 0, TRUE);
}
void MainWindow::InvalidateStartupScreen(int left, int top, int right, int bottom)
{
RECT rect;
GetClientRect(StartupScreen, &rect);
rect.left = left * rect.right / StartupBitmap->bmiHeader.biWidth - 1;
rect.top = top * rect.bottom / StartupBitmap->bmiHeader.biHeight - 1;
rect.right = right * rect.right / StartupBitmap->bmiHeader.biWidth + 1;
rect.bottom = bottom * rect.bottom / StartupBitmap->bmiHeader.biHeight + 1;
InvalidateRect(StartupScreen, &rect, FALSE);
}
void MainWindow::UpdateLayout()
{
LayoutMainWindow(Window, 0);
}
// Lays out the main window with the game title and log controls and possibly the error pane and progress bar.
void MainWindow::LayoutMainWindow(HWND hWnd, HWND pane)
{
RECT rect;
int errorpaneheight = 0;
int bannerheight = 0;
int progressheight = 0;
int netpaneheight = 0;
int leftside = 0;
int w, h;
GetClientRect(hWnd, &rect);
w = rect.right;
h = rect.bottom;
if (GameStartupInfo.Name.IsNotEmpty() && GameTitleWindow != NULL)
{
bannerheight = GameTitleFontHeight + 5;
MoveWindow(GameTitleWindow, 0, 0, w, bannerheight, TRUE);
InvalidateRect(GameTitleWindow, NULL, FALSE);
}
if (ProgressBar != NULL)
{
// Base the height of the progress bar on the height of a scroll bar
// arrow, just as in the progress bar example.
progressheight = GetSystemMetrics(SM_CYVSCROLL);
MoveWindow(ProgressBar, 0, h - progressheight, w, progressheight, TRUE);
}
if (NetStartPane != NULL)
{
netpaneheight = LayoutNetStartPane(NetStartPane, w);
SetWindowPos(NetStartPane, HWND_TOP, 0, h - progressheight - netpaneheight, w, netpaneheight, SWP_SHOWWINDOW);
}
if (pane != NULL)
{
errorpaneheight = LayoutErrorPane(pane, w);
SetWindowPos(pane, HWND_TOP, 0, h - progressheight - netpaneheight - errorpaneheight, w, errorpaneheight, 0);
}
if (ErrorIcon != NULL)
{
leftside = GetSystemMetrics(SM_CXICON) + 6;
MoveWindow(ErrorIcon, 0, bannerheight, leftside, h - bannerheight - errorpaneheight - progressheight - netpaneheight, TRUE);
}
// If there is a startup screen, it covers the log window
if (StartupScreen != NULL)
{
SetWindowPos(StartupScreen, HWND_TOP, leftside, bannerheight, w - leftside, h - bannerheight - errorpaneheight - progressheight - netpaneheight, SWP_SHOWWINDOW);
InvalidateRect(StartupScreen, NULL, FALSE);
MoveWindow(ConWindow, 0, 0, 0, 0, TRUE);
}
else
{
// The log window uses whatever space is left.
MoveWindow(ConWindow, leftside, bannerheight, w - leftside, h - bannerheight - errorpaneheight - progressheight - netpaneheight, TRUE);
}
}
// Lays out the error pane to the desired width, returning the required height.
int MainWindow::LayoutErrorPane(HWND pane, int w)
{
HWND ctl, ctl_two;
RECT rectc, rectc_two;
// Right-align the Quit button.
ctl = GetDlgItem(pane, IDOK);
GetClientRect(ctl, &rectc); // Find out how big it is.
MoveWindow(ctl, w - rectc.right - 1, 1, rectc.right, rectc.bottom, TRUE);
// Second-right-align the Restart button
ctl_two = GetDlgItem(pane, IDC_BUTTON1);
GetClientRect(ctl_two, &rectc_two); // Find out how big it is.
MoveWindow(ctl_two, w - rectc.right - rectc_two.right - 2, 1, rectc.right, rectc.bottom, TRUE);
InvalidateRect(ctl, NULL, TRUE);
InvalidateRect(ctl_two, NULL, TRUE);
// Return the needed height for this layout
return rectc.bottom + 2;
}
// Lays out the network startup pane to the specified width, returning its required height.
int MainWindow::LayoutNetStartPane(HWND pane, int w)
{
HWND ctl;
RECT margin, rectc;
int staticheight, barheight;
// Determine margin sizes.
SetRect(&margin, 7, 7, 0, 0);
MapDialogRect(pane, &margin);
// Stick the message text in the upper left corner.
ctl = GetDlgItem(pane, IDC_NETSTARTMESSAGE);
GetClientRect(ctl, &rectc);
MoveWindow(ctl, margin.left, margin.top, rectc.right, rectc.bottom, TRUE);
// Stick the count text in the upper right corner.
ctl = GetDlgItem(pane, IDC_NETSTARTCOUNT);
GetClientRect(ctl, &rectc);
MoveWindow(ctl, w - rectc.right - margin.left, margin.top, rectc.right, rectc.bottom, TRUE);
staticheight = rectc.bottom;
// Stretch the progress bar to fill the entire width.
ctl = GetDlgItem(pane, IDC_NETSTARTPROGRESS);
barheight = GetSystemMetrics(SM_CYVSCROLL);
MoveWindow(ctl, margin.left, margin.top * 2 + staticheight, w - margin.left * 2, barheight, TRUE);
// Center the abort button underneath the progress bar.
ctl = GetDlgItem(pane, IDCANCEL);
GetClientRect(ctl, &rectc);
MoveWindow(ctl, (w - rectc.right) / 2, margin.top * 3 + staticheight + barheight, rectc.right, rectc.bottom, TRUE);
return margin.top * 4 + staticheight + barheight + rectc.bottom;
}
void MainWindow::CheckForRestart()
{
if (restartrequest)
{
HMODULE hModule = GetModuleHandleW(NULL);
WCHAR path[MAX_PATH];
GetModuleFileNameW(hModule, path, MAX_PATH);
ShellExecuteW(NULL, L"open", path, GetCommandLineW(), NULL, SW_SHOWNORMAL);
}
restartrequest = false;
}
// The main window's WndProc during startup. During gameplay, the WndProc in i_input.cpp is used instead.
LRESULT MainWindow::LConProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
return mainwindow.OnMessage(hWnd, msg, wParam, lParam);
}
INT_PTR MainWindow::ErrorPaneProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_INITDIALOG:
// Appear in the main window.
mainwindow.LayoutMainWindow(GetParent(hDlg), hDlg);
return TRUE;
case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED)
{
if (LOWORD(wParam) == IDC_BUTTON1) // we pressed the restart button, so run GZDoom again
{
mainwindow.restartrequest = true;
}
PostQuitMessage(0);
return TRUE;
}
break;
}
return FALSE;
}
LRESULT MainWindow::OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE: return OnCreate(hWnd, msg, wParam, lParam);
case WM_SIZE: return OnSize(hWnd, msg, wParam, lParam);
case WM_DRAWITEM: return OnDrawItem(hWnd, msg, wParam, lParam);
case WM_COMMAND: return OnCommand(hWnd, msg, wParam, lParam);
case WM_CLOSE: return OnClose(hWnd, msg, wParam, lParam);
case WM_DESTROY: return OnDestroy(hWnd, msg, wParam, lParam);
default: return DefWindowProc(hWnd, msg, wParam, lParam);
}
}
LRESULT MainWindow::OnCreate(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HWND view;
HDC hdc;
HGDIOBJ oldfont;
LOGFONT lf;
TEXTMETRIC tm;
CHARFORMAT2W format;
HINSTANCE inst = (HINSTANCE)(LONG_PTR)GetWindowLongPtr(hWnd, GWLP_HINSTANCE);
// Create game title static control
memset(&lf, 0, sizeof(lf));
hdc = GetDC(hWnd);
lf.lfHeight = -MulDiv(12, GetDeviceCaps(hdc, LOGPIXELSY), 72);
lf.lfCharSet = ANSI_CHARSET;
lf.lfWeight = FW_BOLD;
lf.lfPitchAndFamily = VARIABLE_PITCH | FF_ROMAN;
wcscpy(lf.lfFaceName, L"Trebuchet MS");
GameTitleFont = CreateFontIndirect(&lf);
oldfont = SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT));
GetTextMetrics(hdc, &tm);
DefaultGUIFontHeight = tm.tmHeight;
if (GameTitleFont == NULL)
{
GameTitleFontHeight = DefaultGUIFontHeight;
}
else
{
SelectObject(hdc, GameTitleFont);
GetTextMetrics(hdc, &tm);
GameTitleFontHeight = tm.tmHeight;
}
SelectObject(hdc, oldfont);
// Create log read-only edit control
view = CreateWindowExW(WS_EX_NOPARENTNOTIFY, L"RichEdit20W", nullptr,
WS_CHILD | WS_VISIBLE | WS_VSCROLL |
ES_LEFT | ES_MULTILINE | WS_CLIPSIBLINGS,
0, 0, 0, 0,
hWnd, NULL, inst, NULL);
HRESULT hr;
hr = GetLastError();
if (view == NULL)
{
ReleaseDC(hWnd, hdc);
return -1;
}
SendMessage(view, EM_SETREADONLY, TRUE, 0);
SendMessage(view, EM_EXLIMITTEXT, 0, 0x7FFFFFFE);
SendMessage(view, EM_SETBKGNDCOLOR, 0, RGB(70, 70, 70));
// Setup default font for the log.
//SendMessage (view, WM_SETFONT, (WPARAM)GetStockObject (DEFAULT_GUI_FONT), FALSE);
format.cbSize = sizeof(format);
format.dwMask = CFM_BOLD | CFM_COLOR | CFM_FACE | CFM_SIZE | CFM_CHARSET;
format.dwEffects = 0;
format.yHeight = 200;
format.crTextColor = RGB(223, 223, 223);
format.bCharSet = ANSI_CHARSET;
format.bPitchAndFamily = FF_SWISS | VARIABLE_PITCH;
wcscpy(format.szFaceName, L"DejaVu Sans"); // At least I have it. :p
SendMessageW(view, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&format);
ConWindow = view;
ReleaseDC(hWnd, hdc);
view = CreateWindowExW(WS_EX_NOPARENTNOTIFY, L"STATIC", NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SS_OWNERDRAW, 0, 0, 0, 0, hWnd, nullptr, inst, nullptr);
if (view == nullptr)
{
return -1;
}
SetWindowLong(view, GWL_ID, IDC_STATIC_TITLE);
GameTitleWindow = view;
return 0;
}
LRESULT MainWindow::OnSize(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (wParam != SIZE_MAXHIDE && wParam != SIZE_MAXSHOW)
{
LayoutMainWindow(hWnd, ErrorPane);
}
return 0;
}
LRESULT MainWindow::OnDrawItem(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HGDIOBJ oldfont;
HBRUSH hbr;
DRAWITEMSTRUCT* drawitem;
RECT rect;
SIZE size;
// Draw title banner.
if (wParam == IDC_STATIC_TITLE && GameStartupInfo.Name.IsNotEmpty())
{
const PalEntry* c;
// Draw the game title strip at the top of the window.
drawitem = (LPDRAWITEMSTRUCT)lParam;
// Draw the background.
rect = drawitem->rcItem;
rect.bottom -= 1;
c = (const PalEntry*)&GameStartupInfo.BkColor;
hbr = CreateSolidBrush(RGB(c->r, c->g, c->b));
FillRect(drawitem->hDC, &drawitem->rcItem, hbr);
DeleteObject(hbr);
// Calculate width of the title string.
SetTextAlign(drawitem->hDC, TA_TOP);
oldfont = SelectObject(drawitem->hDC, GameTitleFont != NULL ? GameTitleFont : (HFONT)GetStockObject(DEFAULT_GUI_FONT));
auto widename = GameStartupInfo.Name.WideString();
GetTextExtentPoint32W(drawitem->hDC, widename.c_str(), (int)widename.length(), &size);
// Draw the title.
c = (const PalEntry*)&GameStartupInfo.FgColor;
SetTextColor(drawitem->hDC, RGB(c->r, c->g, c->b));
SetBkMode(drawitem->hDC, TRANSPARENT);
TextOutW(drawitem->hDC, rect.left + (rect.right - rect.left - size.cx) / 2, 2, widename.c_str(), (int)widename.length());
SelectObject(drawitem->hDC, oldfont);
return TRUE;
}
// Draw startup screen
else if (wParam == IDC_STATIC_STARTUP)
{
if (StartupScreen != NULL)
{
drawitem = (LPDRAWITEMSTRUCT)lParam;
rect = drawitem->rcItem;
// Windows expects DIBs to be bottom-up but ours is top-down,
// so flip it vertically while drawing it.
StretchDIBits(drawitem->hDC, rect.left, rect.bottom - 1, rect.right - rect.left, rect.top - rect.bottom,
0, 0, StartupBitmap->bmiHeader.biWidth, StartupBitmap->bmiHeader.biHeight,
ST_Util_BitsForBitmap(StartupBitmap), reinterpret_cast<const BITMAPINFO*>(StartupBitmap), DIB_RGB_COLORS, SRCCOPY);
// If the title banner is gone, then this is an ENDOOM screen, so draw a short prompt
// where the command prompt would have been in DOS.
if (GameTitleWindow == NULL)
{
auto quitmsg = WideString(GStrings("TXT_QUITENDOOM"));
SetTextColor(drawitem->hDC, RGB(240, 240, 240));
SetBkMode(drawitem->hDC, TRANSPARENT);
oldfont = SelectObject(drawitem->hDC, (HFONT)GetStockObject(DEFAULT_GUI_FONT));
TextOutW(drawitem->hDC, 3, drawitem->rcItem.bottom - DefaultGUIFontHeight - 3, quitmsg.c_str(), (int)quitmsg.length());
SelectObject(drawitem->hDC, oldfont);
}
return TRUE;
}
}
// Draw stop icon.
else if (wParam == IDC_ICONPIC)
{
HICON icon;
POINTL char_pos;
drawitem = (LPDRAWITEMSTRUCT)lParam;
// This background color should match the edit control's.
hbr = CreateSolidBrush(RGB(70, 70, 70));
FillRect(drawitem->hDC, &drawitem->rcItem, hbr);
DeleteObject(hbr);
// Draw the icon aligned with the first line of error text.
SendMessage(ConWindow, EM_POSFROMCHAR, (WPARAM)&char_pos, ErrorIconChar);
icon = (HICON)LoadImage(0, IDI_ERROR, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
DrawIcon(drawitem->hDC, 6, char_pos.y, icon);
return TRUE;
}
return FALSE;
}
LRESULT MainWindow::OnCommand(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (ErrorIcon != NULL && (HWND)lParam == ConWindow && HIWORD(wParam) == EN_UPDATE)
{
// Be sure to redraw the error icon if the edit control changes.
InvalidateRect(ErrorIcon, NULL, TRUE);
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
LRESULT MainWindow::OnClose(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
PostQuitMessage(0);
return DefWindowProc(hWnd, msg, wParam, lParam);
}
LRESULT MainWindow::OnDestroy(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (GameTitleFont != NULL)
{
DeleteObject(GameTitleFont);
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
void MainWindow::PrintStr(const char* cp)
{
if (ConWindowHidden)
{
bufferedConsoleStuff.Push(cp);
}
else
{
DoPrintStr(cp);
}
}
void MainWindow::FlushBufferedConsoleStuff()
{
for (unsigned i = 0; i < bufferedConsoleStuff.Size(); i++)
{
DoPrintStr(bufferedConsoleStuff[i]);
}
bufferedConsoleStuff.Clear();
}
void MainWindow::DoPrintStr(const char* cpt)
{
wchar_t wbuf[256];
int bpos = 0;
CHARRANGE selection = {};
CHARRANGE endselection = {};
LONG lines_before = 0, lines_after = 0;
// Store the current selection and set it to the end so we can append text.
SendMessage(ConWindow, EM_EXGETSEL, 0, (LPARAM)&selection);
endselection.cpMax = endselection.cpMin = GetWindowTextLength(ConWindow);
SendMessage(ConWindow, EM_EXSETSEL, 0, (LPARAM)&endselection);
// GetWindowTextLength and EM_EXSETSEL can disagree on where the end of
// the text is. Find out what EM_EXSETSEL thought it was and use that later.
SendMessage(ConWindow, EM_EXGETSEL, 0, (LPARAM)&endselection);
// Remember how many lines there were before we added text.
lines_before = (LONG)SendMessage(ConWindow, EM_GETLINECOUNT, 0, 0);
const uint8_t* cptr = (const uint8_t*)cpt;
auto outputIt = [&]()
{
wbuf[bpos] = 0;
SendMessageW(ConWindow, EM_REPLACESEL, FALSE, (LPARAM)wbuf);
bpos = 0;
};
while (int chr = GetCharFromString(cptr))
{
if ((chr == TEXTCOLOR_ESCAPE && bpos != 0) || bpos == 255)
{
outputIt();
}
if (chr != TEXTCOLOR_ESCAPE)
{
if (chr >= 0x1D && chr <= 0x1F)
{ // The bar characters, most commonly used to indicate map changes
chr = 0x2550; // Box Drawings Double Horizontal
}
wbuf[bpos++] = chr;
}
else
{
EColorRange range = V_ParseFontColor(cptr, CR_UNTRANSLATED, CR_YELLOW);
if (range != CR_UNDEFINED)
{
// Change the color of future text added to the control.
PalEntry color = V_LogColorFromColorRange(range);
// Change the color.
CHARFORMAT format;
format.cbSize = sizeof(format);
format.dwMask = CFM_COLOR;
format.dwEffects = 0;
format.crTextColor = RGB(color.r, color.g, color.b);
SendMessage(ConWindow, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);
}
}
}
if (bpos != 0)
{
outputIt();
}
// If the old selection was at the end of the text, keep it at the end and
// scroll. Don't scroll if the selection is anywhere else.
if (selection.cpMin == endselection.cpMin && selection.cpMax == endselection.cpMax)
{
selection.cpMax = selection.cpMin = GetWindowTextLength(ConWindow);
lines_after = (LONG)SendMessage(ConWindow, EM_GETLINECOUNT, 0, 0);
if (lines_after > lines_before)
{
SendMessage(ConWindow, EM_LINESCROLL, 0, lines_after - lines_before);
}
}
// Restore the previous selection.
SendMessage(ConWindow, EM_EXSETSEL, 0, (LPARAM)&selection);
// Give the edit control a chance to redraw itself.
I_GetEvent();
}
static DWORD CALLBACK WriteLogFileStreamer(DWORD_PTR cookie, LPBYTE buffer, LONG cb, LONG* pcb)
{
uint32_t didwrite;
LONG p, pp;
// Replace gray foreground color with black.
static const char* badfg = "\\red223\\green223\\blue223;";
// 4321098 765432109 876543210
// 2 1 0
for (p = pp = 0; p < cb; ++p)
{
if (buffer[p] == badfg[pp])
{
++pp;
if (pp == 25)
{
buffer[p - 1] = buffer[p - 2] = buffer[p - 3] =
buffer[p - 9] = buffer[p - 10] = buffer[p - 11] =
buffer[p - 18] = buffer[p - 19] = buffer[p - 20] = '0';
break;
}
}
else
{
pp = 0;
}
}
auto& writeData = *reinterpret_cast<std::function<bool(const void* data, uint32_t size, uint32_t& written)>*>(cookie);
if (!writeData((const void*)buffer, cb, didwrite))
{
return 1;
}
*pcb = didwrite;
return 0;
}
void MainWindow::GetLog(std::function<bool(const void* data, uint32_t size, uint32_t& written)> writeData)
{
FlushBufferedConsoleStuff();
EDITSTREAM streamer = { (DWORD_PTR)&writeData, 0, WriteLogFileStreamer };
SendMessage(ConWindow, EM_STREAMOUT, SF_RTF, (LPARAM)&streamer);
}

View file

@ -0,0 +1,93 @@
#pragma once
#include "zstring.h"
#include "printf.h"
#include <functional>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
// The WndProc used when the game view is active
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
class MainWindow
{
public:
void Create(const FString& title, int x, int y, int width, int height);
void ShowGameView();
void RestoreConView();
void ShowErrorPane(const char* text);
void CheckForRestart();
void HideGameTitleWindow();
int GetGameTitleWindowHeight();
void PrintStr(const char* cp);
void GetLog(std::function<bool(const void* data, uint32_t size, uint32_t& written)> writeFile);
void UpdateLayout();
void ShowProgressBar(int maxpos);
void HideProgressBar();
void SetProgressPos(int pos);
void ShowNetStartPane(const char* message, int maxpos);
void SetNetStartProgress(int pos);
bool RunMessageLoop(bool (*timer_callback)(void*), void* userdata);
void HideNetStartPane();
void ShowStartupScreen();
void HideStartupScreen();
void InvalidateStartupScreen();
void InvalidateStartupScreen(int left, int top, int right, int bottom);
HWND GetHandle() { return Window; }
private:
void LayoutMainWindow(HWND hWnd, HWND pane);
int LayoutErrorPane(HWND pane, int w);
int LayoutNetStartPane(HWND pane, int w);
void DoPrintStr(const char* cpt);
void FlushBufferedConsoleStuff();
LRESULT OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT OnCreate(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT OnSize(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT OnDrawItem(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT OnCommand(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT OnClose(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT OnDestroy(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK LConProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
static INT_PTR CALLBACK ErrorPaneProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
HWND Window = 0;
HFONT GameTitleFont = 0;
LONG GameTitleFontHeight = 0;
LONG DefaultGUIFontHeight = 0;
LONG ErrorIconChar = 0;
bool restartrequest = false;
HWND GameTitleWindow = 0;
HWND ErrorPane = 0;
HWND ErrorIcon = 0;
bool ConWindowHidden = false;
HWND ConWindow = 0;
TArray<FString> bufferedConsoleStuff;
HWND ProgressBar = 0;
HWND NetStartPane = 0;
int NetStartMaxPos = 0;
HWND StartupScreen = 0;
};
extern MainWindow mainwindow;

View file

@ -46,6 +46,7 @@
#include "menustate.h" #include "menustate.h"
#include "keydef.h" #include "keydef.h"
#include "i_interface.h" #include "i_interface.h"
#include "i_mainwindow.h"
// MACROS ------------------------------------------------------------------ // MACROS ------------------------------------------------------------------
@ -138,7 +139,6 @@ static void CenterMouse(int x, int y, LONG *centx, LONG *centy);
// EXTERNAL DATA DECLARATIONS ---------------------------------------------- // EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern HWND Window;
extern LPDIRECTINPUT8 g_pdi; extern LPDIRECTINPUT8 g_pdi;
extern LPDIRECTINPUT g_pdi3; extern LPDIRECTINPUT g_pdi3;
extern bool GUICapture; extern bool GUICapture;
@ -193,11 +193,11 @@ CUSTOM_CVAR (Int, in_mouse, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL)
static void SetCursorState(bool visible) static void SetCursorState(bool visible)
{ {
CursorState = visible || !m_hidepointer; CursorState = visible || !m_hidepointer;
if (GetForegroundWindow() == Window) if (GetForegroundWindow() == mainwindow.GetHandle())
{ {
if (CursorState) if (CursorState)
{ {
SetCursor((HCURSOR)(intptr_t)GetClassLongPtr(Window, GCLP_HCURSOR)); SetCursor((HCURSOR)(intptr_t)GetClassLongPtr(mainwindow.GetHandle(), GCLP_HCURSOR));
} }
else else
{ {
@ -219,7 +219,7 @@ static void CenterMouse(int curx, int cury, LONG *centxp, LONG *centyp)
{ {
RECT rect; RECT rect;
GetWindowRect(Window, &rect); GetWindowRect(mainwindow.GetHandle(), &rect);
int centx = (rect.left + rect.right) >> 1; int centx = (rect.left + rect.right) >> 1;
int centy = (rect.top + rect.bottom) >> 1; int centy = (rect.top + rect.bottom) >> 1;
@ -258,7 +258,7 @@ void I_CheckNativeMouse(bool preferNative, bool eventhandlerresult)
} }
else else
{ {
if ((GetForegroundWindow() != Window) || preferNative || !use_mouse) if ((GetForegroundWindow() != mainwindow.GetHandle()) || preferNative || !use_mouse)
{ {
want_native = true; want_native = true;
} }
@ -490,7 +490,7 @@ bool FRawMouse::GetDevice()
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE; rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_MOUSE; rid.usUsage = HID_GDP_MOUSE;
rid.dwFlags = 0; rid.dwFlags = 0;
rid.hwndTarget = Window; rid.hwndTarget = mainwindow.GetHandle();
if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
{ {
return false; return false;
@ -516,7 +516,7 @@ void FRawMouse::Grab()
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE; rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_MOUSE; rid.usUsage = HID_GDP_MOUSE;
rid.dwFlags = RIDEV_CAPTUREMOUSE | RIDEV_NOLEGACY; rid.dwFlags = RIDEV_CAPTUREMOUSE | RIDEV_NOLEGACY;
rid.hwndTarget = Window; rid.hwndTarget = mainwindow.GetHandle();
if (RegisterRawInputDevices(&rid, 1, sizeof(rid))) if (RegisterRawInputDevices(&rid, 1, sizeof(rid)))
{ {
GetCursorPos(&UngrabbedPointerPos); GetCursorPos(&UngrabbedPointerPos);
@ -718,7 +718,7 @@ ufailit:
return false; return false;
} }
hr = Device->SetCooperativeLevel(Window, DISCL_EXCLUSIVE | DISCL_FOREGROUND); hr = Device->SetCooperativeLevel(mainwindow.GetHandle(), DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if (FAILED(hr)) if (FAILED(hr))
{ {
goto ufailit; goto ufailit;
@ -1066,11 +1066,11 @@ void FWin32Mouse::Grab()
GetCursorPos(&UngrabbedPointerPos); GetCursorPos(&UngrabbedPointerPos);
ClipCursor(NULL); // helps with Win95? ClipCursor(NULL); // helps with Win95?
GetClientRect(Window, &rect); GetClientRect(mainwindow.GetHandle(), &rect);
// Reposition the rect so that it only covers the client area. // Reposition the rect so that it only covers the client area.
ClientToScreen(Window, (LPPOINT)&rect.left); ClientToScreen(mainwindow.GetHandle(), (LPPOINT)&rect.left);
ClientToScreen(Window, (LPPOINT)&rect.right); ClientToScreen(mainwindow.GetHandle(), (LPPOINT)&rect.right);
ClipCursor(&rect); ClipCursor(&rect);
SetCursorState(false); SetCursorState(false);

View file

@ -44,6 +44,8 @@
#include "cmdlib.h" #include "cmdlib.h"
#include "keydef.h" #include "keydef.h"
#include "i_mainwindow.h"
// MACROS ------------------------------------------------------------------ // MACROS ------------------------------------------------------------------
#define DEFAULT_DEADZONE 0.25f #define DEFAULT_DEADZONE 0.25f
@ -214,8 +216,6 @@ struct PS2Descriptor
// EXTERNAL DATA DECLARATIONS ---------------------------------------------- // EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern HWND Window;
// PUBLIC DATA DEFINITIONS ------------------------------------------------- // PUBLIC DATA DEFINITIONS -------------------------------------------------
CUSTOM_CVAR(Bool, joy_ps2raw, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE|CVAR_NOINITCALL) CUSTOM_CVAR(Bool, joy_ps2raw, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE|CVAR_NOINITCALL)
@ -905,7 +905,7 @@ bool FRawPS2Manager::GetDevice()
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE; rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_JOYSTICK; rid.usUsage = HID_GDP_JOYSTICK;
rid.dwFlags = RIDEV_INPUTSINK; rid.dwFlags = RIDEV_INPUTSINK;
rid.hwndTarget = Window; rid.hwndTarget = mainwindow.GetHandle();
if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
{ {
return false; return false;
@ -1273,7 +1273,7 @@ void FRawPS2Manager::DoRegister()
if (!Registered) if (!Registered)
{ {
rid.dwFlags = RIDEV_INPUTSINK; rid.dwFlags = RIDEV_INPUTSINK;
rid.hwndTarget = Window; rid.hwndTarget = mainwindow.GetHandle();
if (RegisterRawInputDevices(&rid, 1, sizeof(rid))) if (RegisterRawInputDevices(&rid, 1, sizeof(rid)))
{ {
Registered = true; Registered = true;

View file

@ -81,6 +81,7 @@
#include "bitmap.h" #include "bitmap.h"
#include "cmdlib.h" #include "cmdlib.h"
#include "i_interface.h" #include "i_interface.h"
#include "i_mainwindow.h"
// MACROS ------------------------------------------------------------------ // MACROS ------------------------------------------------------------------
@ -92,10 +93,6 @@
// TYPES ------------------------------------------------------------------- // TYPES -------------------------------------------------------------------
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
extern void LayoutMainWindow(HWND hWnd, HWND pane);
// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
void DestroyCustomCursor(); void DestroyCustomCursor();
@ -116,13 +113,11 @@ EXTERN_CVAR (Bool, autoloadbrightmaps)
EXTERN_CVAR (Bool, autoloadwidescreen) EXTERN_CVAR (Bool, autoloadwidescreen)
EXTERN_CVAR (Int, vid_preferbackend) EXTERN_CVAR (Int, vid_preferbackend)
extern HWND Window, ConWindow, GameTitleWindow;
extern HANDLE StdOut; extern HANDLE StdOut;
extern bool FancyStdOut; extern bool FancyStdOut;
extern HINSTANCE g_hInst; extern HINSTANCE g_hInst;
extern FILE *Logfile; extern FILE *Logfile;
extern bool NativeMouse; extern bool NativeMouse;
extern bool ConWindowHidden;
// PUBLIC DATA DEFINITIONS ------------------------------------------------- // PUBLIC DATA DEFINITIONS -------------------------------------------------
@ -281,42 +276,19 @@ void CalculateCPUSpeed()
// //
//========================================================================== //==========================================================================
static void DoPrintStr(const char *cpt, HWND edit, HANDLE StdOut) static void PrintToStdOut(const char *cpt, HANDLE StdOut)
{ {
if (edit == nullptr && StdOut == nullptr && !con_debugoutput) if (StdOut == nullptr && !con_debugoutput)
return; return;
wchar_t wbuf[256]; wchar_t wbuf[256];
int bpos = 0; int bpos = 0;
CHARRANGE selection = {};
CHARRANGE endselection = {};
LONG lines_before = 0, lines_after;
CHARFORMAT format;
if (edit != NULL)
{
// Store the current selection and set it to the end so we can append text.
SendMessage(edit, EM_EXGETSEL, 0, (LPARAM)&selection);
endselection.cpMax = endselection.cpMin = GetWindowTextLength(edit);
SendMessage(edit, EM_EXSETSEL, 0, (LPARAM)&endselection);
// GetWindowTextLength and EM_EXSETSEL can disagree on where the end of
// the text is. Find out what EM_EXSETSEL thought it was and use that later.
SendMessage(edit, EM_EXGETSEL, 0, (LPARAM)&endselection);
// Remember how many lines there were before we added text.
lines_before = (LONG)SendMessage(edit, EM_GETLINECOUNT, 0, 0);
}
const uint8_t *cptr = (const uint8_t*)cpt; const uint8_t *cptr = (const uint8_t*)cpt;
auto outputIt = [&]() auto outputIt = [&]()
{ {
wbuf[bpos] = 0; wbuf[bpos] = 0;
if (edit != nullptr)
{
SendMessageW(edit, EM_REPLACESEL, FALSE, (LPARAM)wbuf);
}
if (con_debugoutput) if (con_debugoutput)
{ {
OutputDebugStringW(wbuf); OutputDebugStringW(wbuf);
@ -385,17 +357,6 @@ static void DoPrintStr(const char *cpt, HWND edit, HANDLE StdOut)
} }
SetConsoleTextAttribute(StdOut, (WORD)attrib); SetConsoleTextAttribute(StdOut, (WORD)attrib);
} }
if (edit != NULL)
{
// GDI uses BGR colors, but color is RGB, so swap the R and the B.
std::swap(color.r, color.b);
// Change the color.
format.cbSize = sizeof(format);
format.dwMask = CFM_COLOR;
format.dwEffects = 0;
format.crTextColor = color;
SendMessage(edit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);
}
} }
} }
} }
@ -404,52 +365,16 @@ static void DoPrintStr(const char *cpt, HWND edit, HANDLE StdOut)
outputIt(); outputIt();
} }
if (edit != NULL)
{
// If the old selection was at the end of the text, keep it at the end and
// scroll. Don't scroll if the selection is anywhere else.
if (selection.cpMin == endselection.cpMin && selection.cpMax == endselection.cpMax)
{
selection.cpMax = selection.cpMin = GetWindowTextLength (edit);
lines_after = (LONG)SendMessage(edit, EM_GETLINECOUNT, 0, 0);
if (lines_after > lines_before)
{
SendMessage(edit, EM_LINESCROLL, 0, lines_after - lines_before);
}
}
// Restore the previous selection.
SendMessage(edit, EM_EXSETSEL, 0, (LPARAM)&selection);
// Give the edit control a chance to redraw itself.
I_GetEvent();
}
if (StdOut != NULL && FancyStdOut) if (StdOut != NULL && FancyStdOut)
{ // Set text back to gray, in case it was changed. { // Set text back to gray, in case it was changed.
SetConsoleTextAttribute(StdOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); SetConsoleTextAttribute(StdOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
} }
} }
static TArray<FString> bufferedConsoleStuff;
void I_PrintStr(const char *cp) void I_PrintStr(const char *cp)
{ {
if (ConWindowHidden) mainwindow.PrintStr(cp);
{ PrintToStdOut(cp, StdOut);
bufferedConsoleStuff.Push(cp);
DoPrintStr(cp, NULL, StdOut);
}
else
{
DoPrintStr(cp, ConWindow, StdOut);
}
}
void I_FlushBufferedConsoleStuff()
{
for (unsigned i = 0; i < bufferedConsoleStuff.Size(); i++)
{
DoPrintStr(bufferedConsoleStuff[i], ConWindow, NULL);
}
bufferedConsoleStuff.Clear();
} }
//========================================================================== //==========================================================================
@ -635,7 +560,7 @@ int I_PickIWad(WadStuff *wads, int numwads, bool showwin, int defaultiwad)
DefaultWad = defaultiwad; DefaultWad = defaultiwad;
return (int)DialogBox(g_hInst, MAKEINTRESOURCE(IDD_IWADDIALOG), return (int)DialogBox(g_hInst, MAKEINTRESOURCE(IDD_IWADDIALOG),
(HWND)Window, (DLGPROC)IWADBoxCallback); (HWND)mainwindow.GetHandle(), (DLGPROC)IWADBoxCallback);
} }
return defaultiwad; return defaultiwad;
} }
@ -682,16 +607,16 @@ bool I_SetCursor(FGameTexture *cursorpic)
DestroyCustomCursor(); DestroyCustomCursor();
cursor = LoadCursor(NULL, IDC_ARROW); cursor = LoadCursor(NULL, IDC_ARROW);
} }
SetClassLongPtr(Window, GCLP_HCURSOR, (LONG_PTR)cursor); SetClassLongPtr(mainwindow.GetHandle(), GCLP_HCURSOR, (LONG_PTR)cursor);
if (NativeMouse) if (NativeMouse)
{ {
POINT pt; POINT pt;
RECT client; RECT client;
// If the mouse pointer is within the window's client rect, set it now. // If the mouse pointer is within the window's client rect, set it now.
if (GetCursorPos(&pt) && GetClientRect(Window, &client) && if (GetCursorPos(&pt) && GetClientRect(mainwindow.GetHandle(), &client) &&
ClientToScreen(Window, (LPPOINT)&client.left) && ClientToScreen(mainwindow.GetHandle(), (LPPOINT)&client.left) &&
ClientToScreen(Window, (LPPOINT)&client.right)) ClientToScreen(mainwindow.GetHandle(), (LPPOINT)&client.right))
{ {
if (pt.x >= client.left && pt.x < client.right && if (pt.x >= client.left && pt.x < client.right &&
pt.y >= client.top && pt.y < client.bottom) pt.y >= client.top && pt.y < client.bottom)
@ -912,7 +837,7 @@ bool I_WriteIniFailed()
); );
errortext.Format ("The config file %s could not be written:\n%s", GameConfig->GetPathName(), lpMsgBuf); errortext.Format ("The config file %s could not be written:\n%s", GameConfig->GetPathName(), lpMsgBuf);
LocalFree (lpMsgBuf); LocalFree (lpMsgBuf);
return MessageBoxA(Window, errortext.GetChars(), GAMENAME " configuration not saved", MB_ICONEXCLAMATION | MB_RETRYCANCEL) == IDRETRY; return MessageBoxA(mainwindow.GetHandle(), errortext.GetChars(), GAMENAME " configuration not saved", MB_ICONEXCLAMATION | MB_RETRYCANCEL) == IDRETRY;
} }

View file

@ -43,9 +43,6 @@ int I_PickIWad (WadStuff *wads, int numwads, bool queryiwad, int defaultiwad);
// The ini could not be saved at exit // The ini could not be saved at exit
bool I_WriteIniFailed (); bool I_WriteIniFailed ();
// [RH] Used by the display code to set the normal window procedure
void I_SetWndProc();
// [RH] Checks the registry for Steam's install path, so we can scan its // [RH] Checks the registry for Steam's install path, so we can scan its
// directories for IWADs if the user purchased any through Steam. // directories for IWADs if the user purchased any through Steam.
TArray<FString> I_GetSteamPath(); TArray<FString> I_GetSteamPath();

View file

@ -53,6 +53,7 @@
#include "startupinfo.h" #include "startupinfo.h"
#include "i_interface.h" #include "i_interface.h"
#include "texturemanager.h" #include "texturemanager.h"
#include "i_mainwindow.h"
// MACROS ------------------------------------------------------------------ // MACROS ------------------------------------------------------------------
@ -67,24 +68,13 @@
// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
void RestoreConView();
void LayoutMainWindow (HWND hWnd, HWND pane);
int LayoutNetStartPane (HWND pane, int w);
bool ST_Util_CreateStartupWindow ();
void ST_Util_SizeWindowForBitmap (int scale); void ST_Util_SizeWindowForBitmap (int scale);
void ST_Util_InvalidateRect (HWND hwnd, BitmapInfo *bitmap_info, int left, int top, int right, int bottom);
// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
static INT_PTR CALLBACK NetStartPaneProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
// EXTERNAL DATA DECLARATIONS ---------------------------------------------- // EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern HINSTANCE g_hInst; extern HINSTANCE g_hInst;
extern HWND Window, ConWindow, ProgressBar, NetStartPane, StartupScreen, GameTitleWindow;
// PUBLIC DATA DEFINITIONS ------------------------------------------------- // PUBLIC DATA DEFINITIONS -------------------------------------------------
@ -112,7 +102,7 @@ CUSTOM_CVAR(Int, showendoom, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
FStartupScreen *FStartupScreen::CreateInstance(int max_progress) FStartupScreen *FStartupScreen::CreateInstance(int max_progress)
{ {
FStartupScreen *scr = NULL; FStartupScreen *scr = NULL;
HRESULT hr = -1; HRESULT hr = E_FAIL;
if (!Args->CheckParm("-nostartup")) if (!Args->CheckParm("-nostartup"))
{ {
@ -154,12 +144,7 @@ FBasicStartupScreen::FBasicStartupScreen(int max_progress, bool show_bar)
{ {
if (show_bar) if (show_bar)
{ {
ProgressBar = CreateWindowEx(0, PROGRESS_CLASS, mainwindow.ShowProgressBar(MaxPos);
NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
0, 0, 0, 0,
Window, 0, g_hInst, NULL);
SendMessage (ProgressBar, PBM_SETRANGE, 0, MAKELPARAM(0,MaxPos));
LayoutMainWindow (Window, NULL);
} }
NetMaxPos = 0; NetMaxPos = 0;
NetCurPos = 0; NetCurPos = 0;
@ -176,13 +161,8 @@ FBasicStartupScreen::FBasicStartupScreen(int max_progress, bool show_bar)
FBasicStartupScreen::~FBasicStartupScreen() FBasicStartupScreen::~FBasicStartupScreen()
{ {
if (ProgressBar != NULL) mainwindow.HideProgressBar();
{ KillTimer(mainwindow.GetHandle(), 1337);
DestroyWindow (ProgressBar);
ProgressBar = NULL;
LayoutMainWindow (Window, NULL);
}
KillTimer(Window, 1337);
} }
//========================================================================== //==========================================================================
@ -198,7 +178,7 @@ void FBasicStartupScreen::Progress()
if (CurPos < MaxPos) if (CurPos < MaxPos)
{ {
CurPos++; CurPos++;
SendMessage (ProgressBar, PBM_SETPOS, CurPos, 0); mainwindow.SetProgressPos(CurPos);
} }
} }
@ -217,61 +197,8 @@ void FBasicStartupScreen::Progress()
void FBasicStartupScreen::NetInit(const char *message, int numplayers) void FBasicStartupScreen::NetInit(const char *message, int numplayers)
{ {
NetMaxPos = numplayers; NetMaxPos = numplayers;
if (NetStartPane == NULL) mainwindow.ShowNetStartPane(message, numplayers);
{
NetStartPane = CreateDialogParam (g_hInst, MAKEINTRESOURCE(IDD_NETSTARTPANE), Window, NetStartPaneProc, 0);
// We don't need two progress bars.
if (ProgressBar != NULL)
{
DestroyWindow (ProgressBar);
ProgressBar = NULL;
}
RECT winrect;
GetWindowRect (Window, &winrect);
SetWindowPos (Window, NULL, 0, 0,
winrect.right - winrect.left, winrect.bottom - winrect.top + LayoutNetStartPane (NetStartPane, 0),
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
LayoutMainWindow (Window, NULL);
SetFocus (NetStartPane);
}
if (NetStartPane != NULL)
{
HWND ctl;
std::wstring wmessage = WideString(message);
SetDlgItemTextW (NetStartPane, IDC_NETSTARTMESSAGE, wmessage.c_str());
ctl = GetDlgItem (NetStartPane, IDC_NETSTARTPROGRESS);
if (numplayers == 0)
{
// PBM_SETMARQUEE is only available under XP and above, so this might fail.
NetMarqueeMode = SendMessage (ctl, PBM_SETMARQUEE, TRUE, 100);
if (NetMarqueeMode == FALSE)
{
SendMessage (ctl, PBM_SETRANGE, 0, MAKELPARAM(0,16));
}
else
{
// If we don't set the PBS_MARQUEE style, then the marquee will never show up.
SetWindowLong (ctl, GWL_STYLE, GetWindowLong (ctl, GWL_STYLE) | PBS_MARQUEE);
}
SetDlgItemTextW (NetStartPane, IDC_NETSTARTCOUNT, L"");
}
else
{
NetMarqueeMode = FALSE;
SendMessage (ctl, PBM_SETMARQUEE, FALSE, 0);
// Make sure the marquee really is turned off.
SetWindowLong (ctl, GWL_STYLE, GetWindowLong (ctl, GWL_STYLE) & (~PBS_MARQUEE));
SendMessage (ctl, PBM_SETRANGE, 0, MAKELPARAM(0,numplayers));
if (numplayers == 1)
{
SendMessage (ctl, PBM_SETPOS, 1, 0);
SetDlgItemTextW (NetStartPane, IDC_NETSTARTCOUNT, L"");
}
}
}
NetMaxPos = numplayers; NetMaxPos = numplayers;
NetCurPos = 0; NetCurPos = 0;
NetProgress(1); // You always know about yourself NetProgress(1); // You always know about yourself
@ -287,12 +214,7 @@ void FBasicStartupScreen::NetInit(const char *message, int numplayers)
void FBasicStartupScreen::NetDone() void FBasicStartupScreen::NetDone()
{ {
if (NetStartPane != NULL) mainwindow.HideNetStartPane();
{
DestroyWindow (NetStartPane);
NetStartPane = NULL;
LayoutMainWindow (Window, NULL);
}
} }
//========================================================================== //==========================================================================
@ -325,7 +247,7 @@ void FBasicStartupScreen::NetMessage(const char *format, ...)
// //
//========================================================================== //==========================================================================
void FBasicStartupScreen :: NetProgress(int count) void FBasicStartupScreen::NetProgress(int count)
{ {
if (count == 0) if (count == 0)
{ {
@ -335,23 +257,8 @@ void FBasicStartupScreen :: NetProgress(int count)
{ {
NetCurPos = count; NetCurPos = count;
} }
if (NetStartPane == NULL)
{
return;
}
if (NetMaxPos == 0 && !NetMarqueeMode)
{
// PBM_SETMARQUEE didn't work, so just increment the progress bar endlessly.
SendDlgItemMessage (NetStartPane, IDC_NETSTARTPROGRESS, PBM_SETPOS, NetCurPos & 15, 0);
}
else if (NetMaxPos > 1)
{
char buf[16];
mysnprintf (buf, countof(buf), "%d/%d", NetCurPos, NetMaxPos); mainwindow.SetNetStartProgress(count);
SetDlgItemTextA (NetStartPane, IDC_NETSTARTCOUNT, buf);
SendDlgItemMessage (NetStartPane, IDC_NETSTARTPROGRESS, PBM_SETPOS, min(NetCurPos, NetMaxPos), 0);
}
} }
//========================================================================== //==========================================================================
@ -370,59 +277,7 @@ void FBasicStartupScreen :: NetProgress(int count)
bool FBasicStartupScreen::NetLoop(bool (*timer_callback)(void *), void *userdata) bool FBasicStartupScreen::NetLoop(bool (*timer_callback)(void *), void *userdata)
{ {
BOOL bRet; return mainwindow.RunMessageLoop(timer_callback, userdata);
MSG msg;
if (SetTimer (Window, 1337, 500, NULL) == 0)
{
I_FatalError ("Could not set network synchronization timer.");
}
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (bRet == -1)
{
KillTimer (Window, 1337);
return false;
}
else
{
if (msg.message == WM_TIMER && msg.hwnd == Window && msg.wParam == 1337)
{
if (timer_callback (userdata))
{
KillTimer (NetStartPane, 1);
return true;
}
}
if (!IsDialogMessage (NetStartPane, &msg))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
}
}
KillTimer (Window, 1337);
return false;
}
//==========================================================================
//
// NetStartPaneProc
//
// DialogProc for the network startup pane. It just waits for somebody to
// click a button, and the only button available is the abort one.
//
//==========================================================================
static INT_PTR CALLBACK NetStartPaneProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL)
{
PostQuitMessage (0);
return TRUE;
}
return FALSE;
} }
//========================================================================== //==========================================================================
@ -437,6 +292,7 @@ static INT_PTR CALLBACK NetStartPaneProc (HWND hDlg, UINT msg, WPARAM wParam, LP
FGraphicalStartupScreen::FGraphicalStartupScreen(int max_progress) FGraphicalStartupScreen::FGraphicalStartupScreen(int max_progress)
: FBasicStartupScreen(max_progress, false) : FBasicStartupScreen(max_progress, false)
{ {
mainwindow.ShowStartupScreen();
} }
//========================================================================== //==========================================================================
@ -447,11 +303,7 @@ FGraphicalStartupScreen::FGraphicalStartupScreen(int max_progress)
FGraphicalStartupScreen::~FGraphicalStartupScreen() FGraphicalStartupScreen::~FGraphicalStartupScreen()
{ {
if (StartupScreen != NULL) mainwindow.HideStartupScreen();
{
DestroyWindow (StartupScreen);
StartupScreen = NULL;
}
if (StartupBitmap != NULL) if (StartupBitmap != NULL)
{ {
ST_Util_FreeBitmap (StartupBitmap); ST_Util_FreeBitmap (StartupBitmap);
@ -468,8 +320,8 @@ FGraphicalStartupScreen::~FGraphicalStartupScreen()
void FHexenStartupScreen::SetWindowSize() void FHexenStartupScreen::SetWindowSize()
{ {
ST_Util_SizeWindowForBitmap(1); ST_Util_SizeWindowForBitmap(1);
LayoutMainWindow(Window, NULL); mainwindow.UpdateLayout();
InvalidateRect(StartupScreen, NULL, TRUE); mainwindow.InvalidateStartupScreen();
} }
//========================================================================== //==========================================================================
@ -481,8 +333,8 @@ void FHexenStartupScreen::SetWindowSize()
void FHereticStartupScreen::SetWindowSize() void FHereticStartupScreen::SetWindowSize()
{ {
ST_Util_SizeWindowForBitmap(1); ST_Util_SizeWindowForBitmap(1);
LayoutMainWindow(Window, NULL); mainwindow.UpdateLayout();
InvalidateRect(StartupScreen, NULL, TRUE); mainwindow.InvalidateStartupScreen();
} }
//========================================================================== //==========================================================================
@ -494,8 +346,8 @@ void FHereticStartupScreen::SetWindowSize()
void FStrifeStartupScreen::SetWindowSize() void FStrifeStartupScreen::SetWindowSize()
{ {
ST_Util_SizeWindowForBitmap(2); ST_Util_SizeWindowForBitmap(2);
LayoutMainWindow(Window, NULL); mainwindow.UpdateLayout();
InvalidateRect(StartupScreen, NULL, TRUE); mainwindow.InvalidateStartupScreen();
} }
//========================================================================== //==========================================================================
@ -532,13 +384,10 @@ int RunEndoom()
return 0; return 0;
} }
if (!ST_Util_CreateStartupWindow()) mainwindow.ShowStartupScreen();
{
return 0;
}
I_ShutdownGraphics (); I_ShutdownGraphics ();
RestoreConView (); mainwindow.RestoreConView ();
S_StopMusic(true); S_StopMusic(true);
fileSystem.ReadFile (endoom_lump, endoom_screen); fileSystem.ReadFile (endoom_lump, endoom_screen);
@ -548,15 +397,11 @@ int RunEndoom()
ST_Util_DrawTextScreen (StartupBitmap, endoom_screen); ST_Util_DrawTextScreen (StartupBitmap, endoom_screen);
// Make the title banner go away. // Make the title banner go away.
if (GameTitleWindow != NULL) mainwindow.HideGameTitleWindow();
{
DestroyWindow (GameTitleWindow);
GameTitleWindow = NULL;
}
ST_Util_SizeWindowForBitmap (1); ST_Util_SizeWindowForBitmap (1);
LayoutMainWindow (Window, NULL); mainwindow.UpdateLayout();
InvalidateRect (StartupScreen, NULL, TRUE); mainwindow.InvalidateStartupScreen();
// Does this screen need blinking? // Does this screen need blinking?
for (i = 0; i < 80*25; ++i) for (i = 0; i < 80*25; ++i)
@ -567,7 +412,7 @@ int RunEndoom()
break; break;
} }
} }
if (blinking && SetTimer (Window, 0x5A15A, BLINK_PERIOD, NULL) == 0) if (blinking && SetTimer (mainwindow.GetHandle(), 0x5A15A, BLINK_PERIOD, NULL) == 0)
{ {
blinking = false; blinking = false;
} }
@ -581,12 +426,12 @@ int RunEndoom()
{ {
if (blinking) if (blinking)
{ {
KillTimer (Window, 0x5A15A); KillTimer (mainwindow.GetHandle(), 0x5A15A);
} }
ST_Util_FreeBitmap (StartupBitmap); ST_Util_FreeBitmap (StartupBitmap);
return int(bRet == 0 ? mess.wParam : 0); return int(bRet == 0 ? mess.wParam : 0);
} }
else if (blinking && mess.message == WM_TIMER && mess.hwnd == Window && mess.wParam == 0x5A15A) else if (blinking && mess.message == WM_TIMER && mess.hwnd == mainwindow.GetHandle() && mess.wParam == 0x5A15A)
{ {
ST_Util_UpdateTextBlink (StartupBitmap, endoom_screen, blinkstate); ST_Util_UpdateTextBlink (StartupBitmap, endoom_screen, blinkstate);
blinkstate = !blinkstate; blinkstate = !blinkstate;
@ -604,27 +449,6 @@ void ST_Endoom()
} }
//==========================================================================
//
// ST_Util_CreateStartupWindow
//
// Creates the static control that will draw the startup screen.
//
//==========================================================================
bool ST_Util_CreateStartupWindow ()
{
StartupScreen = CreateWindowEx (WS_EX_NOPARENTNOTIFY, L"STATIC", NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SS_OWNERDRAW,
0, 0, 0, 0, Window, NULL, g_hInst, NULL);
if (StartupScreen == NULL)
{
return false;
}
SetWindowLong (StartupScreen, GWL_ID, IDC_STATIC_STARTUP);
return true;
}
//========================================================================== //==========================================================================
// //
// ST_Util_SizeWindowForBitmap // ST_Util_SizeWindowForBitmap
@ -640,16 +464,7 @@ void ST_Util_SizeWindowForBitmap (int scale)
int w, h, cx, cy, x, y; int w, h, cx, cy, x, y;
RECT rect; RECT rect;
if (GameTitleWindow != NULL) RECT sizerect = { 0, 0, StartupBitmap->bmiHeader.biWidth * scale, StartupBitmap->bmiHeader.biHeight * scale + mainwindow.GetGameTitleWindowHeight() };
{
GetClientRect (GameTitleWindow, &rect);
}
else
{
rect.bottom = 0;
}
RECT sizerect = { 0, 0, StartupBitmap->bmiHeader.biWidth * scale,
StartupBitmap->bmiHeader.biHeight * scale + rect.bottom };
AdjustWindowRectEx(&sizerect, WS_VISIBLE|WS_OVERLAPPEDWINDOW, FALSE, WS_EX_APPWINDOW); AdjustWindowRectEx(&sizerect, WS_VISIBLE|WS_OVERLAPPEDWINDOW, FALSE, WS_EX_APPWINDOW);
w = sizerect.right - sizerect.left; w = sizerect.right - sizerect.left;
h = sizerect.bottom - sizerect.top; h = sizerect.bottom - sizerect.top;
@ -659,7 +474,7 @@ void ST_Util_SizeWindowForBitmap (int scale)
memset (&displaysettings, 0, sizeof(displaysettings)); memset (&displaysettings, 0, sizeof(displaysettings));
displaysettings.dmSize = sizeof(displaysettings); displaysettings.dmSize = sizeof(displaysettings);
EnumDisplaySettings (NULL, ENUM_CURRENT_SETTINGS, &displaysettings); EnumDisplaySettings (NULL, ENUM_CURRENT_SETTINGS, &displaysettings);
GetWindowRect (Window, &rect); GetWindowRect (mainwindow.GetHandle(), &rect);
cx = (rect.left + rect.right) / 2; cx = (rect.left + rect.right) / 2;
cy = (rect.top + rect.bottom) / 2; cy = (rect.top + rect.bottom) / 2;
x = cx - w / 2; x = cx - w / 2;
@ -680,7 +495,7 @@ void ST_Util_SizeWindowForBitmap (int scale)
{ {
y = 0; y = 0;
} }
MoveWindow (Window, x, y, w, h, TRUE); MoveWindow (mainwindow.GetHandle(), x, y, w, h, TRUE);
} }
//========================================================================== //==========================================================================
@ -692,19 +507,7 @@ void ST_Util_SizeWindowForBitmap (int scale)
// //
//========================================================================== //==========================================================================
void ST_Util_InvalidateRect (HWND hwnd, BitmapInfo *bitmap_info, int left, int top, int right, int bottom)
{
RECT rect;
GetClientRect (hwnd, &rect);
rect.left = left * rect.right / bitmap_info->bmiHeader.biWidth - 1;
rect.top = top * rect.bottom / bitmap_info->bmiHeader.biHeight - 1;
rect.right = right * rect.right / bitmap_info->bmiHeader.biWidth + 1;
rect.bottom = bottom * rect.bottom / bitmap_info->bmiHeader.biHeight + 1;
InvalidateRect (hwnd, &rect, FALSE);
}
void ST_Util_InvalidateRect(BitmapInfo* bitmap_info, int left, int top, int right, int bottom) void ST_Util_InvalidateRect(BitmapInfo* bitmap_info, int left, int top, int right, int bottom)
{ {
ST_Util_InvalidateRect(StartupScreen , bitmap_info, left, top, right, bottom); mainwindow.InvalidateStartupScreen(left, top, right, bottom);
} }

View file

@ -43,14 +43,12 @@
#include "startupinfo.h" #include "startupinfo.h"
#include "palutil.h" #include "palutil.h"
#include "i_interface.h" #include "i_interface.h"
#include "i_mainwindow.h"
uint8_t* GetHexChar(int codepoint); uint8_t* GetHexChar(int codepoint);
void I_GetEvent(); // i_input.h pulls in too much garbage. void I_GetEvent(); // i_input.h pulls in too much garbage.
void ST_Util_InvalidateRect(BitmapInfo* bitmap_info, int left, int top, int right, int bottom);
bool ST_Util_CreateStartupWindow();
static const uint16_t IBM437ToUnicode[] = { static const uint16_t IBM437ToUnicode[] = {
0x0000, //#NULL 0x0000, //#NULL
0x0001, //#START OF HEADING 0x0001, //#START OF HEADING
@ -420,7 +418,7 @@ FHexenStartupScreen::FHexenStartupScreen(int max_progress, long& hr)
int notch_lump = fileSystem.CheckNumForName("NOTCH"); int notch_lump = fileSystem.CheckNumForName("NOTCH");
hr = -1; hr = -1;
if (startup_lump < 0 || fileSystem.FileLength(startup_lump) != 153648 || !ST_Util_CreateStartupWindow() || if (startup_lump < 0 || fileSystem.FileLength(startup_lump) != 153648 ||
netnotch_lump < 0 || fileSystem.FileLength(netnotch_lump) != ST_NETNOTCH_WIDTH / 2 * ST_NETNOTCH_HEIGHT || netnotch_lump < 0 || fileSystem.FileLength(netnotch_lump) != ST_NETNOTCH_WIDTH / 2 * ST_NETNOTCH_HEIGHT ||
notch_lump < 0 || fileSystem.FileLength(notch_lump) != ST_NOTCH_WIDTH / 2 * ST_NOTCH_HEIGHT) notch_lump < 0 || fileSystem.FileLength(notch_lump) != ST_NOTCH_WIDTH / 2 * ST_NOTCH_HEIGHT)
{ {
@ -585,7 +583,7 @@ FHereticStartupScreen::FHereticStartupScreen(int max_progress, long& hr)
uint8_t loading_screen[4000]; uint8_t loading_screen[4000];
hr = -1; hr = -1;
if (loading_lump < 0 || fileSystem.FileLength(loading_lump) != 4000 || !ST_Util_CreateStartupWindow()) if (loading_lump < 0 || fileSystem.FileLength(loading_lump) != 4000)
{ {
return; return;
} }
@ -657,7 +655,7 @@ void FHereticStartupScreen::LoadingStatus(const char* message, int colors)
{ {
ST_Util_DrawChar(StartupBitmap, 17 + x, HMsgY, message[x], colors); ST_Util_DrawChar(StartupBitmap, 17 + x, HMsgY, message[x], colors);
} }
ST_Util_InvalidateRect(StartupBitmap, 17 * 8, HMsgY * 16, (17 + x) * 8, HMsgY * 16 + 16); mainwindow.InvalidateStartupScreen(17 * 8, HMsgY * 16, (17 + x) * 8, HMsgY * 16 + 16);
HMsgY++; HMsgY++;
I_GetEvent(); I_GetEvent();
} }
@ -678,7 +676,7 @@ void FHereticStartupScreen::AppendStatusLine(const char* status)
{ {
ST_Util_DrawChar(StartupBitmap, SMsgX + x, 24, status[x], 0x1f); ST_Util_DrawChar(StartupBitmap, SMsgX + x, 24, status[x], 0x1f);
} }
ST_Util_InvalidateRect(StartupBitmap, SMsgX * 8, 24 * 16, (SMsgX + x) * 8, 25 * 16); mainwindow.InvalidateStartupScreen(SMsgX * 8, 24 * 16, (SMsgX + x) * 8, 25 * 16);
SMsgX += x; SMsgX += x;
I_GetEvent(); I_GetEvent();
} }
@ -711,7 +709,7 @@ FStrifeStartupScreen::FStrifeStartupScreen(int max_progress, long& hr)
StartupPics[i] = NULL; StartupPics[i] = NULL;
} }
if (startup_lump < 0 || fileSystem.FileLength(startup_lump) != 64000 || !ST_Util_CreateStartupWindow()) if (startup_lump < 0 || fileSystem.FileLength(startup_lump) != 64000)
{ {
return; return;
} }
@ -887,7 +885,7 @@ void ST_Util_DrawBlock(BitmapInfo* bitmap_info, const uint8_t* src, int x, int y
int destpitch = bitmap_info->bmiHeader.biWidth; int destpitch = bitmap_info->bmiHeader.biWidth;
uint8_t* dest = ST_Util_BitsForBitmap(bitmap_info) + x + y * destpitch; uint8_t* dest = ST_Util_BitsForBitmap(bitmap_info) + x + y * destpitch;
ST_Util_InvalidateRect(bitmap_info, x, y, x + bytewidth, y + height); mainwindow.InvalidateStartupScreen(x, y, x + bytewidth, y + height);
if (bytewidth == 8) if (bytewidth == 8)
{ // progress notches { // progress notches
@ -935,7 +933,7 @@ void ST_Util_DrawBlock4(BitmapInfo* bitmap_info, const uint8_t* src, int x, int
int destpitch = bitmap_info->bmiHeader.biWidth; int destpitch = bitmap_info->bmiHeader.biWidth;
uint8_t* dest = ST_Util_BitsForBitmap(bitmap_info) + x + y * destpitch; uint8_t* dest = ST_Util_BitsForBitmap(bitmap_info) + x + y * destpitch;
ST_Util_InvalidateRect(bitmap_info, x, y, x + bytewidth * 2, y + height); mainwindow.InvalidateStartupScreen(x, y, x + bytewidth * 2, y + height);
for (; height > 0; --height) for (; height > 0; --height)
{ {
@ -961,7 +959,7 @@ void ST_Util_ClearBlock(BitmapInfo* bitmap_info, uint8_t fill, int x, int y, int
int destpitch = bitmap_info->bmiHeader.biWidth; int destpitch = bitmap_info->bmiHeader.biWidth;
uint8_t* dest = ST_Util_BitsForBitmap(bitmap_info) + x + y * destpitch; uint8_t* dest = ST_Util_BitsForBitmap(bitmap_info) + x + y * destpitch;
ST_Util_InvalidateRect(bitmap_info, x, y, x + bytewidth, y + height); mainwindow.InvalidateStartupScreen(x, y, x + bytewidth, y + height);
while (height > 0) while (height > 0)
{ {
@ -1165,7 +1163,7 @@ void ST_Util_UpdateTextBlink(BitmapInfo* bitmap_info, const uint8_t* text_screen
if (text_screen[1] & 0x80) if (text_screen[1] & 0x80)
{ {
ST_Util_DrawChar(bitmap_info, x, y, on ? IBM437ToUnicode[text_screen[0]] : ' ', text_screen[1]); ST_Util_DrawChar(bitmap_info, x, y, on ? IBM437ToUnicode[text_screen[0]] : ' ', text_screen[1]);
ST_Util_InvalidateRect(bitmap_info, x * 8, y * 16, x * 8 + 8, y * 16 + 16); mainwindow.InvalidateStartupScreen(x * 8, y * 16, x * 8 + 8, y * 16 + 16);
} }
text_screen += 2; text_screen += 2;
} }

View file

@ -47,6 +47,7 @@
#include "printf.h" #include "printf.h"
#include "win32basevideo.h" #include "win32basevideo.h"
#include "cmdlib.h" #include "cmdlib.h"
#include "i_mainwindow.h"
CVAR(Int, vid_adapter, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR(Int, vid_adapter, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
@ -58,7 +59,7 @@ CVAR(Int, vid_adapter, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
Win32BaseVideo::Win32BaseVideo() Win32BaseVideo::Win32BaseVideo()
{ {
I_SetWndProc(); mainwindow.ShowGameView();
GetDisplayDeviceName(); GetDisplayDeviceName();
} }

View file

@ -3,6 +3,7 @@
#include "hardware.h" #include "hardware.h"
#include "engineerrors.h" #include "engineerrors.h"
#include <Windows.h> #include <Windows.h>
#include "i_mainwindow.h"
#ifdef HAVE_SOFTPOLY #ifdef HAVE_SOFTPOLY
@ -10,8 +11,6 @@ EXTERN_CVAR(Bool, vid_vsync)
bool ViewportLinearScale(); bool ViewportLinearScale();
extern HWND Window;
#include <d3d9.h> #include <d3d9.h>
#pragma comment(lib, "d3d9.lib") #pragma comment(lib, "d3d9.lib")
@ -41,7 +40,7 @@ void I_PolyPresentInit()
} }
RECT rect = {}; RECT rect = {};
GetClientRect(Window, &rect); GetClientRect(mainwindow.GetHandle(), &rect);
ClientWidth = rect.right; ClientWidth = rect.right;
ClientHeight = rect.bottom; ClientHeight = rect.bottom;
@ -52,10 +51,10 @@ void I_PolyPresentInit()
pp.BackBufferWidth = ClientWidth; pp.BackBufferWidth = ClientWidth;
pp.BackBufferHeight = ClientHeight; pp.BackBufferHeight = ClientHeight;
pp.BackBufferCount = 1; pp.BackBufferCount = 1;
pp.hDeviceWindow = Window; pp.hDeviceWindow = mainwindow.GetHandle();
pp.PresentationInterval = CurrentVSync ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE; pp.PresentationInterval = CurrentVSync ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE;
HRESULT result = d3d9->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, Window, D3DCREATE_HARDWARE_VERTEXPROCESSING, &pp, nullptr, &device); HRESULT result = d3d9->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, mainwindow.GetHandle(), D3DCREATE_HARDWARE_VERTEXPROCESSING, &pp, nullptr, &device);
if (FAILED(result)) if (FAILED(result))
{ {
I_FatalError("IDirect3D9.CreateDevice failed"); I_FatalError("IDirect3D9.CreateDevice failed");
@ -67,7 +66,7 @@ uint8_t *I_PolyPresentLock(int w, int h, bool vsync, int &pitch)
HRESULT result; HRESULT result;
RECT rect = {}; RECT rect = {};
GetClientRect(Window, &rect); GetClientRect(mainwindow.GetHandle(), &rect);
if (rect.right != ClientWidth || rect.bottom != ClientHeight || CurrentVSync != vsync) if (rect.right != ClientWidth || rect.bottom != ClientHeight || CurrentVSync != vsync)
{ {
if (surface) if (surface)
@ -86,7 +85,7 @@ uint8_t *I_PolyPresentLock(int w, int h, bool vsync, int &pitch)
pp.BackBufferWidth = ClientWidth; pp.BackBufferWidth = ClientWidth;
pp.BackBufferHeight = ClientHeight; pp.BackBufferHeight = ClientHeight;
pp.BackBufferCount = 1; pp.BackBufferCount = 1;
pp.hDeviceWindow = Window; pp.hDeviceWindow = mainwindow.GetHandle();
pp.PresentationInterval = CurrentVSync ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE; pp.PresentationInterval = CurrentVSync ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE;
device->Reset(&pp); device->Reset(&pp);
} }

View file

@ -7,16 +7,14 @@
#endif #endif
#include "volk/volk.h" #include "volk/volk.h"
#include "i_mainwindow.h"
extern HWND Window;
void I_GetVulkanDrawableSize(int *width, int *height) void I_GetVulkanDrawableSize(int *width, int *height)
{ {
assert(Window); assert(Window);
RECT clientRect = { 0 }; RECT clientRect = { 0 };
GetClientRect(Window, &clientRect); GetClientRect(mainwindow.GetHandle(), &clientRect);
if (width != nullptr) if (width != nullptr)
{ {
@ -67,7 +65,7 @@ bool I_CreateVulkanSurface(VkInstance instance, VkSurfaceKHR *surface)
windowCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; windowCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
windowCreateInfo.pNext = nullptr; windowCreateInfo.pNext = nullptr;
windowCreateInfo.flags = 0; windowCreateInfo.flags = 0;
windowCreateInfo.hwnd = Window; windowCreateInfo.hwnd = mainwindow.GetHandle();
windowCreateInfo.hinstance = GetModuleHandle(nullptr); windowCreateInfo.hinstance = GetModuleHandle(nullptr);
const VkResult result = vkCreateWin32SurfaceKHR(instance, &windowCreateInfo, nullptr, surface); const VkResult result = vkCreateWin32SurfaceKHR(instance, &windowCreateInfo, nullptr, surface);

View file

@ -32,10 +32,6 @@
#include "polyrenderer/drawers/poly_triangle.h" #include "polyrenderer/drawers/poly_triangle.h"
#include <chrono> #include <chrono>
#ifdef WIN32
void PeekThreadedErrorPane();
#endif
CVAR(Int, r_multithreaded, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); CVAR(Int, r_multithreaded, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
CVAR(Int, r_debug_draw, 0, 0); CVAR(Int, r_debug_draw, 0, 0);
@ -101,12 +97,7 @@ void DrawerThreads::WaitForWorkers()
std::unique_lock<std::mutex> end_lock(queue->end_mutex); std::unique_lock<std::mutex> end_lock(queue->end_mutex);
if (!queue->end_condition.wait_for(end_lock, 5s, [&]() { return queue->tasks_left == 0; })) if (!queue->end_condition.wait_for(end_lock, 5s, [&]() { return queue->tasks_left == 0; }))
{ {
#ifdef WIN32 I_FatalError("Drawer threads did not finish within 5 seconds!");
PeekThreadedErrorPane();
#endif
// Invoke the crash reporter so that we can capture the call stack of whatever the hung worker thread is doing
int *threadCrashed = nullptr;
*threadCrashed = 0xdeadbeef;
} }
end_lock.unlock(); end_lock.unlock();

View file

@ -11,6 +11,8 @@ void D_ConfirmSendStats()
#else // !NO_SEND_STATS #else // !NO_SEND_STATS
#include "i_mainwindow.h"
#if defined(_WIN32) #if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
@ -340,8 +342,7 @@ void D_ConfirmSendStats()
UCVarValue enabled = { 0 }; UCVarValue enabled = { 0 };
#ifdef _WIN32 #ifdef _WIN32
extern HWND Window; enabled.Int = MessageBoxA(mainwindow.GetHandle(), MESSAGE_TEXT, TITLE_TEXT, MB_ICONQUESTION | MB_YESNO) == IDYES;
enabled.Int = MessageBoxA(Window, MESSAGE_TEXT, TITLE_TEXT, MB_ICONQUESTION | MB_YESNO) == IDYES;
#elif defined __APPLE__ #elif defined __APPLE__
const CFStringRef messageString = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, MESSAGE_TEXT, kCFStringEncodingASCII, kCFAllocatorNull); const CFStringRef messageString = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, MESSAGE_TEXT, kCFStringEncodingASCII, kCFAllocatorNull);
const CFStringRef titleString = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, TITLE_TEXT, kCFStringEncodingASCII, kCFAllocatorNull); const CFStringRef titleString = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, TITLE_TEXT, kCFStringEncodingASCII, kCFAllocatorNull);

View file

@ -59,10 +59,6 @@
#include "swrenderer/things/r_playersprite.h" #include "swrenderer/things/r_playersprite.h"
#include <chrono> #include <chrono>
#ifdef WIN32
void PeekThreadedErrorPane();
#endif
EXTERN_CVAR(Int, r_clearbuffer) EXTERN_CVAR(Int, r_clearbuffer)
EXTERN_CVAR(Int, r_debug_draw) EXTERN_CVAR(Int, r_debug_draw)
@ -239,12 +235,7 @@ namespace swrenderer
finished_threads++; finished_threads++;
if (!end_condition.wait_for(end_lock, 5s, [&]() { return finished_threads == Threads.size(); })) if (!end_condition.wait_for(end_lock, 5s, [&]() { return finished_threads == Threads.size(); }))
{ {
#ifdef WIN32 I_FatalError("Render threads did not finish within 5 seconds!");
PeekThreadedErrorPane();
#endif
// Invoke the crash reporter so that we can capture the call stack of whatever the hung worker thread is doing
int *threadCrashed = nullptr;
*threadCrashed = 0xdeadbeef;
} }
finished_threads = 0; finished_threads = 0;
} }