Cool console! (#477)
* Cool console! * Debug-specific colors * Fix release build * Clear text "as we go" * dpi awareness :> * Address diamante'"s formatting review * Addressed review, added font license Co-authored-by: rackover <roxanne@thegamebakers.com> Co-authored-by: Louvenarde <louve@louve.systems>
This commit is contained in:
@ -1,5 +1,7 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
#define REMOVE_HEADERBAR 1
|
||||
|
||||
namespace Components
|
||||
{
|
||||
WINDOW* Console::OutputWindow;
|
||||
@ -20,6 +22,24 @@ namespace Components
|
||||
bool Console::HasConsole = false;
|
||||
bool Console::SkipShutdown = false;
|
||||
|
||||
COLORREF Console::TextColor =
|
||||
#if DEBUG
|
||||
RGB(255, 200, 117);
|
||||
#else
|
||||
RGB(120, 237, 122);
|
||||
#endif
|
||||
|
||||
COLORREF Console::BackgroundColor =
|
||||
#if DEBUG
|
||||
RGB(35, 21, 0);
|
||||
#else
|
||||
RGB(25, 32, 25);
|
||||
#endif
|
||||
HBRUSH Console::ForegroundBrush = CreateSolidBrush(TextColor);
|
||||
HBRUSH Console::BackgroundBrush = CreateSolidBrush(BackgroundColor);
|
||||
|
||||
HANDLE Console::CustomConsoleFont;
|
||||
|
||||
std::thread Console::ConsoleThread;
|
||||
|
||||
Game::SafeArea Console::OriginalSafeArea;
|
||||
@ -110,6 +130,44 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
float Console::GetDpiScale(const HWND hWnd)
|
||||
{
|
||||
const auto user32 = Utils::Library("user32.dll");
|
||||
const auto getDpiForWindow = user32.getProc<UINT(WINAPI*)(HWND)>("GetDpiForWindow");
|
||||
const auto getDpiForMonitor = user32.getProc<HRESULT(WINAPI*)(HMONITOR, int, UINT*, UINT*)>("GetDpiForMonitor");
|
||||
|
||||
int dpi;
|
||||
|
||||
if (getDpiForWindow)
|
||||
{
|
||||
dpi = getDpiForWindow(hWnd);
|
||||
}
|
||||
else if (getDpiForMonitor)
|
||||
{
|
||||
HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
|
||||
UINT xdpi, ydpi;
|
||||
LRESULT success = getDpiForMonitor(hMonitor, 0, &xdpi, &ydpi);
|
||||
if (success == S_OK)
|
||||
{
|
||||
dpi = static_cast<int>(ydpi);
|
||||
}
|
||||
|
||||
dpi = 96;
|
||||
}
|
||||
else
|
||||
{
|
||||
HDC hDC = GetDC(hWnd);
|
||||
INT ydpi = GetDeviceCaps(hDC, LOGPIXELSY);
|
||||
ReleaseDC(NULL, hDC);
|
||||
|
||||
dpi = ydpi;
|
||||
}
|
||||
|
||||
constexpr auto unawareDpi = 96.0f;
|
||||
return dpi / unawareDpi;
|
||||
}
|
||||
|
||||
|
||||
const char* Console::Input()
|
||||
{
|
||||
if (!Console::HasConsole)
|
||||
@ -374,6 +432,261 @@ namespace Components
|
||||
Console::RefreshOutput();
|
||||
}
|
||||
|
||||
HFONT __stdcall Console::ReplaceFont(
|
||||
[[maybe_unused]] int cHeight,
|
||||
int cWidth,
|
||||
int cEscapement,
|
||||
int cOrientation,
|
||||
[[maybe_unused]] int cWeight,
|
||||
DWORD bItalic,
|
||||
DWORD bUnderline,
|
||||
DWORD bStrikeOut,
|
||||
DWORD iCharSet,
|
||||
[[maybe_unused]] DWORD iOutPrecision,
|
||||
DWORD iClipPrecision,
|
||||
[[maybe_unused]] DWORD iQuality,
|
||||
[[maybe_unused]] DWORD iPitchAndFamily,
|
||||
[[maybe_unused]] LPCSTR pszFaceName)
|
||||
{
|
||||
auto font = CreateFontA(
|
||||
12,
|
||||
cWidth,
|
||||
cEscapement,
|
||||
cOrientation,
|
||||
700,
|
||||
bItalic,
|
||||
bUnderline,
|
||||
bStrikeOut,
|
||||
iCharSet,
|
||||
OUT_RASTER_PRECIS,
|
||||
iClipPrecision,
|
||||
NONANTIALIASED_QUALITY,
|
||||
0x31,
|
||||
"Terminus (TTF)"); // Terminus (TTF)
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
void Console::GetWindowPos(HWND hWnd, int* x, int* y)
|
||||
{
|
||||
HWND hWndParent = GetParent(hWnd);
|
||||
POINT p = { 0 };
|
||||
|
||||
MapWindowPoints(hWnd, hWndParent, &p, 1);
|
||||
|
||||
(*x) = p.x;
|
||||
(*y) = p.y;
|
||||
}
|
||||
|
||||
BOOL CALLBACK Console::ResizeChildWindow(HWND hwndChild, LPARAM lParam)
|
||||
{
|
||||
auto id = GetWindowLong(hwndChild, GWL_ID);
|
||||
bool isInputBox = id == INPUT_BOX;
|
||||
bool isOutputBox = id == OUTPUT_BOX;
|
||||
|
||||
if (isInputBox || isOutputBox)
|
||||
{
|
||||
RECT newParentRect = *reinterpret_cast<LPRECT>(lParam);
|
||||
|
||||
RECT childRect;
|
||||
|
||||
if (GetWindowRect(hwndChild, &childRect))
|
||||
{
|
||||
|
||||
int childX, childY;
|
||||
|
||||
GetWindowPos(hwndChild, &childX, &childY);
|
||||
|
||||
HWND parent = Utils::Hook::Get<HWND>(0x64A3288);
|
||||
|
||||
float scale = GetDpiScale(parent);
|
||||
|
||||
if (isInputBox)
|
||||
{
|
||||
|
||||
int newX = childX; // No change!
|
||||
int newY = static_cast<int>((newParentRect.bottom - newParentRect.top) - 65 * scale);
|
||||
int newWidth = static_cast<int>((newParentRect.right - newParentRect.left) - 29 * scale);
|
||||
int newHeight = static_cast<int>((childRect.bottom - childRect.top) * scale); // No change!
|
||||
|
||||
MoveWindow(hwndChild, newX, newY, newWidth, newHeight, TRUE);
|
||||
}
|
||||
|
||||
if (isOutputBox)
|
||||
{
|
||||
int newX = childX; // No change!
|
||||
int newY = childY; // No change!
|
||||
int newWidth = static_cast<int>((newParentRect.right - newParentRect.left) - 29);
|
||||
|
||||
int margin = 70;
|
||||
|
||||
#ifdef REMOVE_HEADERBAR
|
||||
margin = 10;
|
||||
#endif
|
||||
int newHeight = static_cast<int>((newParentRect.bottom - newParentRect.top) - 74 * scale - margin);
|
||||
|
||||
MoveWindow(hwndChild, newX, newY, newWidth, newHeight, TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Instead of clearing fully the console text whenever the 0x400's character is written, we
|
||||
// clear it progressively when we run out of room by truncating the top line by line.
|
||||
// A bit of trickery with SETREDRAW is required to avoid having the outputbox jump
|
||||
// around whenever clearing occurs.
|
||||
void Console::MakeRoomForText([[maybe_unused]] int addedCharacters)
|
||||
{
|
||||
constexpr unsigned int maxChars = 0x4000;
|
||||
constexpr unsigned int maxAffectedChars = 0x100;
|
||||
HWND outputBox = Utils::Hook::Get<HWND>(0x64A328C);
|
||||
|
||||
unsigned int totalChars;
|
||||
unsigned int totalClearLength = 0;
|
||||
|
||||
char str[maxAffectedChars];
|
||||
unsigned int fetchedCharacters = static_cast<unsigned int>(GetWindowText(outputBox, str, maxAffectedChars));
|
||||
|
||||
totalChars = GetWindowTextLengthA(outputBox);
|
||||
|
||||
while (totalChars - totalClearLength > maxChars)
|
||||
{
|
||||
unsigned int clearLength = maxAffectedChars; // Default to full clear
|
||||
|
||||
for (size_t i = 0; i < fetchedCharacters; i++)
|
||||
{
|
||||
if (str[i] == '\n')
|
||||
{
|
||||
// Shorter clear if I meet a linebreak
|
||||
clearLength = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
totalClearLength += clearLength;
|
||||
}
|
||||
|
||||
if (totalClearLength > 0)
|
||||
{
|
||||
SendMessage(outputBox, WM_SETREDRAW, FALSE, 0);
|
||||
SendMessage(outputBox, EM_SETSEL, 0, totalClearLength);
|
||||
SendMessage(outputBox, EM_REPLACESEL, FALSE, 0);
|
||||
SendMessage(outputBox, WM_SETREDRAW, TRUE, 0);
|
||||
}
|
||||
|
||||
Utils::Hook::Set(0x64A38B8, totalChars - totalClearLength);
|
||||
}
|
||||
|
||||
void __declspec(naked) Console::Sys_PrintStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
pushad
|
||||
push edi
|
||||
call MakeRoomForText
|
||||
pop edi
|
||||
popad
|
||||
|
||||
// Go back to AppendText
|
||||
push 0x4F57F8
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CALLBACK Console::ConWndProc(HWND hWnd, UINT Msg, WPARAM wParam, unsigned int lParam)
|
||||
{
|
||||
switch (Msg)
|
||||
{
|
||||
|
||||
|
||||
case WM_CREATE:
|
||||
{
|
||||
BOOL darkMode = true;
|
||||
|
||||
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
|
||||
if (SUCCEEDED(DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, reinterpret_cast<LPCVOID>(&darkMode), sizeof(darkMode))))
|
||||
{
|
||||
// cool !
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_CTLCOLORSTATIC:
|
||||
case WM_CTLCOLOREDIT:
|
||||
{
|
||||
SetBkColor(reinterpret_cast<HDC>(wParam), BackgroundColor);
|
||||
SetTextColor(reinterpret_cast<HDC>(wParam), TextColor);
|
||||
return reinterpret_cast<LRESULT>(BackgroundBrush);
|
||||
}
|
||||
|
||||
case WM_SIZE:
|
||||
RECT rect;
|
||||
|
||||
if (GetWindowRect(hWnd, &rect))
|
||||
{
|
||||
EnumChildWindows(hWnd, ResizeChildWindow, reinterpret_cast<LPARAM>(&rect));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Fall through to basegame
|
||||
return Utils::Hook::Call<LRESULT CALLBACK(HWND, UINT, WPARAM, unsigned int)>(0x64DC50)(hWnd, Msg, wParam, lParam);
|
||||
}
|
||||
|
||||
ATOM CALLBACK Console::RegisterClassHook(WNDCLASSA* lpWndClass)
|
||||
{
|
||||
DeleteObject(lpWndClass->hbrBackground);
|
||||
HBRUSH brush = CreateSolidBrush(BackgroundColor);
|
||||
lpWndClass->hbrBackground = brush;
|
||||
|
||||
return RegisterClassA(lpWndClass);
|
||||
}
|
||||
|
||||
void Console::ApplyConsoleStyle()
|
||||
{
|
||||
Utils::Hook::Set<BYTE>(0x428A8E, 0); // Adjust logo Y pos
|
||||
Utils::Hook::Set<BYTE>(0x428A90, 0); // Adjust logo X pos
|
||||
Utils::Hook::Set<BYTE>(0x428AF2, 67); // Adjust output Y pos
|
||||
Utils::Hook::Set<DWORD>(0x428AC5, 397); // Adjust input Y pos
|
||||
Utils::Hook::Set<DWORD>(0x428951, 609); // Reduce window width
|
||||
Utils::Hook::Set<DWORD>(0x42895D, 423); // Reduce window height
|
||||
Utils::Hook::Set<DWORD>(0x428AC0, 597); // Reduce input width
|
||||
Utils::Hook::Set<DWORD>(0x428AED, 596); // Reduce output width
|
||||
|
||||
DWORD fontsInstalled;
|
||||
CustomConsoleFont = AddFontMemResourceEx(const_cast<void*>(reinterpret_cast<const void*>(Font::Terminus::DATA)), Font::Terminus::LENGTH, 0, &fontsInstalled);
|
||||
|
||||
if (fontsInstalled > 0)
|
||||
{
|
||||
Utils::Hook::Nop(0x428A44, 6);
|
||||
Utils::Hook(0x428A44, ReplaceFont, HOOK_CALL).install()->quick();
|
||||
}
|
||||
|
||||
Utils::Hook::Nop(0x42892D, 6);
|
||||
Utils::Hook(0x42892D, RegisterClassHook, HOOK_CALL).install()->quick();
|
||||
|
||||
Utils::Hook::Set(0x4288E6 + 4, &ConWndProc);
|
||||
|
||||
auto style = WS_CAPTION | WS_SIZEBOX | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
|
||||
Utils::Hook::Set(0x42893F + 1, style);
|
||||
Utils::Hook::Set(0x4289E2 + 1, style);
|
||||
|
||||
#ifdef REMOVE_HEADERBAR
|
||||
// Remove that hideous header window -rox
|
||||
Utils::Hook::Set(0x428A7C, static_cast<char>(0xEB));
|
||||
Utils::Hook::Set(0X428AF1 + 1, static_cast<char>(10));
|
||||
#endif
|
||||
|
||||
// Never reset text
|
||||
Utils::Hook::Nop(0x4F57DF, 0x4F57F6 - 0x4F57DF);
|
||||
Utils::Hook(0x4F57DF, Console::Sys_PrintStub, HOOK_JUMP).install()->quick();
|
||||
|
||||
}
|
||||
|
||||
void Console::ConsoleRunner()
|
||||
{
|
||||
Console::SkipShutdown = false;
|
||||
@ -588,14 +901,7 @@ namespace Components
|
||||
Utils::Hook(0x482AC3, Console::RegisterConColor, HOOK_CALL).install()->quick();
|
||||
|
||||
// Modify console style
|
||||
Utils::Hook::Set<BYTE>(0x428A8E, 0); // Adjust logo Y pos
|
||||
Utils::Hook::Set<BYTE>(0x428A90, 0); // Adjust logo X pos
|
||||
Utils::Hook::Set<BYTE>(0x428AF2, 67); // Adjust output Y pos
|
||||
Utils::Hook::Set<DWORD>(0x428AC5, 397); // Adjust input Y pos
|
||||
Utils::Hook::Set<DWORD>(0x428951, 609); // Reduce window width
|
||||
Utils::Hook::Set<DWORD>(0x42895D, 423); // Reduce window height
|
||||
Utils::Hook::Set<DWORD>(0x428AC0, 597); // Reduce input width
|
||||
Utils::Hook::Set<DWORD>(0x428AED, 596); // Reduce output width
|
||||
ApplyConsoleStyle();
|
||||
|
||||
// Don't resize the console
|
||||
Utils::Hook(0x64DC6B, 0x64DCC2, HOOK_JUMP).install()->quick();
|
||||
|
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Terminus_4.49.1.ttf.hpp"
|
||||
|
||||
#define OUTPUT_HEIGHT 250
|
||||
#define OUTPUT_MAX_TOP (OUTPUT_HEIGHT - (Console::Height - 2))
|
||||
|
||||
@ -20,6 +22,10 @@ namespace Components
|
||||
static void ShowAsyncConsole();
|
||||
|
||||
private:
|
||||
|
||||
static constexpr int OUTPUT_BOX = 0x64;
|
||||
static constexpr int INPUT_BOX = 0x65;
|
||||
|
||||
// Text-based console stuff
|
||||
static WINDOW* OutputWindow;
|
||||
static WINDOW* InputWindow;
|
||||
@ -32,6 +38,13 @@ namespace Components
|
||||
static int OutBuffer;
|
||||
static int LastRefresh;
|
||||
|
||||
static COLORREF TextColor;
|
||||
static COLORREF BackgroundColor;
|
||||
static HBRUSH ForegroundBrush;
|
||||
static HBRUSH BackgroundBrush;
|
||||
|
||||
static HANDLE CustomConsoleFont;
|
||||
|
||||
static char LineBuffer[1024];
|
||||
static char LineBuffer2[1024];
|
||||
static int LineBufferIndex;
|
||||
@ -69,5 +82,29 @@ namespace Components
|
||||
static void AddConsoleCommand();
|
||||
|
||||
static Game::dvar_t* RegisterConColor(const char* dvarName, float r, float g, float b, float a, float min, float max, unsigned __int16 flags, const char* description);
|
||||
|
||||
static LRESULT CALLBACK ConWndProc(HWND hWnd, UINT Msg, WPARAM wParam, unsigned int lParam);
|
||||
static ATOM CALLBACK RegisterClassHook(WNDCLASSA* lpWndClass);
|
||||
static BOOL CALLBACK ResizeChildWindow(HWND hwndChild, LPARAM lParam);
|
||||
static HFONT CALLBACK ReplaceFont(
|
||||
int cHeight,
|
||||
int cWidth,
|
||||
int cEscapement,
|
||||
int cOrientation,
|
||||
int cWeight,
|
||||
DWORD bItalic,
|
||||
DWORD bUnderline,
|
||||
DWORD bStrikeOut,
|
||||
DWORD iCharSet,
|
||||
DWORD iOutPrecision,
|
||||
DWORD iClipPrecision,
|
||||
DWORD iQuality,
|
||||
DWORD iPitchAndFamily,
|
||||
LPCSTR pszFaceName);
|
||||
static void ApplyConsoleStyle();
|
||||
static void GetWindowPos(HWND hWnd, int* x, int* y);
|
||||
static void Sys_PrintStub();
|
||||
static void MakeRoomForText(int addedCharacters);
|
||||
static float GetDpiScale(const HWND hWnd);
|
||||
};
|
||||
}
|
||||
|
Reference in New Issue
Block a user