terminal/src/host/utils.cpp

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 };
}