237 lines
8.8 KiB
C++
237 lines
8.8 KiB
C++
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
|
|
#include "precomp.h"
|
|
|
|
#include "utils.hpp"
|
|
|
|
#include "../interactivity/inc/ServiceLocator.hpp"
|
|
|
|
#include "srvinit.h"
|
|
|
|
using Microsoft::Console::Interactivity::ServiceLocator;
|
|
|
|
short CalcWindowSizeX(const SMALL_RECT& rect) noexcept
|
|
{
|
|
return rect.Right - rect.Left + 1;
|
|
}
|
|
|
|
short CalcWindowSizeY(const SMALL_RECT& rect) noexcept
|
|
{
|
|
return rect.Bottom - rect.Top + 1;
|
|
}
|
|
|
|
short CalcCursorYOffsetInPixels(const short sFontSizeY, const ULONG ulSize) noexcept
|
|
{
|
|
// TODO: MSFT 10229700 - Note, we want to likely enforce that this isn't negative.
|
|
// Pretty sure there's not a valid case for negative offsets here.
|
|
return (short)((sFontSizeY) - (ulSize));
|
|
}
|
|
|
|
WORD ConvertStringToDec(_In_ PCWSTR pwchToConvert, _Out_opt_ PCWSTR* const ppwchEnd) noexcept
|
|
{
|
|
WORD val = 0;
|
|
|
|
while (*pwchToConvert != L'\0')
|
|
{
|
|
WCHAR ch = *pwchToConvert;
|
|
if (L'0' <= ch && ch <= L'9')
|
|
{
|
|
val = (val * 10) + (ch - L'0');
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
pwchToConvert++;
|
|
}
|
|
|
|
if (nullptr != ppwchEnd)
|
|
{
|
|
*ppwchEnd = pwchToConvert;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
// Routine Description:
|
|
// - Retrieves string resources from our resource files.
|
|
// Arguments:
|
|
// - id - Resource id from resource.h to the string we need to load.
|
|
// Return Value:
|
|
// - The string resource
|
|
std::wstring _LoadString(const UINT id)
|
|
{
|
|
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
|
WCHAR ItemString[70];
|
|
size_t ItemLength = 0;
|
|
LANGID LangId;
|
|
|
|
const NTSTATUS Status = GetConsoleLangId(gci.OutputCP, &LangId);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
ItemLength = s_LoadStringEx(ServiceLocator::LocateGlobals().hInstance, id, ItemString, ARRAYSIZE(ItemString), LangId);
|
|
}
|
|
if (!NT_SUCCESS(Status) || ItemLength == 0)
|
|
{
|
|
ItemLength = LoadStringW(ServiceLocator::LocateGlobals().hInstance, id, ItemString, ARRAYSIZE(ItemString));
|
|
}
|
|
|
|
return std::wstring(ItemString, ItemLength);
|
|
}
|
|
|
|
// Routine Description:
|
|
// - Helper to retrieve string resources from a MUI with a particular LANGID.
|
|
// Arguments:
|
|
// - hModule - The module related to loading the resource
|
|
// - wID - The resource ID number
|
|
// - lpBuffer - Buffer to place string data when read.
|
|
// - cchBufferMax - Size of buffer
|
|
// - wLangId - Language ID of resources that we should retrieve.
|
|
UINT s_LoadStringEx(_In_ HINSTANCE hModule, _In_ UINT wID, _Out_writes_(cchBufferMax) LPWSTR lpBuffer, _In_ UINT cchBufferMax, _In_ WORD wLangId)
|
|
{
|
|
// Make sure the parms are valid.
|
|
if (lpBuffer == nullptr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
UINT cch = 0;
|
|
|
|
// String Tables are broken up into 16 string segments. Find the segment containing the string we are interested in.
|
|
HANDLE const hResInfo = FindResourceEx(hModule, RT_STRING, (LPTSTR)((LONG_PTR)(((USHORT)wID >> 4) + 1)), wLangId);
|
|
if (hResInfo != nullptr)
|
|
{
|
|
// Load that segment.
|
|
HANDLE const hStringSeg = (HRSRC)LoadResource(hModule, (HRSRC)hResInfo);
|
|
|
|
// Lock the resource.
|
|
LPTSTR lpsz;
|
|
if (hStringSeg != nullptr && (lpsz = (LPTSTR)LockResource(hStringSeg)) != nullptr)
|
|
{
|
|
// Move past the other strings in this segment. (16 strings in a segment -> & 0x0F)
|
|
wID &= 0x0F;
|
|
for (;;)
|
|
{
|
|
// PASCAL like string count
|
|
// first WCHAR is count of WCHARs
|
|
cch = *((WCHAR*)lpsz++);
|
|
if (wID-- == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
lpsz += cch; // Step to start if next string
|
|
}
|
|
|
|
// chhBufferMax == 0 means return a pointer to the read-only resource buffer.
|
|
if (cchBufferMax == 0)
|
|
{
|
|
*(LPTSTR*)lpBuffer = lpsz;
|
|
}
|
|
else
|
|
{
|
|
// Account for the nullptr
|
|
cchBufferMax--;
|
|
|
|
// Don't copy more than the max allowed.
|
|
if (cch > cchBufferMax)
|
|
cch = cchBufferMax;
|
|
|
|
// Copy the string into the buffer.
|
|
memmove(lpBuffer, lpsz, cch * sizeof(WCHAR));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Append a nullptr.
|
|
if (cchBufferMax != 0)
|
|
{
|
|
lpBuffer[cch] = 0;
|
|
}
|
|
|
|
return cch;
|
|
}
|
|
|
|
// Routine Description:
|
|
// - Compares two coordinate positions to determine whether they're the same, left, or right within the given buffer size
|
|
// Arguments:
|
|
// - bufferSize - The size of the buffer to use for measurements.
|
|
// - coordFirst - The first coordinate position
|
|
// - coordSecond - The second coordinate position
|
|
// Return Value:
|
|
// - Negative if First is to the left of the Second.
|
|
// - 0 if First and Second are the same coordinate.
|
|
// - Positive if First is to the right of the Second.
|
|
// - This is so you can do s_CompareCoords(first, second) <= 0 for "first is left or the same as second".
|
|
// (the < looks like a left arrow :D)
|
|
// - The magnitude of the result is the distance between the two coordinates when typing characters into the buffer (left to right, top to bottom)
|
|
int Utils::s_CompareCoords(const COORD bufferSize, const COORD coordFirst, const COORD coordSecond) noexcept
|
|
{
|
|
const short cRowWidth = bufferSize.X;
|
|
|
|
// Assert that our coordinates are within the expected boundaries
|
|
const short cRowHeight = bufferSize.Y;
|
|
FAIL_FAST_IF(!(coordFirst.X >= 0 && coordFirst.X < cRowWidth));
|
|
FAIL_FAST_IF(!(coordSecond.X >= 0 && coordSecond.X < cRowWidth));
|
|
FAIL_FAST_IF(!(coordFirst.Y >= 0 && coordFirst.Y < cRowHeight));
|
|
FAIL_FAST_IF(!(coordSecond.Y >= 0 && coordSecond.Y < cRowHeight));
|
|
|
|
// First set the distance vertically
|
|
// If first is on row 4 and second is on row 6, first will be -2 rows behind second * an 80 character row would be -160.
|
|
// For the same row, it'll be 0 rows * 80 character width = 0 difference.
|
|
int retVal = (coordFirst.Y - coordSecond.Y) * cRowWidth;
|
|
|
|
// Now adjust for horizontal differences
|
|
// If first is in position 15 and second is in position 30, first is -15 left in relation to 30.
|
|
retVal += (coordFirst.X - coordSecond.X);
|
|
|
|
// Further notes:
|
|
// If we already moved behind one row, this will help correct for when first is right of second.
|
|
// For example, with row 4, col 79 and row 5, col 0 as first and second respectively, the distance is -1.
|
|
// Assume the row width is 80.
|
|
// Step one will set the retVal as -80 as first is one row behind the second.
|
|
// Step two will then see that first is 79 - 0 = +79 right of second and add 79
|
|
// The total is -80 + 79 = -1.
|
|
return retVal;
|
|
}
|
|
|
|
// Routine Description:
|
|
// - Compares two coordinate positions to determine whether they're the same, left, or right
|
|
// Arguments:
|
|
// - coordFirst - The first coordinate position
|
|
// - coordSecond - The second coordinate position
|
|
// Return Value:
|
|
// - Negative if First is to the left of the Second.
|
|
// - 0 if First and Second are the same coordinate.
|
|
// - Positive if First is to the right of the Second.
|
|
// - This is so you can do s_CompareCoords(first, second) <= 0 for "first is left or the same as second".
|
|
// (the < looks like a left arrow :D)
|
|
// - The magnitude of the result is the distance between the two coordinates when typing characters into the buffer (left to right, top to bottom)
|
|
int Utils::s_CompareCoords(const COORD coordFirst, const COORD coordSecond) noexcept
|
|
{
|
|
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
|
// find the width of one row
|
|
const COORD coordScreenBufferSize = gci.GetActiveOutputBuffer().GetBufferSize().Dimensions();
|
|
return s_CompareCoords(coordScreenBufferSize, coordFirst, coordSecond);
|
|
}
|
|
|
|
// Routine Description:
|
|
// - Finds the opposite corner given a rectangle and one of its corners.
|
|
// - For example, finds the bottom right corner given a rectangle and its top left corner.
|
|
// Arguments:
|
|
// - srRectangle - The rectangle to check
|
|
// - coordCorner - One of the corners of the given rectangle
|
|
// Return Value:
|
|
// - The opposite corner of the one given.
|
|
COORD Utils::s_GetOppositeCorner(const SMALL_RECT srRectangle, const COORD coordCorner) noexcept
|
|
{
|
|
// Assert we were given coordinates that are indeed one of the corners of the rectangle.
|
|
FAIL_FAST_IF(!(coordCorner.X == srRectangle.Left || coordCorner.X == srRectangle.Right));
|
|
FAIL_FAST_IF(!(coordCorner.Y == srRectangle.Top || coordCorner.Y == srRectangle.Bottom));
|
|
|
|
return { (srRectangle.Left == coordCorner.X) ? srRectangle.Right : srRectangle.Left,
|
|
(srRectangle.Top == coordCorner.Y) ? srRectangle.Bottom : srRectangle.Top };
|
|
}
|