diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index baddb70676..595b32fdfd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -446,6 +446,7 @@ set( PLAT_WIN32_SOURCES common/platform/win32/i_rawps2.cpp common/platform/win32/i_xinput.cpp common/platform/win32/i_main.cpp + common/platform/win32/i_mainwindow.cpp common/platform/win32/i_system.cpp common/platform/win32/i_specialpaths.cpp common/platform/win32/st_start.cpp diff --git a/src/common/engine/st_start.h b/src/common/engine/st_start.h index 487aa4555f..35d2a1dfcf 100644 --- a/src/common/engine/st_start.h +++ b/src/common/engine/st_start.h @@ -77,7 +77,6 @@ public: void NetDone(); bool NetLoop(bool (*timer_callback)(void*), void* userdata); protected: - long long NetMarqueeMode; int NetMaxPos, NetCurPos; }; diff --git a/src/common/platform/win32/base_sysfb.cpp b/src/common/platform/win32/base_sysfb.cpp index 8130b8b6c4..4d0219316f 100644 --- a/src/common/platform/win32/base_sysfb.cpp +++ b/src/common/platform/win32/base_sysfb.cpp @@ -50,9 +50,7 @@ #include "base_sysfb.h" #include "win32basevideo.h" #include "c_dispatch.h" - - -extern HWND Window; +#include "i_mainwindow.h" extern "C" { __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; @@ -85,7 +83,7 @@ EXTERN_CVAR(Int, vid_defheight) EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &displaysettings); scrwidth = (int)displaysettings.dmPelsWidth; scrheight = (int)displaysettings.dmPelsHeight; - GetWindowRect(Window, &rect); + GetWindowRect(mainwindow.GetHandle(), &rect); cx = scrwidth / 2; cy = scrheight / 2; 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. 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; - if (GetWindowRect(Window, &wrect)) + if (GetWindowRect(mainwindow.GetHandle(), &wrect)) { // If (win_x,win_y) specify to center the window, don't change them // if the window is still centered. @@ -171,7 +169,7 @@ void SystemBaseFrameBuffer::SaveWindowedPos() 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); } - 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")) - 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 exStyle = WS_EX_WINDOWEDGE; - SetWindowLong(Window, GWL_STYLE, style); - SetWindowLong(Window, GWL_EXSTYLE, exStyle); + SetWindowLong(mainwindow.GetHandle(), GWL_STYLE, style); + SetWindowLong(mainwindow.GetHandle(), GWL_EXSTYLE, exStyle); int winx, winy, winw, winh, scrwidth, scrheight; @@ -250,8 +248,8 @@ void SystemBaseFrameBuffer::SetWindowSize(int w, int h) if (!vid_fullscreen) { - ShowWindow(Window, SW_SHOWNORMAL); - SetWindowPos(Window, nullptr, winx, winy, winw, winh, SWP_NOZORDER | SWP_FRAMECHANGED); + ShowWindow(mainwindow.GetHandle(), SW_SHOWNORMAL); + SetWindowPos(mainwindow.GetHandle(), nullptr, winx, winy, winw, winh, SWP_NOZORDER | SWP_FRAMECHANGED); win_maximized = false; SetSize(GetClientWidth(), GetClientHeight()); 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; exStyle = 0; @@ -316,13 +314,13 @@ void SystemBaseFrameBuffer::PositionWindow(bool fullscreen, bool initialcall) exStyle |= WS_EX_WINDOWEDGE; } - SetWindowLong(Window, GWL_STYLE, style); - SetWindowLong(Window, GWL_EXSTYLE, exStyle); + SetWindowLong(mainwindow.GetHandle(), GWL_STYLE, style); + SetWindowLong(mainwindow.GetHandle(), GWL_EXSTYLE, exStyle); if (fullscreen) { - SetWindowPos(Window, 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); + SetWindowPos(mainwindow.GetHandle(), 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); + 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. } @@ -352,9 +350,9 @@ SystemBaseFrameBuffer::SystemBaseFrameBuffer(void *hMonitor, bool fullscreen) : m_displayDeviceName = 0; 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(); - ShowWindow (Window, SW_SHOW); - SetWindowLong(Window, GWL_STYLE, WS_VISIBLE | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW); - SetWindowLong(Window, GWL_EXSTYLE, WS_EX_WINDOWEDGE); - SetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); + ShowWindow (mainwindow.GetHandle(), SW_SHOW); + SetWindowLong(mainwindow.GetHandle(), GWL_STYLE, WS_VISIBLE | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW); + SetWindowLong(mainwindow.GetHandle(), GWL_EXSTYLE, WS_EX_WINDOWEDGE); + SetWindowPos(mainwindow.GetHandle(), 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); static_cast(Video)->Shutdown(); } @@ -407,13 +405,13 @@ void SystemBaseFrameBuffer::ToggleFullscreen(bool yes) int SystemBaseFrameBuffer::GetClientWidth() { RECT rect = { 0 }; - GetClientRect(Window, &rect); + GetClientRect(mainwindow.GetHandle(), &rect); return rect.right - rect.left; } int SystemBaseFrameBuffer::GetClientHeight() { RECT rect = { 0 }; - GetClientRect(Window, &rect); + GetClientRect(mainwindow.GetHandle(), &rect); return rect.bottom - rect.top; } diff --git a/src/common/platform/win32/gl_sysfb.cpp b/src/common/platform/win32/gl_sysfb.cpp index 8aa266883a..ce282c5fb7 100644 --- a/src/common/platform/win32/gl_sysfb.cpp +++ b/src/common/platform/win32/gl_sysfb.cpp @@ -49,8 +49,7 @@ #include "m_argv.h" #include "engineerrors.h" #include "win32glvideo.h" - -extern HWND Window; +#include "i_mainwindow.h" extern "C" PROC zd_wglGetProcAddress(LPCSTR name); @@ -71,13 +70,13 @@ PFNWGLSWAPINTERVALEXTPROC myWglSwapIntervalExtProc; SystemGLFrameBuffer::SystemGLFrameBuffer(void *hMonitor, bool fullscreen) : SystemBaseFrameBuffer(hMonitor, fullscreen) { - if (!static_cast(Video)->InitHardware(Window, 0)) + if (!static_cast(Video)->InitHardware(mainwindow.GetHandle(), 0)) { I_FatalError("Unable to initialize OpenGL"); return; } - HDC hDC = GetDC(Window); + HDC hDC = GetDC(mainwindow.GetHandle()); const char *wglext = nullptr; myWglSwapIntervalExtProc = (PFNWGLSWAPINTERVALEXTPROC)zd_wglGetProcAddress("wglSwapIntervalEXT"); @@ -102,7 +101,7 @@ SystemGLFrameBuffer::SystemGLFrameBuffer(void *hMonitor, bool fullscreen) : Syst SwapInterval = -1; } } - ReleaseDC(Window, hDC); + ReleaseDC(mainwindow.GetHandle(), hDC); } //========================================================================== diff --git a/src/common/platform/win32/hardware.cpp b/src/common/platform/win32/hardware.cpp index a5e67604f4..31ed03d7ce 100644 --- a/src/common/platform/win32/hardware.cpp +++ b/src/common/platform/win32/hardware.cpp @@ -52,11 +52,10 @@ #endif #include "engineerrors.h" #include "i_system.h" +#include "i_mainwindow.h" EXTERN_CVAR(Int, vid_preferbackend) -extern HWND Window; - IVideo *Video; // 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. // (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 // already foregrounded but may not be focused.) - SetForegroundWindow(Window); - SetFocus(Window); + SetForegroundWindow(mainwindow.GetHandle()); + SetFocus(mainwindow.GetHandle()); // 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 // still receives a WM_ACTIVATEAPP message telling it that it's the diff --git a/src/common/platform/win32/i_crash.cpp b/src/common/platform/win32/i_crash.cpp index 94ca7d6c23..b906635978 100644 --- a/src/common/platform/win32/i_crash.cpp +++ b/src/common/platform/win32/i_crash.cpp @@ -65,6 +65,7 @@ #include "zstring.h" #include "printf.h" #include "cmdlib.h" +#include "i_mainwindow.h" #include #include @@ -212,9 +213,6 @@ struct MiniDumpThreadData // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- -// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- -void I_FlushBufferedConsoleStuff(); - // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- 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; file = CreateTempFile(); if (file != INVALID_HANDLE_VALUE) { - EDITSTREAM streamer = { (DWORD_PTR)file, 0, WriteLogFileStreamer }; - SendMessage(edit, EM_STREAMOUT, SF_RTF, (LPARAM)&streamer); + auto writeFile = [&](const void* data, uint32_t size, uint32_t& written) -> bool + { + DWORD tmp = 0; + BOOL result = WriteFile(file, data, size, &tmp, nullptr); + written = tmp; + return result == TRUE; + }; + mainwindow.GetLog(writeFile); } 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. if (NumFiles != 0) @@ -560,11 +529,7 @@ void CreateCrashLog (const char *custominfo, DWORD customsize, HWND richlog) AddFile (file, "local.txt"); } } - if (richlog != NULL) - { - I_FlushBufferedConsoleStuff(); - AddFile (WriteLogFile(richlog), "log.rtf"); - } + AddFile (WriteLogFile(), "log.rtf"); CloseHandle (DbgProcess); } diff --git a/src/common/platform/win32/i_dijoy.cpp b/src/common/platform/win32/i_dijoy.cpp index 61e7605b38..11f60a35f4 100644 --- a/src/common/platform/win32/i_dijoy.cpp +++ b/src/common/platform/win32/i_dijoy.cpp @@ -53,6 +53,8 @@ #include "keydef.h" #include "printf.h" +#include "i_mainwindow.h" + #define SAFE_RELEASE(x) { if (x != NULL) { x->Release(); x = NULL; } } // WBEMIDL BITS -- because w32api doesn't have this, either ----------------- @@ -266,7 +268,6 @@ protected: // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern LPDIRECTINPUT8 g_pdi; -extern HWND Window; // PUBLIC DATA DEFINITIONS ------------------------------------------------- @@ -372,7 +373,7 @@ bool FDInputJoystick::GetDevice() Printf(TEXTCOLOR_ORANGE "Setting data format for %s failed.\n", Name.GetChars()); return false; } - hr = Device->SetCooperativeLevel(Window, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND); + hr = Device->SetCooperativeLevel(mainwindow.GetHandle(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND); if (FAILED(hr)) { Printf(TEXTCOLOR_ORANGE "Setting cooperative level for %s failed.\n", Name.GetChars()); diff --git a/src/common/platform/win32/i_input.cpp b/src/common/platform/win32/i_input.cpp index 06b1e98017..1b3b4aa834 100644 --- a/src/common/platform/win32/i_input.cpp +++ b/src/common/platform/win32/i_input.cpp @@ -86,6 +86,7 @@ #include "printf.h" #include "c_buttons.h" #include "cmdlib.h" +#include "i_mainwindow.h" // Compensate for w32api's lack #ifndef GET_XBUTTON_WPARAM @@ -116,7 +117,6 @@ extern bool ToggleFullscreen; bool VidResizing; extern BOOL vidactive; -extern HWND Window, ConWindow; EXTERN_CVAR (String, language) EXTERN_CVAR (Bool, lookstrafe) @@ -160,7 +160,7 @@ static void I_CheckGUICapture () void I_SetMouseCapture() { - SetCapture(Window); + SetCapture(mainwindow.GetHandle()); } void I_ReleaseMouseCapture() @@ -444,7 +444,7 @@ LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) break; case WM_SIZE: - InvalidateRect (Window, NULL, FALSE); + InvalidateRect (hWnd, NULL, FALSE); break; case WM_KEYDOWN: @@ -820,7 +820,7 @@ IJoystickConfig *I_UpdateDeviceList() void I_PutInClipboard (const char *str) { - if (str == NULL || !OpenClipboard (Window)) + if (str == NULL || !OpenClipboard (mainwindow.GetHandle())) return; EmptyClipboard (); @@ -842,7 +842,7 @@ FString I_GetFromClipboard (bool return_nothing) HGLOBAL cliphandle; wchar_t *clipstr; - if (return_nothing || !IsClipboardFormatAvailable (CF_UNICODETEXT) || !OpenClipboard (Window)) + if (return_nothing || !IsClipboardFormatAvailable (CF_UNICODETEXT) || !OpenClipboard (mainwindow.GetHandle())) return retstr; cliphandle = GetClipboardData (CF_UNICODETEXT); diff --git a/src/common/platform/win32/i_keyboard.cpp b/src/common/platform/win32/i_keyboard.cpp index 00f9109498..e1222d9697 100644 --- a/src/common/platform/win32/i_keyboard.cpp +++ b/src/common/platform/win32/i_keyboard.cpp @@ -40,6 +40,7 @@ #include "i_input.h" #include "d_eventbase.h" +#include "i_mainwindow.h" // MACROS ------------------------------------------------------------------ @@ -87,7 +88,6 @@ protected: // EXTERNAL DATA DECLARATIONS ---------------------------------------------- -extern HWND Window; extern LPDIRECTINPUT8 g_pdi; extern LPDIRECTINPUT g_pdi3; extern bool GUICapture; @@ -347,7 +347,7 @@ ufailit: return false; } - hr = Device->SetCooperativeLevel(Window, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); + hr = Device->SetCooperativeLevel(mainwindow.GetHandle(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); if (FAILED(hr)) { goto ufailit; @@ -379,7 +379,7 @@ void FDInputKeyboard::ProcessInput() DIDEVICEOBJECTDATA od; DWORD dwElements; HRESULT hr; - bool foreground = (GetForegroundWindow() == Window); + bool foreground = (GetForegroundWindow() == mainwindow.GetHandle()); for (;;) { @@ -448,7 +448,7 @@ bool FRawKeyboard::GetDevice() rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE; rid.usUsage = HID_GDP_KEYBOARD; rid.dwFlags = RIDEV_INPUTSINK; - rid.hwndTarget = Window; + rid.hwndTarget = mainwindow.GetHandle(); if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { return false; diff --git a/src/common/platform/win32/i_main.cpp b/src/common/platform/win32/i_main.cpp index 68cd06a00a..8b417f6f9e 100644 --- a/src/common/platform/win32/i_main.cpp +++ b/src/common/platform/win32/i_main.cpp @@ -80,6 +80,8 @@ #include "startupinfo.h" #include "printf.h" +#include "i_mainwindow.h" + // MACROS ------------------------------------------------------------------ // The main window's title. @@ -94,9 +96,8 @@ // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- 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 I_FlushBufferedConsoleStuff(); void DestroyCustomCursor(); int GameMain(); @@ -120,19 +121,6 @@ HANDLE MainThread; DWORD MainThreadID; HANDLE StdOut; 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 Shell32Module{"Shell32"}; @@ -140,7 +128,6 @@ FModule User32Module{"User32"}; // PRIVATE DATA DEFINITIONS ------------------------------------------------ -static const WCHAR WinClassName[] = WGAMENAME "MainWindow"; static HMODULE hwtsapi32; // handle to wtsapi32.dll // CODE -------------------------------------------------------------------- @@ -174,155 +161,13 @@ static void UnWTS (void) ursn unreg = (ursn)GetProcAddress (hwtsapi32, "WTSUnRegisterSessionNotification"); if (unreg != 0) { - unreg (Window); + unreg (mainwindow.GetHandle()); } FreeLibrary (hwtsapi32); 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 @@ -332,431 +177,7 @@ void LayoutMainWindow (HWND hWnd, HWND pane) void I_SetIWADInfo() { // Make the startup banner show itself - LayoutMainWindow(Window, NULL); -} - -//========================================================================== -// -// 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(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)¶format); - 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); + mainwindow.UpdateLayout(); } static void UnTbp() @@ -904,44 +325,9 @@ int DoMain (HINSTANCE hInstance) 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 */ FStringf caption("" GAMENAME " %s " X64 " (%s)", GetVersionString(), GetGitTime()); - 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); - } + mainwindow.Create(caption, x, y, width, height); if (kernel != NULL) { @@ -957,7 +343,7 @@ int DoMain (HINSTANCE hInstance) if (hwtsapi32 != 0) { 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); hwtsapi32 = 0; @@ -970,7 +356,7 @@ int DoMain (HINSTANCE hInstance) } } - GetClientRect (Window, &cRect); + GetClientRect (mainwindow.GetHandle(), &cRect); WinWidth = cRect.right; WinHeight = cRect.bottom; @@ -979,7 +365,7 @@ int DoMain (HINSTANCE hInstance) atexit (UnCOM); int ret = GameMain (); - CheckForRestart(); + mainwindow.CheckForRestart(); DestroyCustomCursor(); if (ret == 1337) // special exit code for 'norun'. @@ -991,7 +377,7 @@ int DoMain (HINSTANCE hInstance) DWORD bytes; 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); FlushConsoleInputBuffer(stdinput); SetConsoleMode(stdinput, 0); @@ -999,7 +385,7 @@ int DoMain (HINSTANCE hInstance) } else if (StdOut == NULL) { - ShowErrorPane(NULL); + mainwindow.ShowErrorPane(nullptr); } } } @@ -1009,9 +395,8 @@ int DoMain (HINSTANCE hInstance) void I_ShowFatalError(const char *msg) { I_ShutdownGraphics (); - RestoreConView (); + mainwindow.RestoreConView(); S_StopMusic(true); - I_FlushBufferedConsoleStuff(); if (CVMAbortException::stacktrace.IsNotEmpty()) { @@ -1020,7 +405,7 @@ void I_ShowFatalError(const char *msg) if (!batchrun) { - ShowErrorPane(msg); + mainwindow.ShowErrorPane(msg); } else { @@ -1087,7 +472,7 @@ void CALLBACK ExitFatally (ULONG_PTR dummy) { SetUnhandledExceptionFilter (ExitMessedUp); I_ShutdownGraphics (); - RestoreConView (); + mainwindow.RestoreConView (); DisplayCrashLog (); exit(-1); } @@ -1122,7 +507,7 @@ LONG WINAPI CatchAllExceptions (LPEXCEPTION_POINTERS info) CrashPointers = *info; 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. // 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; } __except(CrashPointers = *GetExceptionInformation(), - CreateCrashLog ("TestCrash", 9, NULL), EXCEPTION_EXECUTE_HANDLER) + CreateCrashLog ("TestCrash", 9), EXCEPTION_EXECUTE_HANDLER) { } DisplayCrashLog (); @@ -1221,7 +606,7 @@ int WINAPI wWinMain (HINSTANCE hInstance, HINSTANCE nothing, LPWSTR cmdline, int infiniterecursion(1); } __except(CrashPointers = *GetExceptionInformation(), - CreateCrashLog ("TestStackCrash", 14, NULL), EXCEPTION_EXECUTE_HANDLER) + CreateCrashLog ("TestStackCrash", 14), EXCEPTION_EXECUTE_HANDLER) { } DisplayCrashLog (); @@ -1287,5 +672,5 @@ void I_SetWindowTitle(const char* caption) { widecaption = WideString(caption); } - SetWindowText(Window, widecaption.c_str()); + SetWindowText(mainwindow.GetHandle(), widecaption.c_str()); } diff --git a/src/common/platform/win32/i_mainwindow.cpp b/src/common/platform/win32/i_mainwindow.cpp new file mode 100644 index 0000000000..deacd068ec --- /dev/null +++ b/src/common/platform/win32/i_mainwindow.cpp @@ -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 +#include +#include + +#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)¶format); + 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(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*>(cookie); + if (!writeData((const void*)buffer, cb, didwrite)) + { + return 1; + } + *pcb = didwrite; + return 0; +} + +void MainWindow::GetLog(std::function writeData) +{ + FlushBufferedConsoleStuff(); + + EDITSTREAM streamer = { (DWORD_PTR)&writeData, 0, WriteLogFileStreamer }; + SendMessage(ConWindow, EM_STREAMOUT, SF_RTF, (LPARAM)&streamer); +} diff --git a/src/common/platform/win32/i_mainwindow.h b/src/common/platform/win32/i_mainwindow.h new file mode 100644 index 0000000000..de00bd6064 --- /dev/null +++ b/src/common/platform/win32/i_mainwindow.h @@ -0,0 +1,93 @@ +#pragma once + +#include "zstring.h" +#include "printf.h" + +#include + +#define WIN32_LEAN_AND_MEAN +#include + +// 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 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 bufferedConsoleStuff; + + HWND ProgressBar = 0; + + HWND NetStartPane = 0; + int NetStartMaxPos = 0; + + HWND StartupScreen = 0; +}; + +extern MainWindow mainwindow; diff --git a/src/common/platform/win32/i_mouse.cpp b/src/common/platform/win32/i_mouse.cpp index 4c2673e35b..4702a94202 100644 --- a/src/common/platform/win32/i_mouse.cpp +++ b/src/common/platform/win32/i_mouse.cpp @@ -46,6 +46,7 @@ #include "menustate.h" #include "keydef.h" #include "i_interface.h" +#include "i_mainwindow.h" // MACROS ------------------------------------------------------------------ @@ -138,7 +139,6 @@ static void CenterMouse(int x, int y, LONG *centx, LONG *centy); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- -extern HWND Window; extern LPDIRECTINPUT8 g_pdi; extern LPDIRECTINPUT g_pdi3; extern bool GUICapture; @@ -193,11 +193,11 @@ CUSTOM_CVAR (Int, in_mouse, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) static void SetCursorState(bool visible) { CursorState = visible || !m_hidepointer; - if (GetForegroundWindow() == Window) + if (GetForegroundWindow() == mainwindow.GetHandle()) { if (CursorState) { - SetCursor((HCURSOR)(intptr_t)GetClassLongPtr(Window, GCLP_HCURSOR)); + SetCursor((HCURSOR)(intptr_t)GetClassLongPtr(mainwindow.GetHandle(), GCLP_HCURSOR)); } else { @@ -219,7 +219,7 @@ static void CenterMouse(int curx, int cury, LONG *centxp, LONG *centyp) { RECT rect; - GetWindowRect(Window, &rect); + GetWindowRect(mainwindow.GetHandle(), &rect); int centx = (rect.left + rect.right) >> 1; int centy = (rect.top + rect.bottom) >> 1; @@ -258,7 +258,7 @@ void I_CheckNativeMouse(bool preferNative, bool eventhandlerresult) } else { - if ((GetForegroundWindow() != Window) || preferNative || !use_mouse) + if ((GetForegroundWindow() != mainwindow.GetHandle()) || preferNative || !use_mouse) { want_native = true; } @@ -490,7 +490,7 @@ bool FRawMouse::GetDevice() rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE; rid.usUsage = HID_GDP_MOUSE; rid.dwFlags = 0; - rid.hwndTarget = Window; + rid.hwndTarget = mainwindow.GetHandle(); if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { return false; @@ -516,7 +516,7 @@ void FRawMouse::Grab() rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE; rid.usUsage = HID_GDP_MOUSE; rid.dwFlags = RIDEV_CAPTUREMOUSE | RIDEV_NOLEGACY; - rid.hwndTarget = Window; + rid.hwndTarget = mainwindow.GetHandle(); if (RegisterRawInputDevices(&rid, 1, sizeof(rid))) { GetCursorPos(&UngrabbedPointerPos); @@ -718,7 +718,7 @@ ufailit: return false; } - hr = Device->SetCooperativeLevel(Window, DISCL_EXCLUSIVE | DISCL_FOREGROUND); + hr = Device->SetCooperativeLevel(mainwindow.GetHandle(), DISCL_EXCLUSIVE | DISCL_FOREGROUND); if (FAILED(hr)) { goto ufailit; @@ -1066,11 +1066,11 @@ void FWin32Mouse::Grab() GetCursorPos(&UngrabbedPointerPos); ClipCursor(NULL); // helps with Win95? - GetClientRect(Window, &rect); + GetClientRect(mainwindow.GetHandle(), &rect); // Reposition the rect so that it only covers the client area. - ClientToScreen(Window, (LPPOINT)&rect.left); - ClientToScreen(Window, (LPPOINT)&rect.right); + ClientToScreen(mainwindow.GetHandle(), (LPPOINT)&rect.left); + ClientToScreen(mainwindow.GetHandle(), (LPPOINT)&rect.right); ClipCursor(&rect); SetCursorState(false); diff --git a/src/common/platform/win32/i_rawps2.cpp b/src/common/platform/win32/i_rawps2.cpp index f418872e81..f063788039 100644 --- a/src/common/platform/win32/i_rawps2.cpp +++ b/src/common/platform/win32/i_rawps2.cpp @@ -44,6 +44,8 @@ #include "cmdlib.h" #include "keydef.h" +#include "i_mainwindow.h" + // MACROS ------------------------------------------------------------------ #define DEFAULT_DEADZONE 0.25f @@ -214,8 +216,6 @@ struct PS2Descriptor // EXTERNAL DATA DECLARATIONS ---------------------------------------------- -extern HWND Window; - // PUBLIC DATA DEFINITIONS ------------------------------------------------- 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.usUsage = HID_GDP_JOYSTICK; rid.dwFlags = RIDEV_INPUTSINK; - rid.hwndTarget = Window; + rid.hwndTarget = mainwindow.GetHandle(); if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { return false; @@ -1273,7 +1273,7 @@ void FRawPS2Manager::DoRegister() if (!Registered) { rid.dwFlags = RIDEV_INPUTSINK; - rid.hwndTarget = Window; + rid.hwndTarget = mainwindow.GetHandle(); if (RegisterRawInputDevices(&rid, 1, sizeof(rid))) { Registered = true; diff --git a/src/common/platform/win32/i_system.cpp b/src/common/platform/win32/i_system.cpp index 9a3f4a795d..68a1261ea2 100644 --- a/src/common/platform/win32/i_system.cpp +++ b/src/common/platform/win32/i_system.cpp @@ -81,6 +81,7 @@ #include "bitmap.h" #include "cmdlib.h" #include "i_interface.h" +#include "i_mainwindow.h" // MACROS ------------------------------------------------------------------ @@ -92,10 +93,6 @@ // TYPES ------------------------------------------------------------------- -// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- - -extern void LayoutMainWindow(HWND hWnd, HWND pane); - // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- void DestroyCustomCursor(); @@ -116,13 +113,11 @@ EXTERN_CVAR (Bool, autoloadbrightmaps) EXTERN_CVAR (Bool, autoloadwidescreen) EXTERN_CVAR (Int, vid_preferbackend) -extern HWND Window, ConWindow, GameTitleWindow; extern HANDLE StdOut; extern bool FancyStdOut; extern HINSTANCE g_hInst; extern FILE *Logfile; extern bool NativeMouse; -extern bool ConWindowHidden; // 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; wchar_t wbuf[256]; 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; auto outputIt = [&]() { wbuf[bpos] = 0; - if (edit != nullptr) - { - SendMessageW(edit, EM_REPLACESEL, FALSE, (LPARAM)wbuf); - } if (con_debugoutput) { OutputDebugStringW(wbuf); @@ -385,17 +357,6 @@ static void DoPrintStr(const char *cpt, HWND edit, HANDLE StdOut) } 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(); } - 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) { // Set text back to gray, in case it was changed. SetConsoleTextAttribute(StdOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); } } -static TArray bufferedConsoleStuff; - void I_PrintStr(const char *cp) { - if (ConWindowHidden) - { - 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(); + mainwindow.PrintStr(cp); + PrintToStdOut(cp, StdOut); } //========================================================================== @@ -635,7 +560,7 @@ int I_PickIWad(WadStuff *wads, int numwads, bool showwin, int defaultiwad) DefaultWad = defaultiwad; return (int)DialogBox(g_hInst, MAKEINTRESOURCE(IDD_IWADDIALOG), - (HWND)Window, (DLGPROC)IWADBoxCallback); + (HWND)mainwindow.GetHandle(), (DLGPROC)IWADBoxCallback); } return defaultiwad; } @@ -682,16 +607,16 @@ bool I_SetCursor(FGameTexture *cursorpic) DestroyCustomCursor(); cursor = LoadCursor(NULL, IDC_ARROW); } - SetClassLongPtr(Window, GCLP_HCURSOR, (LONG_PTR)cursor); + SetClassLongPtr(mainwindow.GetHandle(), GCLP_HCURSOR, (LONG_PTR)cursor); if (NativeMouse) { POINT pt; RECT client; // If the mouse pointer is within the window's client rect, set it now. - if (GetCursorPos(&pt) && GetClientRect(Window, &client) && - ClientToScreen(Window, (LPPOINT)&client.left) && - ClientToScreen(Window, (LPPOINT)&client.right)) + if (GetCursorPos(&pt) && GetClientRect(mainwindow.GetHandle(), &client) && + ClientToScreen(mainwindow.GetHandle(), (LPPOINT)&client.left) && + ClientToScreen(mainwindow.GetHandle(), (LPPOINT)&client.right)) { if (pt.x >= client.left && pt.x < client.right && 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); 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; } diff --git a/src/common/platform/win32/i_system.h b/src/common/platform/win32/i_system.h index f7b2f239b9..648d8705e9 100644 --- a/src/common/platform/win32/i_system.h +++ b/src/common/platform/win32/i_system.h @@ -43,9 +43,6 @@ int I_PickIWad (WadStuff *wads, int numwads, bool queryiwad, int defaultiwad); // The ini could not be saved at exit 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 // directories for IWADs if the user purchased any through Steam. TArray I_GetSteamPath(); diff --git a/src/common/platform/win32/st_start.cpp b/src/common/platform/win32/st_start.cpp index 53f7018852..7b1f314417 100644 --- a/src/common/platform/win32/st_start.cpp +++ b/src/common/platform/win32/st_start.cpp @@ -53,6 +53,7 @@ #include "startupinfo.h" #include "i_interface.h" #include "texturemanager.h" +#include "i_mainwindow.h" // MACROS ------------------------------------------------------------------ @@ -67,24 +68,13 @@ // 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_InvalidateRect (HWND hwnd, BitmapInfo *bitmap_info, int left, int top, int right, int bottom); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- -// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- - -static INT_PTR CALLBACK NetStartPaneProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); - // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern HINSTANCE g_hInst; -extern HWND Window, ConWindow, ProgressBar, NetStartPane, StartupScreen, GameTitleWindow; // PUBLIC DATA DEFINITIONS ------------------------------------------------- @@ -112,7 +102,7 @@ CUSTOM_CVAR(Int, showendoom, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) FStartupScreen *FStartupScreen::CreateInstance(int max_progress) { FStartupScreen *scr = NULL; - HRESULT hr = -1; + HRESULT hr = E_FAIL; if (!Args->CheckParm("-nostartup")) { @@ -154,12 +144,7 @@ FBasicStartupScreen::FBasicStartupScreen(int max_progress, bool show_bar) { if (show_bar) { - ProgressBar = CreateWindowEx(0, PROGRESS_CLASS, - 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); + mainwindow.ShowProgressBar(MaxPos); } NetMaxPos = 0; NetCurPos = 0; @@ -176,13 +161,8 @@ FBasicStartupScreen::FBasicStartupScreen(int max_progress, bool show_bar) FBasicStartupScreen::~FBasicStartupScreen() { - if (ProgressBar != NULL) - { - DestroyWindow (ProgressBar); - ProgressBar = NULL; - LayoutMainWindow (Window, NULL); - } - KillTimer(Window, 1337); + mainwindow.HideProgressBar(); + KillTimer(mainwindow.GetHandle(), 1337); } //========================================================================== @@ -198,7 +178,7 @@ void FBasicStartupScreen::Progress() if (CurPos < MaxPos) { 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) { NetMaxPos = numplayers; - if (NetStartPane == NULL) - { - 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; + mainwindow.ShowNetStartPane(message, numplayers); - 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; NetCurPos = 0; NetProgress(1); // You always know about yourself @@ -287,12 +214,7 @@ void FBasicStartupScreen::NetInit(const char *message, int numplayers) void FBasicStartupScreen::NetDone() { - if (NetStartPane != NULL) - { - DestroyWindow (NetStartPane); - NetStartPane = NULL; - LayoutMainWindow (Window, NULL); - } + mainwindow.HideNetStartPane(); } //========================================================================== @@ -325,7 +247,7 @@ void FBasicStartupScreen::NetMessage(const char *format, ...) // //========================================================================== -void FBasicStartupScreen :: NetProgress(int count) +void FBasicStartupScreen::NetProgress(int count) { if (count == 0) { @@ -335,23 +257,8 @@ void FBasicStartupScreen :: NetProgress(int 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); - SetDlgItemTextA (NetStartPane, IDC_NETSTARTCOUNT, buf); - SendDlgItemMessage (NetStartPane, IDC_NETSTARTPROGRESS, PBM_SETPOS, min(NetCurPos, NetMaxPos), 0); - } + mainwindow.SetNetStartProgress(count); } //========================================================================== @@ -370,59 +277,7 @@ void FBasicStartupScreen :: NetProgress(int count) bool FBasicStartupScreen::NetLoop(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 (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; + return mainwindow.RunMessageLoop(timer_callback, userdata); } //========================================================================== @@ -437,6 +292,7 @@ static INT_PTR CALLBACK NetStartPaneProc (HWND hDlg, UINT msg, WPARAM wParam, LP FGraphicalStartupScreen::FGraphicalStartupScreen(int max_progress) : FBasicStartupScreen(max_progress, false) { + mainwindow.ShowStartupScreen(); } //========================================================================== @@ -447,11 +303,7 @@ FGraphicalStartupScreen::FGraphicalStartupScreen(int max_progress) FGraphicalStartupScreen::~FGraphicalStartupScreen() { - if (StartupScreen != NULL) - { - DestroyWindow (StartupScreen); - StartupScreen = NULL; - } + mainwindow.HideStartupScreen(); if (StartupBitmap != NULL) { ST_Util_FreeBitmap (StartupBitmap); @@ -468,8 +320,8 @@ FGraphicalStartupScreen::~FGraphicalStartupScreen() void FHexenStartupScreen::SetWindowSize() { ST_Util_SizeWindowForBitmap(1); - LayoutMainWindow(Window, NULL); - InvalidateRect(StartupScreen, NULL, TRUE); + mainwindow.UpdateLayout(); + mainwindow.InvalidateStartupScreen(); } //========================================================================== @@ -481,8 +333,8 @@ void FHexenStartupScreen::SetWindowSize() void FHereticStartupScreen::SetWindowSize() { ST_Util_SizeWindowForBitmap(1); - LayoutMainWindow(Window, NULL); - InvalidateRect(StartupScreen, NULL, TRUE); + mainwindow.UpdateLayout(); + mainwindow.InvalidateStartupScreen(); } //========================================================================== @@ -494,8 +346,8 @@ void FHereticStartupScreen::SetWindowSize() void FStrifeStartupScreen::SetWindowSize() { ST_Util_SizeWindowForBitmap(2); - LayoutMainWindow(Window, NULL); - InvalidateRect(StartupScreen, NULL, TRUE); + mainwindow.UpdateLayout(); + mainwindow.InvalidateStartupScreen(); } //========================================================================== @@ -532,13 +384,10 @@ int RunEndoom() return 0; } - if (!ST_Util_CreateStartupWindow()) - { - return 0; - } + mainwindow.ShowStartupScreen(); I_ShutdownGraphics (); - RestoreConView (); + mainwindow.RestoreConView (); S_StopMusic(true); fileSystem.ReadFile (endoom_lump, endoom_screen); @@ -548,15 +397,11 @@ int RunEndoom() ST_Util_DrawTextScreen (StartupBitmap, endoom_screen); // Make the title banner go away. - if (GameTitleWindow != NULL) - { - DestroyWindow (GameTitleWindow); - GameTitleWindow = NULL; - } + mainwindow.HideGameTitleWindow(); ST_Util_SizeWindowForBitmap (1); - LayoutMainWindow (Window, NULL); - InvalidateRect (StartupScreen, NULL, TRUE); + mainwindow.UpdateLayout(); + mainwindow.InvalidateStartupScreen(); // Does this screen need blinking? for (i = 0; i < 80*25; ++i) @@ -567,7 +412,7 @@ int RunEndoom() break; } } - if (blinking && SetTimer (Window, 0x5A15A, BLINK_PERIOD, NULL) == 0) + if (blinking && SetTimer (mainwindow.GetHandle(), 0x5A15A, BLINK_PERIOD, NULL) == 0) { blinking = false; } @@ -581,12 +426,12 @@ int RunEndoom() { if (blinking) { - KillTimer (Window, 0x5A15A); + KillTimer (mainwindow.GetHandle(), 0x5A15A); } ST_Util_FreeBitmap (StartupBitmap); 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); 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 @@ -640,16 +464,7 @@ void ST_Util_SizeWindowForBitmap (int scale) int w, h, cx, cy, x, y; RECT rect; - if (GameTitleWindow != NULL) - { - GetClientRect (GameTitleWindow, &rect); - } - else - { - rect.bottom = 0; - } - RECT sizerect = { 0, 0, StartupBitmap->bmiHeader.biWidth * scale, - StartupBitmap->bmiHeader.biHeight * scale + rect.bottom }; + RECT sizerect = { 0, 0, StartupBitmap->bmiHeader.biWidth * scale, StartupBitmap->bmiHeader.biHeight * scale + mainwindow.GetGameTitleWindowHeight() }; AdjustWindowRectEx(&sizerect, WS_VISIBLE|WS_OVERLAPPEDWINDOW, FALSE, WS_EX_APPWINDOW); w = sizerect.right - sizerect.left; h = sizerect.bottom - sizerect.top; @@ -659,7 +474,7 @@ void ST_Util_SizeWindowForBitmap (int scale) memset (&displaysettings, 0, sizeof(displaysettings)); displaysettings.dmSize = sizeof(displaysettings); EnumDisplaySettings (NULL, ENUM_CURRENT_SETTINGS, &displaysettings); - GetWindowRect (Window, &rect); + GetWindowRect (mainwindow.GetHandle(), &rect); cx = (rect.left + rect.right) / 2; cy = (rect.top + rect.bottom) / 2; x = cx - w / 2; @@ -680,7 +495,7 @@ void ST_Util_SizeWindowForBitmap (int scale) { 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) { - ST_Util_InvalidateRect(StartupScreen , bitmap_info, left, top, right, bottom); + mainwindow.InvalidateStartupScreen(left, top, right, bottom); } diff --git a/src/common/platform/win32/st_start_util.cpp b/src/common/platform/win32/st_start_util.cpp index c71f43df3f..0aa6ae9b80 100644 --- a/src/common/platform/win32/st_start_util.cpp +++ b/src/common/platform/win32/st_start_util.cpp @@ -43,14 +43,12 @@ #include "startupinfo.h" #include "palutil.h" #include "i_interface.h" +#include "i_mainwindow.h" uint8_t* GetHexChar(int codepoint); 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[] = { 0x0000, //#NULL 0x0001, //#START OF HEADING @@ -420,7 +418,7 @@ FHexenStartupScreen::FHexenStartupScreen(int max_progress, long& hr) int notch_lump = fileSystem.CheckNumForName("NOTCH"); 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 || 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]; hr = -1; - if (loading_lump < 0 || fileSystem.FileLength(loading_lump) != 4000 || !ST_Util_CreateStartupWindow()) + if (loading_lump < 0 || fileSystem.FileLength(loading_lump) != 4000) { 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_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++; I_GetEvent(); } @@ -678,7 +676,7 @@ void FHereticStartupScreen::AppendStatusLine(const char* status) { 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; I_GetEvent(); } @@ -711,7 +709,7 @@ FStrifeStartupScreen::FStrifeStartupScreen(int max_progress, long& hr) 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; } @@ -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; 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) { // 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; 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) { @@ -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; 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) { @@ -1165,7 +1163,7 @@ void ST_Util_UpdateTextBlink(BitmapInfo* bitmap_info, const uint8_t* text_screen if (text_screen[1] & 0x80) { 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; } diff --git a/src/common/platform/win32/win32basevideo.cpp b/src/common/platform/win32/win32basevideo.cpp index b579b21f33..b67af93d77 100644 --- a/src/common/platform/win32/win32basevideo.cpp +++ b/src/common/platform/win32/win32basevideo.cpp @@ -47,6 +47,7 @@ #include "printf.h" #include "win32basevideo.h" #include "cmdlib.h" +#include "i_mainwindow.h" CVAR(Int, vid_adapter, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) @@ -58,7 +59,7 @@ CVAR(Int, vid_adapter, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) Win32BaseVideo::Win32BaseVideo() { - I_SetWndProc(); + mainwindow.ShowGameView(); GetDisplayDeviceName(); } diff --git a/src/common/platform/win32/win32polyvideo.cpp b/src/common/platform/win32/win32polyvideo.cpp index 9a4538d292..b6781b4ce0 100644 --- a/src/common/platform/win32/win32polyvideo.cpp +++ b/src/common/platform/win32/win32polyvideo.cpp @@ -3,6 +3,7 @@ #include "hardware.h" #include "engineerrors.h" #include +#include "i_mainwindow.h" #ifdef HAVE_SOFTPOLY @@ -10,8 +11,6 @@ EXTERN_CVAR(Bool, vid_vsync) bool ViewportLinearScale(); -extern HWND Window; - #include #pragma comment(lib, "d3d9.lib") @@ -41,7 +40,7 @@ void I_PolyPresentInit() } RECT rect = {}; - GetClientRect(Window, &rect); + GetClientRect(mainwindow.GetHandle(), &rect); ClientWidth = rect.right; ClientHeight = rect.bottom; @@ -52,10 +51,10 @@ void I_PolyPresentInit() pp.BackBufferWidth = ClientWidth; pp.BackBufferHeight = ClientHeight; pp.BackBufferCount = 1; - pp.hDeviceWindow = Window; + pp.hDeviceWindow = mainwindow.GetHandle(); 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)) { I_FatalError("IDirect3D9.CreateDevice failed"); @@ -67,7 +66,7 @@ uint8_t *I_PolyPresentLock(int w, int h, bool vsync, int &pitch) HRESULT result; RECT rect = {}; - GetClientRect(Window, &rect); + GetClientRect(mainwindow.GetHandle(), &rect); if (rect.right != ClientWidth || rect.bottom != ClientHeight || CurrentVSync != vsync) { if (surface) @@ -86,7 +85,7 @@ uint8_t *I_PolyPresentLock(int w, int h, bool vsync, int &pitch) pp.BackBufferWidth = ClientWidth; pp.BackBufferHeight = ClientHeight; pp.BackBufferCount = 1; - pp.hDeviceWindow = Window; + pp.hDeviceWindow = mainwindow.GetHandle(); pp.PresentationInterval = CurrentVSync ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE; device->Reset(&pp); } diff --git a/src/common/platform/win32/win32vulkanvideo.cpp b/src/common/platform/win32/win32vulkanvideo.cpp index 4c8f887c09..88c886c959 100644 --- a/src/common/platform/win32/win32vulkanvideo.cpp +++ b/src/common/platform/win32/win32vulkanvideo.cpp @@ -7,16 +7,14 @@ #endif #include "volk/volk.h" - - -extern HWND Window; +#include "i_mainwindow.h" void I_GetVulkanDrawableSize(int *width, int *height) { assert(Window); RECT clientRect = { 0 }; - GetClientRect(Window, &clientRect); + GetClientRect(mainwindow.GetHandle(), &clientRect); 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.pNext = nullptr; windowCreateInfo.flags = 0; - windowCreateInfo.hwnd = Window; + windowCreateInfo.hwnd = mainwindow.GetHandle(); windowCreateInfo.hinstance = GetModuleHandle(nullptr); const VkResult result = vkCreateWin32SurfaceKHR(instance, &windowCreateInfo, nullptr, surface); diff --git a/src/common/rendering/r_thread.cpp b/src/common/rendering/r_thread.cpp index 552418605b..b9e70d67e5 100644 --- a/src/common/rendering/r_thread.cpp +++ b/src/common/rendering/r_thread.cpp @@ -32,10 +32,6 @@ #include "polyrenderer/drawers/poly_triangle.h" #include -#ifdef WIN32 -void PeekThreadedErrorPane(); -#endif - CVAR(Int, r_multithreaded, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); CVAR(Int, r_debug_draw, 0, 0); @@ -101,12 +97,7 @@ void DrawerThreads::WaitForWorkers() std::unique_lock end_lock(queue->end_mutex); if (!queue->end_condition.wait_for(end_lock, 5s, [&]() { return queue->tasks_left == 0; })) { -#ifdef WIN32 - 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; + I_FatalError("Drawer threads did not finish within 5 seconds!"); } end_lock.unlock(); diff --git a/src/d_anonstats.cpp b/src/d_anonstats.cpp index 42c4d34ef5..4df68406c9 100644 --- a/src/d_anonstats.cpp +++ b/src/d_anonstats.cpp @@ -11,6 +11,8 @@ void D_ConfirmSendStats() #else // !NO_SEND_STATS +#include "i_mainwindow.h" + #if defined(_WIN32) #define WIN32_LEAN_AND_MEAN #include @@ -340,8 +342,7 @@ void D_ConfirmSendStats() UCVarValue enabled = { 0 }; #ifdef _WIN32 - extern HWND Window; - enabled.Int = MessageBoxA(Window, MESSAGE_TEXT, TITLE_TEXT, MB_ICONQUESTION | MB_YESNO) == IDYES; + enabled.Int = MessageBoxA(mainwindow.GetHandle(), MESSAGE_TEXT, TITLE_TEXT, MB_ICONQUESTION | MB_YESNO) == IDYES; #elif defined __APPLE__ const CFStringRef messageString = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, MESSAGE_TEXT, kCFStringEncodingASCII, kCFAllocatorNull); const CFStringRef titleString = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, TITLE_TEXT, kCFStringEncodingASCII, kCFAllocatorNull); diff --git a/src/rendering/swrenderer/scene/r_scene.cpp b/src/rendering/swrenderer/scene/r_scene.cpp index 7bc3bec2f9..38773fb609 100644 --- a/src/rendering/swrenderer/scene/r_scene.cpp +++ b/src/rendering/swrenderer/scene/r_scene.cpp @@ -59,10 +59,6 @@ #include "swrenderer/things/r_playersprite.h" #include -#ifdef WIN32 -void PeekThreadedErrorPane(); -#endif - EXTERN_CVAR(Int, r_clearbuffer) EXTERN_CVAR(Int, r_debug_draw) @@ -239,12 +235,7 @@ namespace swrenderer finished_threads++; if (!end_condition.wait_for(end_lock, 5s, [&]() { return finished_threads == Threads.size(); })) { -#ifdef WIN32 - 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; + I_FatalError("Render threads did not finish within 5 seconds!"); } finished_threads = 0; }