diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 62eefe2ec4..db98c71020 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,4 +1,11 @@ -March 16, 2009 +March 17, 2009 +- Added the -norun parameter to quit the game just before video + initialization. To be used to check for errors in scripts without actually + running the game. +- Added the -stdout parameter to the Windows version to send all output to + a console, like the Linux version has done all along. + +March 16, 2009 - Added support for loading ZGL2 nodes. (Only useful with UDMF and maps with more than 65534 lines.) diff --git a/src/d_main.cpp b/src/d_main.cpp index d7cface4b9..78166e3f47 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -2637,6 +2637,12 @@ void D_DoomMain (void) delete StartScreen; StartScreen = NULL; + + if (Args->CheckParm("-norun")) + { + throw CNoRunExit(); + } + V_Init2(); files = Args->GatherFiles ("-playdemo", ".lmp", false); diff --git a/src/doomerrors.h b/src/doomerrors.h index dc77250aae..d0181c27bd 100644 --- a/src/doomerrors.h +++ b/src/doomerrors.h @@ -68,6 +68,10 @@ private: char m_Message[MAX_ERRORTEXT]; }; +class CNoRunExit : public CDoomError +{ +}; + class CRecoverableError : public CDoomError { public: diff --git a/src/win32/i_main.cpp b/src/win32/i_main.cpp index 2203ed2ba3..e908c43cf5 100644 --- a/src/win32/i_main.cpp +++ b/src/win32/i_main.cpp @@ -125,6 +125,8 @@ HINSTANCE g_hInst; DWORD SessionID; HANDLE MainThread; DWORD MainThreadID; +HANDLE StdOut; +bool FancyStdOut, AttachedStdOut; // The main window HWND Window; @@ -806,6 +808,49 @@ void DoMain (HINSTANCE hInstance) // need to extract the ProcessIdToSessionId function from kernel32.dll manually. HMODULE kernel = GetModuleHandle ("kernel32.dll"); + if (Args->CheckParm("-stdout")) + { + // As a GUI application, we don't normally get a console when we start. + // If we were run from the shell and are on XP+, we can attach to its + // console. Otherwise, we can create a new one. If we already have a + // stdout handle, then we have been redirected and should just use that + // handle instead of creating a console window. + + StdOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (StdOut != NULL) + { + // It seems that running from a shell always creates a std output + // for us, even if it doesn't go anywhere. (Running from Explorer + // does not.) If we can get file information for this handle, it's + // a file or pipe, so use it. Otherwise, pretend it wasn't there + // and find a console to use instead. + BY_HANDLE_FILE_INFORMATION info; + if (!GetFileInformationByHandle(StdOut, &info)) + { + StdOut = NULL; + } + } + if (StdOut == NULL) + { + // AttachConsole was introduced with Windows XP. (OTOH, since we + // have to share the console with the shell, I'm not sure if it's + // a good idea to actually attach to it.) + typedef BOOL (WINAPI *ac)(DWORD); + ac attach_console = kernel != NULL ? (ac)GetProcAddress(kernel, "AttachConsole") : NULL; + if (attach_console != NULL && attach_console(ATTACH_PARENT_PROCESS)) + { + StdOut = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD foo; WriteFile(StdOut, "\n", 1, &foo, NULL); + AttachedStdOut = true; + } + if (StdOut == NULL && AllocConsole()) + { + StdOut = GetStdHandle(STD_OUTPUT_HANDLE); + } + FancyStdOut = true; + } + } + // Set the timer to be as accurate as possible if (timeGetDevCaps (&tc, sizeof(tc)) != TIMERR_NOERROR) TimerPeriod = 1; // Assume minimum resolution of 1 ms @@ -906,7 +951,7 @@ void DoMain (HINSTANCE hInstance) if (!Window) I_FatalError ("Could not open window"); - if (kernel != 0) + if (kernel != NULL) { typedef BOOL (WINAPI *pts)(DWORD, DWORD *); pts pidsid = (pts)GetProcAddress (kernel, "ProcessIdToSessionId"); @@ -946,6 +991,36 @@ void DoMain (HINSTANCE hInstance) I_DetectOS (); D_DoomMain (); } + catch (class CNoRunExit &) + { + I_ShutdownGraphics(); + if (FancyStdOut && !AttachedStdOut) + { // Outputting to a new console window: Wait for a keypress before quitting. + DWORD bytes; + HANDLE stdinput = GetStdHandle(STD_INPUT_HANDLE); + + ShowWindow (Window, SW_HIDE); + WriteFile(StdOut, "Press any key to exit...", 24, &bytes, NULL); + FlushConsoleInputBuffer(stdinput); + SetConsoleMode(stdinput, 0); + ReadConsole(stdinput, &bytes, 1, &bytes, NULL); + } + else if (StdOut == NULL) + { + BOOL bRet; + MSG msg; + + RestoreConView(); + while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) + { + if (bRet == -1) + { + exit(0); + } + } + } + exit(0); + } catch (class CDoomError &error) { I_ShutdownGraphics (); diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index 94faa24a7a..7972bf2e27 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -66,7 +66,7 @@ #include "v_font.h" #include "g_level.h" #include "doomstat.h" - +#include "v_palette.h" #include "stats.h" EXTERN_CVAR (String, language) @@ -74,6 +74,8 @@ EXTERN_CVAR (String, language) extern void CheckCPUID(CPUInfo *cpu); extern HWND Window, ConWindow, GameTitleWindow; +extern HANDLE StdOut; +extern bool FancyStdOut; extern HINSTANCE g_hInst; double PerfToSec, PerfToMillisec; @@ -554,10 +556,9 @@ void I_SetIWADInfo (const IWADInfo *info) void I_PrintStr (const char *cp) { - if (ConWindow == NULL) + if (ConWindow == NULL && StdOut == NULL) return; - static bool newLine = true; HWND edit = ConWindow; char buf[256]; int bpos = 0; @@ -566,17 +567,20 @@ void I_PrintStr (const char *cp) LONG lines_before, lines_after; CHARFORMAT format; - // 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); + 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); + // 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 = SendMessage (edit, EM_GETLINECOUNT, 0, 0); + // Remember how many lines there were before we added text. + lines_before = SendMessage (edit, EM_GETLINECOUNT, 0, 0); + } while (*cp != 0) { @@ -584,8 +588,15 @@ void I_PrintStr (const char *cp) if ((*cp == 28 && bpos != 0) || bpos == 255) { buf[bpos] = 0; - SendMessage (edit, EM_REPLACESEL, FALSE, (LPARAM)buf); - newLine = buf[bpos-1] == '\n'; + if (edit != NULL) + { + SendMessage (edit, EM_REPLACESEL, FALSE, (LPARAM)buf); + } + if (StdOut != NULL) + { + DWORD bytes_written; + WriteFile(StdOut, buf, bpos, &bytes_written, NULL); + } bpos = 0; } if (*cp != 28) @@ -602,39 +613,81 @@ void I_PrintStr (const char *cp) { // Change the color of future text added to the control. PalEntry color = V_LogColorFromColorRange (range); - // GDI uses BGR colors, but color is RGB, so swap the R and the B. - 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); + if (StdOut != NULL && FancyStdOut) + { + // Unfortunately, we are pretty limited here: There are only + // eight basic colors, and each comes in a dark and a bright + // variety. + float h, s, v, r, g, b; + WORD attrib = 0; + + RGBtoHSV(color.r / 255.0, color.g / 255.0, color.b / 255.0, &h, &s, &v); + if (s != 0) + { // color + HSVtoRGB(&r, &g, &b, h, 1, 1); + if (r == 1) attrib = FOREGROUND_RED; + if (g == 1) attrib |= FOREGROUND_GREEN; + if (b == 1) attrib |= FOREGROUND_BLUE; + if (v > 0.6) attrib |= FOREGROUND_INTENSITY; + } + else + { // gray + if (v < 0.33) attrib = FOREGROUND_INTENSITY; + else if (v < 0.90) attrib = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + else attrib = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; + } + SetConsoleTextAttribute(StdOut, attrib); + } + if (edit != NULL) + { + // GDI uses BGR colors, but color is RGB, so swap the R and the B. + 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); + } } } } if (bpos != 0) { buf[bpos] = 0; - SendMessage (edit, EM_REPLACESEL, FALSE, (LPARAM)buf); - newLine = buf[bpos-1] == '\n'; - } - - // 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 = SendMessage (edit, EM_GETLINECOUNT, 0, 0); - if (lines_after > lines_before) + if (edit != NULL) { - SendMessage (edit, EM_LINESCROLL, 0, lines_after - lines_before); + SendMessage (edit, EM_REPLACESEL, FALSE, (LPARAM)buf); + } + if (StdOut != NULL) + { + DWORD bytes_written; + WriteFile(StdOut, buf, bpos, &bytes_written, NULL); } } - // Restore the previous selection. - SendMessage (edit, EM_EXSETSEL, 0, (LPARAM)&selection); - // Give the edit control a chance to redraw itself. - I_GetEvent (); + + 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 = 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); + } } EXTERN_CVAR (Bool, queryiwad);