mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-22 04:01:31 +00:00
Create initial error and netstart windows
This commit is contained in:
parent
b7362aa3f2
commit
3f50136c8d
23 changed files with 240 additions and 890 deletions
|
@ -104,6 +104,8 @@ public:
|
|||
Point MapToGlobal(const Point& pos) const;
|
||||
Point MapToParent(const Point& pos) const { return MapTo(Parent(), pos); }
|
||||
|
||||
static Size GetScreenSize();
|
||||
|
||||
protected:
|
||||
virtual void OnPaintFrame(Canvas* canvas) { }
|
||||
virtual void OnPaint(Canvas* canvas) { }
|
||||
|
|
|
@ -141,6 +141,8 @@ public:
|
|||
static void RunLoop();
|
||||
static void ExitLoop();
|
||||
|
||||
static Size GetScreenSize();
|
||||
|
||||
virtual ~DisplayWindow() = default;
|
||||
|
||||
virtual void SetWindowTitle(const std::string& text) = 0;
|
||||
|
|
|
@ -592,3 +592,8 @@ void Widget::OnWindowDeactivated()
|
|||
void Widget::OnWindowDpiScaleChanged()
|
||||
{
|
||||
}
|
||||
|
||||
Size Widget::GetScreenSize()
|
||||
{
|
||||
return DisplayWindow::GetScreenSize();
|
||||
}
|
||||
|
|
|
@ -966,9 +966,9 @@ void TextEdit::LayoutLines(Canvas* canvas)
|
|||
{
|
||||
line.layout.Clear();
|
||||
if (!line.text.empty())
|
||||
line.layout.AddText(line.text, font, Colorf());
|
||||
line.layout.AddText(line.text, font, Colorf::fromRgba8(255, 255, 255));
|
||||
else
|
||||
line.layout.AddText(" ", font, Colorf()); // Draw one space character to get the correct height
|
||||
line.layout.AddText(" ", font, Colorf::fromRgba8(255, 255, 255)); // Draw one space character to get the correct height
|
||||
line.layout.Layout(canvas, GetWidth());
|
||||
line.box = Rect(draw_pos, line.layout.GetSize());
|
||||
line.invalidated = false;
|
||||
|
@ -989,6 +989,7 @@ void TextEdit::LayoutLines(Canvas* canvas)
|
|||
if (cursor_blink_visible && cursor_pos.y == i)
|
||||
{
|
||||
line.layout.SetCursorPos(cursor_pos.x);
|
||||
line.layout.SetCursorColor(Colorf::fromRgba8(255, 255, 255));
|
||||
line.layout.ShowCursor();
|
||||
}
|
||||
}
|
||||
|
@ -1006,8 +1007,8 @@ void TextEdit::OnPaintFrame(Canvas* canvas)
|
|||
{
|
||||
double w = GetFrameGeometry().width;
|
||||
double h = GetFrameGeometry().height;
|
||||
Colorf bordercolor(200 / 255.0f, 200 / 255.0f, 200 / 255.0f);
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, w, h), Colorf::fromRgba8(255, 255, 255));
|
||||
Colorf bordercolor = Colorf::fromRgba8(100, 100, 100);
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, w, h), Colorf::fromRgba8(38, 38, 38));
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, w, 1.0), bordercolor);
|
||||
canvas->fillRect(Rect::xywh(0.0, h - 1.0, w, 1.0), bordercolor);
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, 1.0, h - 0.0), bordercolor);
|
||||
|
|
|
@ -481,5 +481,16 @@ void Win32Window::ExitLoop()
|
|||
ExitRunLoop = true;
|
||||
}
|
||||
|
||||
Size Win32Window::GetScreenSize()
|
||||
{
|
||||
HDC screenDC = GetDC(0);
|
||||
int screenWidth = GetDeviceCaps(screenDC, HORZRES);
|
||||
int screenHeight = GetDeviceCaps(screenDC, VERTRES);
|
||||
double dpiScale = GetDeviceCaps(screenDC, LOGPIXELSX) / 96.0;
|
||||
ReleaseDC(0, screenDC);
|
||||
|
||||
return Size(screenWidth / dpiScale, screenHeight / dpiScale);
|
||||
}
|
||||
|
||||
std::list<Win32Window*> Win32Window::Windows;
|
||||
bool Win32Window::ExitRunLoop;
|
||||
|
|
|
@ -49,6 +49,7 @@ public:
|
|||
static void ProcessEvents();
|
||||
static void RunLoop();
|
||||
static void ExitLoop();
|
||||
static Size GetScreenSize();
|
||||
|
||||
static bool ExitRunLoop;
|
||||
static std::list<Win32Window*> Windows;
|
||||
|
|
|
@ -25,6 +25,11 @@ void DisplayWindow::ExitLoop()
|
|||
Win32Window::ExitLoop();
|
||||
}
|
||||
|
||||
Size DisplayWindow::GetScreenSize()
|
||||
{
|
||||
return Win32Window::GetScreenSize();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
std::unique_ptr<DisplayWindow> DisplayWindow::Create(DisplayWindowHost* windowHost)
|
||||
|
@ -47,4 +52,9 @@ void DisplayWindow::ExitLoop()
|
|||
throw std::runtime_error("DisplayWindow::ExitLoop not implemented");
|
||||
}
|
||||
|
||||
Size DisplayWindow::GetScreenSize()
|
||||
{
|
||||
throw std::runtime_error("DisplayWindow::GetScreenSize not implemented");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1000,6 +1000,9 @@ set (PCH_SOURCES
|
|||
common/startscreen/startscreen_generic.cpp
|
||||
common/startscreen/endoom.cpp
|
||||
common/widgets/launcherwindow.cpp
|
||||
common/widgets/errorwindow.cpp
|
||||
common/widgets/netstartwindow.cpp
|
||||
common/widgets/widgetresourcedata.cpp
|
||||
common/fonts/singlelumpfont.cpp
|
||||
common/fonts/singlepicfont.cpp
|
||||
common/fonts/specialfont.cpp
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
class FStartupScreen
|
||||
{
|
||||
public:
|
||||
static FStartupScreen *CreateInstance(int max_progress, bool showprogress);
|
||||
static FStartupScreen *CreateInstance(int max_progress);
|
||||
|
||||
FStartupScreen(int max_progress)
|
||||
{
|
||||
|
@ -66,7 +66,7 @@ protected:
|
|||
class FBasicStartupScreen : public FStartupScreen
|
||||
{
|
||||
public:
|
||||
FBasicStartupScreen(int max_progress, bool show_bar);
|
||||
FBasicStartupScreen(int max_progress);
|
||||
~FBasicStartupScreen();
|
||||
|
||||
void Progress();
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
FBasicStartupScreen::FBasicStartupScreen(int maxProgress, bool showBar)
|
||||
FBasicStartupScreen::FBasicStartupScreen(int maxProgress)
|
||||
: FStartupScreen(maxProgress)
|
||||
{
|
||||
FConsoleWindow& consoleWindow = FConsoleWindow::GetInstance();
|
||||
|
@ -132,7 +132,7 @@ bool FBasicStartupScreen::NetLoop(bool (*timerCallback)(void*), void* const user
|
|||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
FStartupScreen *FStartupScreen::CreateInstance(const int maxProgress, bool showprogress)
|
||||
FStartupScreen *FStartupScreen::CreateInstance(const int maxProgress)
|
||||
{
|
||||
return new FBasicStartupScreen(maxProgress, showprogress);
|
||||
return new FBasicStartupScreen(maxProgress);
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ static const char SpinnyProgressChars[4] = { '|', '/', '-', '\\' };
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
FStartupScreen *FStartupScreen::CreateInstance(int max_progress, bool)
|
||||
FStartupScreen *FStartupScreen::CreateInstance(int max_progress)
|
||||
{
|
||||
return new FTTYStartupScreen(max_progress);
|
||||
}
|
||||
|
|
|
@ -132,8 +132,6 @@ bool FancyStdOut, AttachedStdOut;
|
|||
|
||||
void I_SetIWADInfo()
|
||||
{
|
||||
// Make the startup banner show itself
|
||||
mainwindow.UpdateLayout();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -266,7 +264,14 @@ int DoMain (HINSTANCE hInstance)
|
|||
atexit ([](){ CoUninitialize(); }); // beware of calling convention.
|
||||
|
||||
int ret = GameMain ();
|
||||
mainwindow.CheckForRestart();
|
||||
|
||||
if (mainwindow.CheckForRestart())
|
||||
{
|
||||
HMODULE hModule = GetModuleHandleW(NULL);
|
||||
WCHAR path[MAX_PATH];
|
||||
GetModuleFileNameW(hModule, path, MAX_PATH);
|
||||
ShellExecuteW(NULL, L"open", path, GetCommandLineW(), NULL, SW_SHOWNORMAL);
|
||||
}
|
||||
|
||||
DestroyCustomCursor();
|
||||
if (ret == 1337) // special exit code for 'norun'.
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#include "v_font.h"
|
||||
#include "i_net.h"
|
||||
#include "engineerrors.h"
|
||||
#include "common/widgets/errorwindow.h"
|
||||
#include "common/widgets/netstartwindow.h"
|
||||
#include <richedit.h>
|
||||
#include <shellapi.h>
|
||||
#include <commctrl.h>
|
||||
|
@ -18,14 +20,6 @@
|
|||
|
||||
#pragma comment(lib, "dwmapi.lib")
|
||||
|
||||
#ifdef _M_X64
|
||||
#define X64 " 64-bit"
|
||||
#elif _M_ARM64
|
||||
#define X64 " ARM-64"
|
||||
#else
|
||||
#define X64 ""
|
||||
#endif
|
||||
|
||||
MainWindow mainwindow;
|
||||
|
||||
void MainWindow::Create(const FString& caption, int x, int y, int width, int height)
|
||||
|
@ -81,29 +75,6 @@ void MainWindow::Create(const FString& caption, int x, int y, int width, int hei
|
|||
DwmSetWindowAttribute(Window, 36/*DWMWA_TEXT_COLOR*/, &textcolor, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
|
@ -111,10 +82,6 @@ void MainWindow::ShowGameView()
|
|||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -122,766 +89,87 @@ void MainWindow::ShowGameView()
|
|||
// 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.
|
||||
ShowWindow(Window, SW_HIDE);
|
||||
|
||||
// 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 (StartWindow != NULL) // Ensure that the network pane is hidden.
|
||||
if (StartWindow) // Ensure that the network pane is hidden.
|
||||
{
|
||||
I_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;
|
||||
PrintStr(text);
|
||||
|
||||
// Append the error message to the log.
|
||||
end.cpMax = end.cpMin = GetWindowTextLength(ConWindow);
|
||||
SendMessage(ConWindow, EM_EXSETSEL, 0, (LPARAM)&end);
|
||||
size_t totalsize = 0;
|
||||
for (const FString& line : bufferedConsoleStuff)
|
||||
totalsize += line.Len();
|
||||
|
||||
// Remember current charformat.
|
||||
oldformat.cbSize = sizeof(oldformat);
|
||||
SendMessage(ConWindow, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&oldformat);
|
||||
std::string alltext;
|
||||
alltext.reserve(totalsize);
|
||||
for (const FString& line : bufferedConsoleStuff)
|
||||
alltext.append(line.GetChars(), line.Len());
|
||||
|
||||
// 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)
|
||||
{
|
||||
PostMessage(hDlg, WM_COMMAND, 1337, 1337);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
ErrorWindow::ExecModal(alltext);
|
||||
}
|
||||
|
||||
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, sizeof(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
|
||||
{
|
||||
// This must be outside the window function so that the exception does not cross DLL boundaries.
|
||||
if (msg.message == WM_COMMAND && msg.wParam == 1337 && msg.lParam == 1337)
|
||||
{
|
||||
throw CExitEvent(0);
|
||||
}
|
||||
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);
|
||||
// To do: pump messages while showing the netstart window and calling timer_callback every 500 ms
|
||||
// To do: return true if timer_callback returns true and stop pumping.
|
||||
return false;
|
||||
}
|
||||
|
||||
void MainWindow::UpdateLayout()
|
||||
bool MainWindow::CheckForRestart()
|
||||
{
|
||||
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);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
bool result = restartrequest;
|
||||
restartrequest = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
// 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 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].GetChars());
|
||||
}
|
||||
bufferedConsoleStuff.Clear();
|
||||
}
|
||||
|
||||
void MainWindow::DoPrintStr(const char* cpt)
|
||||
{
|
||||
wchar_t wbuf[256];
|
||||
int bpos = 0;
|
||||
CHARRANGE selection = {};
|
||||
CHARRANGE endselection = {};
|
||||
LONG lines_before = 0, lines_after = 0;
|
||||
|
||||
// Store the current selection and set it to the end so we can append text.
|
||||
SendMessage(ConWindow, EM_EXGETSEL, 0, (LPARAM)&selection);
|
||||
endselection.cpMax = endselection.cpMin = GetWindowTextLength(ConWindow);
|
||||
SendMessage(ConWindow, EM_EXSETSEL, 0, (LPARAM)&endselection);
|
||||
|
||||
// GetWindowTextLength and EM_EXSETSEL can disagree on where the end of
|
||||
// the text is. Find out what EM_EXSETSEL thought it was and use that later.
|
||||
SendMessage(ConWindow, EM_EXGETSEL, 0, (LPARAM)&endselection);
|
||||
|
||||
// Remember how many lines there were before we added text.
|
||||
lines_before = (LONG)SendMessage(ConWindow, EM_GETLINECOUNT, 0, 0);
|
||||
|
||||
const uint8_t* cptr = (const uint8_t*)cpt;
|
||||
|
||||
auto outputIt = [&]()
|
||||
{
|
||||
wbuf[bpos] = 0;
|
||||
SendMessageW(ConWindow, EM_REPLACESEL, FALSE, (LPARAM)wbuf);
|
||||
bpos = 0;
|
||||
};
|
||||
|
||||
while (int chr = GetCharFromString(cptr))
|
||||
{
|
||||
if ((chr == TEXTCOLOR_ESCAPE && bpos != 0) || bpos == 255)
|
||||
{
|
||||
outputIt();
|
||||
}
|
||||
if (chr != TEXTCOLOR_ESCAPE)
|
||||
{
|
||||
if (chr >= 0x1D && chr <= 0x1F)
|
||||
{ // The bar characters, most commonly used to indicate map changes
|
||||
chr = 0x2550; // Box Drawings Double Horizontal
|
||||
}
|
||||
wbuf[bpos++] = chr;
|
||||
}
|
||||
else
|
||||
{
|
||||
EColorRange range = V_ParseFontColor(cptr, CR_UNTRANSLATED, CR_YELLOW);
|
||||
|
||||
if (range != CR_UNDEFINED)
|
||||
{
|
||||
// Change the color of future text added to the control.
|
||||
PalEntry color = V_LogColorFromColorRange(range);
|
||||
|
||||
// Change the color.
|
||||
CHARFORMAT format;
|
||||
format.cbSize = sizeof(format);
|
||||
format.dwMask = CFM_COLOR;
|
||||
format.dwEffects = 0;
|
||||
format.crTextColor = RGB(color.r, color.g, color.b);
|
||||
SendMessage(ConWindow, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bpos != 0)
|
||||
{
|
||||
outputIt();
|
||||
}
|
||||
|
||||
// If the old selection was at the end of the text, keep it at the end and
|
||||
// scroll. Don't scroll if the selection is anywhere else.
|
||||
if (selection.cpMin == endselection.cpMin && selection.cpMax == endselection.cpMax)
|
||||
{
|
||||
selection.cpMax = selection.cpMin = GetWindowTextLength(ConWindow);
|
||||
lines_after = (LONG)SendMessage(ConWindow, EM_GETLINECOUNT, 0, 0);
|
||||
if (lines_after > lines_before)
|
||||
{
|
||||
SendMessage(ConWindow, EM_LINESCROLL, 0, lines_after - lines_before);
|
||||
}
|
||||
}
|
||||
// Restore the previous selection.
|
||||
SendMessage(ConWindow, EM_EXSETSEL, 0, (LPARAM)&selection);
|
||||
// Give the edit control a chance to redraw itself.
|
||||
I_GetEvent();
|
||||
}
|
||||
|
||||
static DWORD CALLBACK WriteLogFileStreamer(DWORD_PTR cookie, LPBYTE buffer, LONG cb, LONG* pcb)
|
||||
{
|
||||
uint32_t didwrite;
|
||||
LONG p, pp;
|
||||
|
||||
// Replace gray foreground color with black.
|
||||
static const char* badfg = "\\red223\\green223\\blue223;";
|
||||
// 4321098 765432109 876543210
|
||||
// 2 1 0
|
||||
for (p = pp = 0; p < cb; ++p)
|
||||
{
|
||||
if (buffer[p] == badfg[pp])
|
||||
{
|
||||
++pp;
|
||||
if (pp == 25)
|
||||
{
|
||||
buffer[p - 1] = buffer[p - 2] = buffer[p - 3] =
|
||||
buffer[p - 9] = buffer[p - 10] = buffer[p - 11] =
|
||||
buffer[p - 18] = buffer[p - 19] = buffer[p - 20] = '0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto& writeData = *reinterpret_cast<std::function<bool(const void* data, uint32_t size, uint32_t& written)>*>(cookie);
|
||||
if (!writeData((const void*)buffer, cb, didwrite))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
*pcb = didwrite;
|
||||
return 0;
|
||||
bufferedConsoleStuff.Push(cp);
|
||||
}
|
||||
|
||||
void MainWindow::GetLog(std::function<bool(const void* data, uint32_t size, uint32_t& written)> writeData)
|
||||
{
|
||||
FlushBufferedConsoleStuff();
|
||||
|
||||
EDITSTREAM streamer = { (DWORD_PTR)&writeData, 0, WriteLogFileStreamer };
|
||||
SendMessage(ConWindow, EM_STREAMOUT, SF_RTF, (LPARAM)&streamer);
|
||||
for (const FString& line : bufferedConsoleStuff)
|
||||
{
|
||||
size_t pos = 0;
|
||||
size_t len = line.Len();
|
||||
while (pos < len)
|
||||
{
|
||||
uint32_t size = (uint32_t)std::min(len - pos, 0x0fffffffULL);
|
||||
uint32_t written = 0;
|
||||
if (!writeData(&line[pos], size, written))
|
||||
return;
|
||||
pos += written;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// each platform has its own specific version of this function.
|
||||
|
@ -890,7 +178,7 @@ void MainWindow::SetWindowTitle(const char* caption)
|
|||
std::wstring widecaption;
|
||||
if (!caption)
|
||||
{
|
||||
FStringf default_caption("" GAMENAME " %s " X64 " (%s)", GetVersionString(), GetGitTime());
|
||||
FStringf default_caption("" GAMENAME " %s (%s)", GetVersionString(), GetGitTime());
|
||||
widecaption = default_caption.WideString();
|
||||
}
|
||||
else
|
||||
|
|
|
@ -20,20 +20,11 @@ public:
|
|||
void RestoreConView();
|
||||
|
||||
void ShowErrorPane(const char* text);
|
||||
void CheckForRestart();
|
||||
|
||||
void HideGameTitleWindow();
|
||||
int GetGameTitleWindowHeight();
|
||||
bool CheckForRestart();
|
||||
|
||||
void PrintStr(const char* cp);
|
||||
void GetLog(std::function<bool(const void* data, uint32_t size, uint32_t& written)> writeFile);
|
||||
|
||||
void UpdateLayout();
|
||||
|
||||
void ShowProgressBar(int maxpos);
|
||||
void HideProgressBar();
|
||||
void SetProgressPos(int pos);
|
||||
|
||||
void ShowNetStartPane(const char* message, int maxpos);
|
||||
void SetNetStartProgress(int pos);
|
||||
bool RunMessageLoop(bool (*timer_callback)(void*), void* userdata);
|
||||
|
@ -44,47 +35,11 @@ public:
|
|||
HWND GetHandle() { return Window; }
|
||||
|
||||
private:
|
||||
void LayoutMainWindow(HWND hWnd, HWND pane);
|
||||
int LayoutErrorPane(HWND pane, int w);
|
||||
int LayoutNetStartPane(HWND pane, int w);
|
||||
|
||||
void DoPrintStr(const char* cpt);
|
||||
void FlushBufferedConsoleStuff();
|
||||
|
||||
LRESULT OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
LRESULT OnCreate(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
LRESULT OnSize(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
LRESULT OnDrawItem(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
LRESULT OnCommand(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
LRESULT OnClose(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
LRESULT OnDestroy(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
static LRESULT CALLBACK LConProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
static INT_PTR CALLBACK ErrorPaneProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
HWND Window = 0;
|
||||
|
||||
HFONT GameTitleFont = 0;
|
||||
LONG GameTitleFontHeight = 0;
|
||||
LONG DefaultGUIFontHeight = 0;
|
||||
LONG ErrorIconChar = 0;
|
||||
|
||||
bool restartrequest = false;
|
||||
|
||||
HWND GameTitleWindow = 0;
|
||||
HWND ErrorPane = 0;
|
||||
HWND ErrorIcon = 0;
|
||||
|
||||
bool ConWindowHidden = false;
|
||||
HWND ConWindow = 0;
|
||||
TArray<FString> bufferedConsoleStuff;
|
||||
|
||||
HWND ProgressBar = 0;
|
||||
|
||||
HWND NetStartPane = 0;
|
||||
int NetStartMaxPos = 0;
|
||||
|
||||
HWND StartupScreen = 0;
|
||||
};
|
||||
|
||||
extern MainWindow mainwindow;
|
||||
|
|
|
@ -76,9 +76,9 @@ extern HINSTANCE g_hInst;
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
FStartupScreen *FStartupScreen::CreateInstance(int max_progress, bool showprogress)
|
||||
FStartupScreen *FStartupScreen::CreateInstance(int max_progress)
|
||||
{
|
||||
return new FBasicStartupScreen(max_progress, showprogress);
|
||||
return new FBasicStartupScreen(max_progress);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -89,13 +89,9 @@ FStartupScreen *FStartupScreen::CreateInstance(int max_progress, bool showprogre
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
FBasicStartupScreen::FBasicStartupScreen(int max_progress, bool show_bar)
|
||||
FBasicStartupScreen::FBasicStartupScreen(int max_progress)
|
||||
: FStartupScreen(max_progress)
|
||||
{
|
||||
if (show_bar)
|
||||
{
|
||||
mainwindow.ShowProgressBar(MaxPos);
|
||||
}
|
||||
NetMaxPos = 0;
|
||||
NetCurPos = 0;
|
||||
}
|
||||
|
@ -111,8 +107,6 @@ FBasicStartupScreen::FBasicStartupScreen(int max_progress, bool show_bar)
|
|||
|
||||
FBasicStartupScreen::~FBasicStartupScreen()
|
||||
{
|
||||
mainwindow.HideProgressBar();
|
||||
KillTimer(mainwindow.GetHandle(), 1337);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -128,7 +122,6 @@ void FBasicStartupScreen::Progress()
|
|||
if (CurPos < MaxPos)
|
||||
{
|
||||
CurPos++;
|
||||
mainwindow.SetProgressPos(CurPos);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
44
src/common/widgets/errorwindow.cpp
Normal file
44
src/common/widgets/errorwindow.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
|
||||
#include "errorwindow.h"
|
||||
#include "version.h"
|
||||
#include <zwidget/widgets/textedit/textedit.h>
|
||||
|
||||
void ErrorWindow::ExecModal(const std::string& text)
|
||||
{
|
||||
Size screenSize = GetScreenSize();
|
||||
double windowWidth = 1200.0;
|
||||
double windowHeight = 700.0;
|
||||
|
||||
auto window = new ErrorWindow();
|
||||
window->SetText(text);
|
||||
window->SetFrameGeometry((screenSize.width - windowWidth) * 0.5, (screenSize.height - windowHeight) * 0.5, windowWidth, windowHeight);
|
||||
window->Show();
|
||||
|
||||
DisplayWindow::RunLoop();
|
||||
}
|
||||
|
||||
ErrorWindow::ErrorWindow() : Widget(nullptr, WidgetType::Window)
|
||||
{
|
||||
FStringf caption("Fatal Error - " GAMENAME " %s (%s)", GetVersionString(), GetGitTime());
|
||||
SetWindowTitle(caption.GetChars());
|
||||
SetWindowBackground(Colorf::fromRgba8(51, 51, 51));
|
||||
SetWindowBorderColor(Colorf::fromRgba8(51, 51, 51));
|
||||
SetWindowCaptionColor(Colorf::fromRgba8(33, 33, 33));
|
||||
SetWindowCaptionTextColor(Colorf::fromRgba8(226, 223, 219));
|
||||
|
||||
LogView = new TextEdit(this);
|
||||
|
||||
LogView->SetFocus();
|
||||
}
|
||||
|
||||
void ErrorWindow::SetText(const std::string& text)
|
||||
{
|
||||
LogView->SetText(text);
|
||||
}
|
||||
|
||||
void ErrorWindow::OnGeometryChanged()
|
||||
{
|
||||
double w = GetWidth();
|
||||
double h = GetHeight();
|
||||
LogView->SetFrameGeometry(Rect::xywh(20.0, 20.0, w - 40.0, h - 40.0));
|
||||
}
|
21
src/common/widgets/errorwindow.h
Normal file
21
src/common/widgets/errorwindow.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include <zwidget/core/widget.h>
|
||||
|
||||
class TextEdit;
|
||||
|
||||
class ErrorWindow : public Widget
|
||||
{
|
||||
public:
|
||||
static void ExecModal(const std::string& text);
|
||||
|
||||
protected:
|
||||
void OnGeometryChanged() override;
|
||||
|
||||
private:
|
||||
ErrorWindow();
|
||||
|
||||
void SetText(const std::string& text);
|
||||
|
||||
TextEdit* LogView = nullptr;
|
||||
};
|
|
@ -3,7 +3,6 @@
|
|||
#include "v_video.h"
|
||||
#include "version.h"
|
||||
#include "i_interface.h"
|
||||
#include <zwidget/core/resourcedata.h>
|
||||
#include <zwidget/core/image.h>
|
||||
#include <zwidget/window/window.h>
|
||||
#include <zwidget/widgets/textedit/textedit.h>
|
||||
|
@ -160,74 +159,3 @@ void LauncherWindow::OnGeometryChanged()
|
|||
double listViewBottom = y - 10.0;
|
||||
GamesList->SetFrameGeometry(20.0, listViewTop, GetWidth() - 40.0, std::max(listViewBottom - listViewTop, 0.0));
|
||||
}
|
||||
|
||||
|
||||
// To do: clean this up
|
||||
#ifdef WIN32
|
||||
#include <Windows.h>
|
||||
#include <stdexcept>
|
||||
static std::wstring to_utf16(const std::string& str)
|
||||
{
|
||||
if (str.empty()) return {};
|
||||
int needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("MultiByteToWideChar failed");
|
||||
std::wstring result;
|
||||
result.resize(needed);
|
||||
needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &result[0], (int)result.size());
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("MultiByteToWideChar failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::vector<uint8_t> ReadAllBytes(const std::string& filename)
|
||||
{
|
||||
HANDLE handle = CreateFile(to_utf16(filename).c_str(), 0x0001/*FILE_READ_ACCESS*/, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
throw std::runtime_error("Could not open " + filename);
|
||||
|
||||
LARGE_INTEGER fileSize;
|
||||
BOOL result = GetFileSizeEx(handle, &fileSize);
|
||||
if (result == FALSE)
|
||||
{
|
||||
CloseHandle(handle);
|
||||
throw std::runtime_error("GetFileSizeEx failed");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> buffer(fileSize.QuadPart);
|
||||
|
||||
DWORD bytesRead = 0;
|
||||
result = ReadFile(handle, buffer.data(), (DWORD)buffer.size(), &bytesRead, nullptr);
|
||||
if (result == FALSE || bytesRead != buffer.size())
|
||||
{
|
||||
CloseHandle(handle);
|
||||
throw std::runtime_error("ReadFile failed");
|
||||
}
|
||||
|
||||
CloseHandle(handle);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> LoadWidgetFontData(const std::string& name)
|
||||
{
|
||||
return ReadAllBytes("C:\\Windows\\Fonts\\segoeui.ttf");
|
||||
}
|
||||
|
||||
Size LauncherWindow::GetScreenSize()
|
||||
{
|
||||
HDC screenDC = GetDC(0);
|
||||
int screenWidth = GetDeviceCaps(screenDC, HORZRES);
|
||||
int screenHeight = GetDeviceCaps(screenDC, VERTRES);
|
||||
double dpiScale = GetDeviceCaps(screenDC, LOGPIXELSX) / 96.0;
|
||||
ReleaseDC(0, screenDC);
|
||||
|
||||
return Size(screenWidth / dpiScale, screenHeight / dpiScale);
|
||||
}
|
||||
|
||||
#else
|
||||
std::vector<uint8_t> LoadWidgetFontData(const std::string& name)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -34,6 +34,4 @@ private:
|
|||
PushButton* PlayButton = nullptr;
|
||||
PushButton* ExitButton = nullptr;
|
||||
ListView* GamesList = nullptr;
|
||||
|
||||
static Size GetScreenSize();
|
||||
};
|
||||
|
|
12
src/common/widgets/netstartwindow.cpp
Normal file
12
src/common/widgets/netstartwindow.cpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
#include "netstartwindow.h"
|
||||
#include "version.h"
|
||||
|
||||
NetStartWindow::NetStartWindow() : Widget(nullptr, WidgetType::Window)
|
||||
{
|
||||
SetWindowBackground(Colorf::fromRgba8(51, 51, 51));
|
||||
SetWindowBorderColor(Colorf::fromRgba8(51, 51, 51));
|
||||
SetWindowCaptionColor(Colorf::fromRgba8(33, 33, 33));
|
||||
SetWindowCaptionTextColor(Colorf::fromRgba8(226, 223, 219));
|
||||
SetWindowTitle(GAMENAME);
|
||||
}
|
9
src/common/widgets/netstartwindow.h
Normal file
9
src/common/widgets/netstartwindow.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <zwidget/core/widget.h>
|
||||
|
||||
class NetStartWindow : public Widget
|
||||
{
|
||||
public:
|
||||
NetStartWindow();
|
||||
};
|
62
src/common/widgets/widgetresourcedata.cpp
Normal file
62
src/common/widgets/widgetresourcedata.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
|
||||
#include <zwidget/core/resourcedata.h>
|
||||
|
||||
// To do: this should load data from lumps
|
||||
|
||||
#ifdef WIN32
|
||||
#include <Windows.h>
|
||||
#include <stdexcept>
|
||||
static std::wstring to_utf16(const std::string& str)
|
||||
{
|
||||
if (str.empty()) return {};
|
||||
int needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("MultiByteToWideChar failed");
|
||||
std::wstring result;
|
||||
result.resize(needed);
|
||||
needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &result[0], (int)result.size());
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("MultiByteToWideChar failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::vector<uint8_t> ReadAllBytes(const std::string& filename)
|
||||
{
|
||||
HANDLE handle = CreateFile(to_utf16(filename).c_str(), 0x0001/*FILE_READ_ACCESS*/, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
throw std::runtime_error("Could not open " + filename);
|
||||
|
||||
LARGE_INTEGER fileSize;
|
||||
BOOL result = GetFileSizeEx(handle, &fileSize);
|
||||
if (result == FALSE)
|
||||
{
|
||||
CloseHandle(handle);
|
||||
throw std::runtime_error("GetFileSizeEx failed");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> buffer(fileSize.QuadPart);
|
||||
|
||||
DWORD bytesRead = 0;
|
||||
result = ReadFile(handle, buffer.data(), (DWORD)buffer.size(), &bytesRead, nullptr);
|
||||
if (result == FALSE || bytesRead != buffer.size())
|
||||
{
|
||||
CloseHandle(handle);
|
||||
throw std::runtime_error("ReadFile failed");
|
||||
}
|
||||
|
||||
CloseHandle(handle);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> LoadWidgetFontData(const std::string& name)
|
||||
{
|
||||
return ReadAllBytes("C:\\Windows\\Fonts\\segoeui.ttf");
|
||||
}
|
||||
|
||||
#else
|
||||
std::vector<uint8_t> LoadWidgetFontData(const std::string& name)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
#endif
|
|
@ -3284,7 +3284,7 @@ static int D_InitGame(const FIWADInfo* iwad_info, std::vector<std::string>& allw
|
|||
if (!batchrun) Printf ("ST_Init: Init startup screen.\n");
|
||||
if (!restart)
|
||||
{
|
||||
StartWindow = FStartupScreen::CreateInstance (TexMan.GuesstimateNumTextures() + 5, StartScreen == nullptr);
|
||||
StartWindow = FStartupScreen::CreateInstance (TexMan.GuesstimateNumTextures() + 5);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue