mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-09 07:41:06 +00:00
Compare commits
11 Commits
dev/miniks
...
v0.10.781.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b8b0b9d94 | ||
|
|
b4dae1238e | ||
|
|
7deaf6b5aa | ||
|
|
266402a2d6 | ||
|
|
3d7b455bb7 | ||
|
|
a6dedbb25a | ||
|
|
37d417c07d | ||
|
|
91503e0c96 | ||
|
|
9516372a8a | ||
|
|
7b9c8c7055 | ||
|
|
860affd608 |
@@ -110,31 +110,31 @@ For commands with arguments:
|
||||
|
||||
| Command | Command Description | Action (*=required) | Action Arguments | Argument Descriptions |
|
||||
| ------- | ------------------- | ------ | ---------------- | ----------------- |
|
||||
| closePane | Close the active pane. | | | |
|
||||
| closeTab | Close the current tab. | | | |
|
||||
| closeWindow | Close the current window and all tabs within it. | | | |
|
||||
| copy | Copy the selected terminal content to your Windows Clipboard. | `trimWhitespace` | boolean | When `true`, newlines persist from the selected text. When `false`, copied content will paste on one line. |
|
||||
| decreaseFontSize | Make the text smaller by one delta. | `delta` | integer | Amount of size decrease per command invocation. |
|
||||
| duplicateTab | Make a copy and open the current tab. | | | |
|
||||
| find | Open the search dialog box. | | | |
|
||||
| increaseFontSize | Make the text larger by one delta. | `delta` | integer | Amount of size increase per command invocation. |
|
||||
| moveFocus | Focus on a different pane depending on direction. | `direction`* | `left`, `right`, `up`, `down` | Direction in which the focus will move. |
|
||||
| newTab | Create a new tab. Without any arguments, this will open the default profile in a new tab. | 1. `commandLine`<br>2. `startingDirectory`<br>3. `tabTitle`<br>4. `index`<br>5. `profile` | 1. string<br>2. string<br>3. string<br>4. integer<br>5. string | 1. Executable run within the tab.<br>2. Directory in which the tab will open.<br>3. Title of the new tab.<br>4. Profile that will open based on its position in the dropdown (starting at 0).<br>5. Profile that will open based on its GUID or name. |
|
||||
| nextTab | Open the tab to the right of the current one. | | | |
|
||||
| openNewTabDropdown | Open the dropdown menu. | | | |
|
||||
| openSettings | Open the settings file. | | | |
|
||||
| paste | Insert the content that was copied onto the clipboard. | | | |
|
||||
| prevTab | Open the tab to the left of the current one. | | | |
|
||||
| resetFontSize | Reset the text size to the default value. | | | |
|
||||
| resizePane | Change the size of the active pane. | `direction`* | `left`, `right`, `up`, `down` | Direction in which the pane will be resized. |
|
||||
| scrollDown | Move the screen down. | | | |
|
||||
| scrollUp | Move the screen up. | | | |
|
||||
| scrollUpPage | Move the screen up a whole page. | | | |
|
||||
| scrollDownPage | Move the screen down a whole page. | | | |
|
||||
| splitPane | Halve the size of the active pane and open another. Without any arguments, this will open the default profile in the new pane. | 1. `split`*<br>2. `commandLine`<br>3. `startingDirectory`<br>4. `tabTitle`<br>5. `index`<br>6. `profile` | 1. `vertical`, `horizontal`, `auto`<br>2. string<br>3. string<br>4. string<br>5. integer<br>6. string | 1. How the pane will split. `auto` will split in the direction that provides the most surface area.<br>2. Executable run within the pane.<br>3. Directory in which the pane will open.<br>4. Title of the tab when the new pane is focused.<br>5. Profile that will open based on its position in the dropdown (starting at 0).<br>6. Profile that will open based on its GUID or name. |
|
||||
| switchToTab | Open a specific tab depending on index. | `index`* | integer | Tab that will open based on its position in the tab bar (starting at 0). |
|
||||
| toggleFullscreen | Switch between fullscreen and default window sizes. | | | |
|
||||
| unbound | Unbind the associated keys from any command. | | | |
|
||||
| `closePane` | Close the active pane. | | | |
|
||||
| `closeTab` | Close the current tab. | | | |
|
||||
| `closeWindow` | Close the current window and all tabs within it. | | | |
|
||||
| `copy` | Copy the selected terminal content to your Windows Clipboard. | `trimWhitespace` | boolean | When `true`, newlines persist from the selected text. When `false`, copied content will paste on one line. |
|
||||
| `decreaseFontSize` | Make the text smaller by one delta. | `delta` | integer | Amount of size decrease per command invocation. |
|
||||
| `duplicateTab` | Make a copy and open the current tab. | | | |
|
||||
| `find` | Open the search dialog box. | | | |
|
||||
| `increaseFontSize` | Make the text larger by one delta. | `delta` | integer | Amount of size increase per command invocation. |
|
||||
| `moveFocus` | Focus on a different pane depending on direction. | `direction`* | `left`, `right`, `up`, `down` | Direction in which the focus will move. |
|
||||
| `newTab` | Create a new tab. Without any arguments, this will open the default profile in a new tab. | 1. `commandLine`<br>2. `startingDirectory`<br>3. `tabTitle`<br>4. `index`<br>5. `profile` | 1. string<br>2. string<br>3. string<br>4. integer<br>5. string | 1. Executable run within the tab.<br>2. Directory in which the tab will open.<br>3. Title of the new tab.<br>4. Profile that will open based on its position in the dropdown (starting at 0).<br>5. Profile that will open based on its GUID or name. |
|
||||
| `nextTab` | Open the tab to the right of the current one. | | | |
|
||||
| `openNewTabDropdown` | Open the dropdown menu. | | | |
|
||||
| `openSettings` | Open the settings file. | | | |
|
||||
| `paste` | Insert the content that was copied onto the clipboard. | | | |
|
||||
| `prevTab` | Open the tab to the left of the current one. | | | |
|
||||
| `resetFontSize` | Reset the text size to the default value. | | | |
|
||||
| `resizePane` | Change the size of the active pane. | `direction`* | `left`, `right`, `up`, `down` | Direction in which the pane will be resized. |
|
||||
| `scrollDown` | Move the screen down. | | | |
|
||||
| `scrollUp` | Move the screen up. | | | |
|
||||
| `scrollUpPage` | Move the screen up a whole page. | | | |
|
||||
| `scrollDownPage` | Move the screen down a whole page. | | | |
|
||||
| `splitPane` | Halve the size of the active pane and open another. Without any arguments, this will open the default profile in the new pane. | 1. `split`*<br>2. `commandLine`<br>3. `startingDirectory`<br>4. `tabTitle`<br>5. `index`<br>6. `profile` | 1. `vertical`, `horizontal`, `auto`<br>2. string<br>3. string<br>4. string<br>5. integer<br>6. string | 1. How the pane will split. `auto` will split in the direction that provides the most surface area.<br>2. Executable run within the pane.<br>3. Directory in which the pane will open.<br>4. Title of the tab when the new pane is focused.<br>5. Profile that will open based on its position in the dropdown (starting at 0).<br>6. Profile that will open based on its GUID or name. |
|
||||
| `switchToTab` | Open a specific tab depending on index. | `index`* | integer | Tab that will open based on its position in the tab bar (starting at 0). |
|
||||
| `toggleFullscreen` | Switch between fullscreen and default window sizes. | | | |
|
||||
| `unbound` | Unbind the associated keys from any command. | | | |
|
||||
|
||||
### Accepted Modifiers and Keys
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
Version="1.0.0.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>ms-resource:AppName</DisplayName>
|
||||
<DisplayName>Windows Terminal</DisplayName>
|
||||
<PublisherDisplayName>Microsoft Corporation</PublisherDisplayName>
|
||||
<Logo>Images\StoreLogo.png</Logo>
|
||||
</Properties>
|
||||
|
||||
@@ -516,7 +516,7 @@ std::vector<::TerminalApp::SettingsLoadWarnings> winrt::TerminalApp::implementat
|
||||
warnings.insert(warnings.end(), parseWarnings.begin(), parseWarnings.end());
|
||||
|
||||
// if an arg parser was registered, but failed, bail
|
||||
if (args == nullptr)
|
||||
if (pfn && args == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -803,10 +803,10 @@ void Pane::_CreateRowColDefinitions(const Size& rootSize)
|
||||
const auto paneSizes = _CalcChildrenSizes(rootSize.Width);
|
||||
|
||||
auto firstColDef = Controls::ColumnDefinition();
|
||||
firstColDef.Width(GridLengthHelper::FromPixels(paneSizes.first));
|
||||
firstColDef.Width(GridLengthHelper::FromValueAndType(paneSizes.first, GridUnitType::Star));
|
||||
|
||||
auto secondColDef = Controls::ColumnDefinition();
|
||||
secondColDef.Width(GridLengthHelper::FromPixels(paneSizes.second));
|
||||
secondColDef.Width(GridLengthHelper::FromValueAndType(paneSizes.second, GridUnitType::Star));
|
||||
|
||||
_root.ColumnDefinitions().Append(firstColDef);
|
||||
_root.ColumnDefinitions().Append(secondColDef);
|
||||
@@ -819,10 +819,10 @@ void Pane::_CreateRowColDefinitions(const Size& rootSize)
|
||||
const auto paneSizes = _CalcChildrenSizes(rootSize.Height);
|
||||
|
||||
auto firstRowDef = Controls::RowDefinition();
|
||||
firstRowDef.Height(GridLengthHelper::FromPixels(paneSizes.first));
|
||||
firstRowDef.Height(GridLengthHelper::FromValueAndType(paneSizes.first, GridUnitType::Star));
|
||||
|
||||
auto secondRowDef = Controls::RowDefinition();
|
||||
secondRowDef.Height(GridLengthHelper::FromPixels(paneSizes.second));
|
||||
secondRowDef.Height(GridLengthHelper::FromValueAndType(paneSizes.second, GridUnitType::Star));
|
||||
|
||||
_root.RowDefinitions().Append(firstRowDef);
|
||||
_root.RowDefinitions().Append(secondRowDef);
|
||||
|
||||
@@ -63,13 +63,13 @@ std::vector<TerminalApp::Profile> WslDistroGenerator::GenerateProfiles()
|
||||
nullptr,
|
||||
&si,
|
||||
&pi));
|
||||
switch (WaitForSingleObject(pi.hProcess, INFINITE))
|
||||
switch (WaitForSingleObject(pi.hProcess, 2000))
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
break;
|
||||
case WAIT_ABANDONED:
|
||||
case WAIT_TIMEOUT:
|
||||
THROW_HR(ERROR_CHILD_NOT_COMPLETE);
|
||||
return profiles;
|
||||
case WAIT_FAILED:
|
||||
THROW_LAST_ERROR();
|
||||
default:
|
||||
|
||||
@@ -724,6 +724,16 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
const auto scanCode = gsl::narrow_cast<WORD>(e.KeyStatus().ScanCode);
|
||||
bool handled = false;
|
||||
|
||||
// Alt-Numpad# input will send us a character once the user releases Alt, so we should be ignoring the individual keydowns.
|
||||
// The character will be sent through the TSFInputControl.
|
||||
// See GH#1401 for more details
|
||||
if (modifiers.IsAltPressed() && (e.OriginalKey() >= VirtualKey::NumberPad0 && e.OriginalKey() <= VirtualKey::NumberPad9))
|
||||
|
||||
{
|
||||
e.Handled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// GH#2235: Terminal::Settings hasn't been modified to differentiate between AltGr and Ctrl+Alt yet.
|
||||
// -> Don't check for key bindings if this is an AltGr key combination.
|
||||
if (!modifiers.IsAltGrPressed())
|
||||
|
||||
@@ -173,15 +173,17 @@ void Terminal::UpdateSettings(winrt::Microsoft::Terminal::Settings::ICoreSetting
|
||||
{
|
||||
return S_FALSE;
|
||||
}
|
||||
const auto dx = viewportSize.X - oldDimensions.X;
|
||||
|
||||
const auto dx = ::base::ClampSub(viewportSize.X, oldDimensions.X);
|
||||
|
||||
const auto oldTop = _mutableViewport.Top();
|
||||
|
||||
const short newBufferHeight = viewportSize.Y + _scrollbackLines;
|
||||
const short newBufferHeight = ::base::ClampAdd(viewportSize.Y, _scrollbackLines);
|
||||
|
||||
COORD bufferSize{ viewportSize.X, newBufferHeight };
|
||||
|
||||
// Save cursor's relative height versus the viewport
|
||||
const short sCursorHeightInViewportBefore = _buffer->GetCursor().GetPosition().Y - _mutableViewport.Top();
|
||||
const short sCursorHeightInViewportBefore = ::base::ClampSub(_buffer->GetCursor().GetPosition().Y, _mutableViewport.Top());
|
||||
|
||||
// This will be used to determine where the viewport should be in the new buffer.
|
||||
const short oldViewportTop = _mutableViewport.Top();
|
||||
@@ -266,7 +268,7 @@ void Terminal::UpdateSettings(winrt::Microsoft::Terminal::Settings::ICoreSetting
|
||||
|
||||
const auto maxRow = std::max(newLastChar.Y, newCursorPos.Y);
|
||||
|
||||
const short proposedTopFromLastLine = ::base::saturated_cast<short>(maxRow - viewportSize.Y + 1);
|
||||
const short proposedTopFromLastLine = ::base::ClampAdd(::base::ClampSub(maxRow, viewportSize.Y), 1);
|
||||
const short proposedTopFromScrollback = newViewportTop;
|
||||
|
||||
short proposedTop = std::max(proposedTopFromLastLine,
|
||||
@@ -294,7 +296,7 @@ void Terminal::UpdateSettings(winrt::Microsoft::Terminal::Settings::ICoreSetting
|
||||
{
|
||||
try
|
||||
{
|
||||
auto row = newTextBuffer->GetRowByOffset(::base::saturated_cast<short>(proposedTop - 1));
|
||||
auto row = newTextBuffer->GetRowByOffset(::base::ClampSub(proposedTop, 1));
|
||||
if (row.GetCharRow().WasWrapForced())
|
||||
{
|
||||
proposedTop--;
|
||||
@@ -324,7 +326,7 @@ void Terminal::UpdateSettings(winrt::Microsoft::Terminal::Settings::ICoreSetting
|
||||
const auto proposedBottom = newView.BottomExclusive();
|
||||
if (proposedBottom > bufferSize.Y)
|
||||
{
|
||||
proposedTop = ::base::saturated_cast<short>(proposedTop - (proposedBottom - bufferSize.Y));
|
||||
proposedTop = ::base::ClampSub(proposedTop, ::base::ClampSub(proposedBottom, bufferSize.Y));
|
||||
}
|
||||
|
||||
_mutableViewport = Viewport::FromDimensions({ 0, proposedTop }, viewportSize);
|
||||
@@ -339,7 +341,7 @@ void Terminal::UpdateSettings(winrt::Microsoft::Terminal::Settings::ICoreSetting
|
||||
|
||||
// If the old scrolloffset was 0, then we weren't scrolled back at all
|
||||
// before, and shouldn't be now either.
|
||||
_scrollOffset = originalOffsetWasZero ? 0 : _mutableViewport.Top() - newVisibleTop;
|
||||
_scrollOffset = originalOffsetWasZero ? 0 : ::base::ClampSub(_mutableViewport.Top(), newVisibleTop);
|
||||
_NotifyScrollEvent();
|
||||
|
||||
return S_OK;
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
using namespace Microsoft::Terminal::Core;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
using namespace WEX::Common;
|
||||
|
||||
namespace TerminalCoreUnitTests
|
||||
{
|
||||
@@ -21,66 +24,109 @@ namespace TerminalCoreUnitTests
|
||||
{
|
||||
TEST_CLASS(ScreenSizeLimitsTest);
|
||||
|
||||
TEST_METHOD(ScreenWidthAndHeightAreClampedToBounds)
|
||||
{
|
||||
DummyRenderTarget emptyRenderTarget;
|
||||
TEST_METHOD(ScreenWidthAndHeightAreClampedToBounds);
|
||||
TEST_METHOD(ScrollbackHistorySizeIsClampedToBounds);
|
||||
|
||||
// Negative values for initial visible row count or column count
|
||||
// are clamped to 1. Too-large positive values are clamped to SHRT_MAX.
|
||||
auto negativeColumnsSettings = winrt::make<MockTermSettings>(10000, 9999999, -1234);
|
||||
Terminal negativeColumnsTerminal;
|
||||
negativeColumnsTerminal.CreateFromSettings(negativeColumnsSettings, emptyRenderTarget);
|
||||
COORD actualDimensions = negativeColumnsTerminal.GetViewport().Dimensions();
|
||||
VERIFY_ARE_EQUAL(actualDimensions.Y, SHRT_MAX, L"Row count clamped to SHRT_MAX == " WCS(SHRT_MAX));
|
||||
VERIFY_ARE_EQUAL(actualDimensions.X, 1, L"Column count clamped to 1");
|
||||
|
||||
// Zero values are clamped to 1 as well.
|
||||
auto zeroRowsSettings = winrt::make<MockTermSettings>(10000, 0, 9999999);
|
||||
Terminal zeroRowsTerminal;
|
||||
zeroRowsTerminal.CreateFromSettings(zeroRowsSettings, emptyRenderTarget);
|
||||
actualDimensions = zeroRowsTerminal.GetViewport().Dimensions();
|
||||
VERIFY_ARE_EQUAL(actualDimensions.Y, 1, L"Row count clamped to 1");
|
||||
VERIFY_ARE_EQUAL(actualDimensions.X, SHRT_MAX, L"Column count clamped to SHRT_MAX == " WCS(SHRT_MAX));
|
||||
}
|
||||
|
||||
TEST_METHOD(ScrollbackHistorySizeIsClampedToBounds)
|
||||
{
|
||||
// What is actually clamped is the number of rows in the internal history buffer,
|
||||
// which is the *sum* of the history size plus the number of rows
|
||||
// actually visible on screen at the moment.
|
||||
|
||||
const unsigned int visibleRowCount = 100;
|
||||
DummyRenderTarget emptyRenderTarget;
|
||||
|
||||
// Zero history size is acceptable.
|
||||
auto noHistorySettings = winrt::make<MockTermSettings>(0, visibleRowCount, 100);
|
||||
Terminal noHistoryTerminal;
|
||||
noHistoryTerminal.CreateFromSettings(noHistorySettings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(noHistoryTerminal.GetTextBuffer().TotalRowCount(), visibleRowCount, L"History size of 0 is accepted");
|
||||
|
||||
// Negative history sizes are clamped to zero.
|
||||
auto negativeHistorySizeSettings = winrt::make<MockTermSettings>(-100, visibleRowCount, 100);
|
||||
Terminal negativeHistorySizeTerminal;
|
||||
negativeHistorySizeTerminal.CreateFromSettings(negativeHistorySizeSettings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(negativeHistorySizeTerminal.GetTextBuffer().TotalRowCount(), visibleRowCount, L"Negative history size is clamped to 0");
|
||||
|
||||
// History size + initial visible rows == SHRT_MAX is acceptable.
|
||||
auto maxHistorySizeSettings = winrt::make<MockTermSettings>(SHRT_MAX - visibleRowCount, visibleRowCount, 100);
|
||||
Terminal maxHistorySizeTerminal;
|
||||
maxHistorySizeTerminal.CreateFromSettings(maxHistorySizeSettings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(maxHistorySizeTerminal.GetTextBuffer().TotalRowCount(), static_cast<unsigned int>(SHRT_MAX), L"History size == SHRT_MAX - initial row count is accepted");
|
||||
|
||||
// History size + initial visible rows == SHRT_MAX + 1 will be clamped slightly.
|
||||
auto justTooBigHistorySizeSettings = winrt::make<MockTermSettings>(SHRT_MAX - visibleRowCount + 1, visibleRowCount, 100);
|
||||
Terminal justTooBigHistorySizeTerminal;
|
||||
justTooBigHistorySizeTerminal.CreateFromSettings(justTooBigHistorySizeSettings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(justTooBigHistorySizeTerminal.GetTextBuffer().TotalRowCount(), static_cast<unsigned int>(SHRT_MAX), L"History size == 1 + SHRT_MAX - initial row count is clamped to SHRT_MAX - initial row count");
|
||||
|
||||
// Ridiculously large history sizes are also clamped.
|
||||
auto farTooBigHistorySizeSettings = winrt::make<MockTermSettings>(99999999, visibleRowCount, 100);
|
||||
Terminal farTooBigHistorySizeTerminal;
|
||||
farTooBigHistorySizeTerminal.CreateFromSettings(farTooBigHistorySizeSettings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(farTooBigHistorySizeTerminal.GetTextBuffer().TotalRowCount(), static_cast<unsigned int>(SHRT_MAX), L"History size that is far too large is clamped to SHRT_MAX - initial row count");
|
||||
}
|
||||
TEST_METHOD(ResizeIsClampedToBounds);
|
||||
};
|
||||
}
|
||||
|
||||
using namespace TerminalCoreUnitTests;
|
||||
|
||||
void ScreenSizeLimitsTest::ScreenWidthAndHeightAreClampedToBounds()
|
||||
{
|
||||
DummyRenderTarget emptyRenderTarget;
|
||||
|
||||
// Negative values for initial visible row count or column count
|
||||
// are clamped to 1. Too-large positive values are clamped to SHRT_MAX.
|
||||
auto negativeColumnsSettings = winrt::make<MockTermSettings>(10000, 9999999, -1234);
|
||||
Terminal negativeColumnsTerminal;
|
||||
negativeColumnsTerminal.CreateFromSettings(negativeColumnsSettings, emptyRenderTarget);
|
||||
COORD actualDimensions = negativeColumnsTerminal.GetViewport().Dimensions();
|
||||
VERIFY_ARE_EQUAL(actualDimensions.Y, SHRT_MAX, L"Row count clamped to SHRT_MAX == " WCS(SHRT_MAX));
|
||||
VERIFY_ARE_EQUAL(actualDimensions.X, 1, L"Column count clamped to 1");
|
||||
|
||||
// Zero values are clamped to 1 as well.
|
||||
auto zeroRowsSettings = winrt::make<MockTermSettings>(10000, 0, 9999999);
|
||||
Terminal zeroRowsTerminal;
|
||||
zeroRowsTerminal.CreateFromSettings(zeroRowsSettings, emptyRenderTarget);
|
||||
actualDimensions = zeroRowsTerminal.GetViewport().Dimensions();
|
||||
VERIFY_ARE_EQUAL(actualDimensions.Y, 1, L"Row count clamped to 1");
|
||||
VERIFY_ARE_EQUAL(actualDimensions.X, SHRT_MAX, L"Column count clamped to SHRT_MAX == " WCS(SHRT_MAX));
|
||||
}
|
||||
|
||||
void ScreenSizeLimitsTest::ScrollbackHistorySizeIsClampedToBounds()
|
||||
{
|
||||
// What is actually clamped is the number of rows in the internal history buffer,
|
||||
// which is the *sum* of the history size plus the number of rows
|
||||
// actually visible on screen at the moment.
|
||||
|
||||
const unsigned int visibleRowCount = 100;
|
||||
DummyRenderTarget emptyRenderTarget;
|
||||
|
||||
// Zero history size is acceptable.
|
||||
auto noHistorySettings = winrt::make<MockTermSettings>(0, visibleRowCount, 100);
|
||||
Terminal noHistoryTerminal;
|
||||
noHistoryTerminal.CreateFromSettings(noHistorySettings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(noHistoryTerminal.GetTextBuffer().TotalRowCount(), visibleRowCount, L"History size of 0 is accepted");
|
||||
|
||||
// Negative history sizes are clamped to zero.
|
||||
auto negativeHistorySizeSettings = winrt::make<MockTermSettings>(-100, visibleRowCount, 100);
|
||||
Terminal negativeHistorySizeTerminal;
|
||||
negativeHistorySizeTerminal.CreateFromSettings(negativeHistorySizeSettings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(negativeHistorySizeTerminal.GetTextBuffer().TotalRowCount(), visibleRowCount, L"Negative history size is clamped to 0");
|
||||
|
||||
// History size + initial visible rows == SHRT_MAX is acceptable.
|
||||
auto maxHistorySizeSettings = winrt::make<MockTermSettings>(SHRT_MAX - visibleRowCount, visibleRowCount, 100);
|
||||
Terminal maxHistorySizeTerminal;
|
||||
maxHistorySizeTerminal.CreateFromSettings(maxHistorySizeSettings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(maxHistorySizeTerminal.GetTextBuffer().TotalRowCount(), static_cast<unsigned int>(SHRT_MAX), L"History size == SHRT_MAX - initial row count is accepted");
|
||||
|
||||
// History size + initial visible rows == SHRT_MAX + 1 will be clamped slightly.
|
||||
auto justTooBigHistorySizeSettings = winrt::make<MockTermSettings>(SHRT_MAX - visibleRowCount + 1, visibleRowCount, 100);
|
||||
Terminal justTooBigHistorySizeTerminal;
|
||||
justTooBigHistorySizeTerminal.CreateFromSettings(justTooBigHistorySizeSettings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(justTooBigHistorySizeTerminal.GetTextBuffer().TotalRowCount(), static_cast<unsigned int>(SHRT_MAX), L"History size == 1 + SHRT_MAX - initial row count is clamped to SHRT_MAX - initial row count");
|
||||
|
||||
// Ridiculously large history sizes are also clamped.
|
||||
auto farTooBigHistorySizeSettings = winrt::make<MockTermSettings>(99999999, visibleRowCount, 100);
|
||||
Terminal farTooBigHistorySizeTerminal;
|
||||
farTooBigHistorySizeTerminal.CreateFromSettings(farTooBigHistorySizeSettings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(farTooBigHistorySizeTerminal.GetTextBuffer().TotalRowCount(), static_cast<unsigned int>(SHRT_MAX), L"History size that is far too large is clamped to SHRT_MAX - initial row count");
|
||||
}
|
||||
|
||||
void ScreenSizeLimitsTest::ResizeIsClampedToBounds()
|
||||
{
|
||||
// What is actually clamped is the number of rows in the internal history buffer,
|
||||
// which is the *sum* of the history size plus the number of rows
|
||||
// actually visible on screen at the moment.
|
||||
//
|
||||
// This is a test for GH#2630, GH#2815.
|
||||
|
||||
const unsigned int initialVisibleColCount = 50;
|
||||
const unsigned int initialVisibleRowCount = 50;
|
||||
const auto historySize = SHRT_MAX - (initialVisibleRowCount * 2);
|
||||
DummyRenderTarget emptyRenderTarget;
|
||||
|
||||
Log::Comment(L"Watch out - this test takes a while on debug, because "
|
||||
L"ResizeWithReflow takes a while on debug. This is expected.");
|
||||
|
||||
auto settings = winrt::make<MockTermSettings>(historySize, initialVisibleRowCount, initialVisibleColCount);
|
||||
Log::Comment(L"First create a terminal with fewer than SHRT_MAX lines");
|
||||
Terminal terminal;
|
||||
terminal.CreateFromSettings(settings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(terminal.GetTextBuffer().TotalRowCount(), static_cast<unsigned int>(historySize + initialVisibleRowCount));
|
||||
|
||||
Log::Comment(L"Resize the terminal to have exactly SHRT_MAX lines");
|
||||
VERIFY_SUCCEEDED(terminal.UserResize({ initialVisibleColCount, initialVisibleRowCount * 2 }));
|
||||
|
||||
VERIFY_ARE_EQUAL(terminal.GetTextBuffer().TotalRowCount(), static_cast<unsigned int>(SHRT_MAX));
|
||||
|
||||
Log::Comment(L"Resize the terminal to have MORE than SHRT_MAX lines - we should clamp to SHRT_MAX");
|
||||
VERIFY_SUCCEEDED(terminal.UserResize({ initialVisibleColCount, initialVisibleRowCount * 3 }));
|
||||
VERIFY_ARE_EQUAL(terminal.GetTextBuffer().TotalRowCount(), static_cast<unsigned int>(SHRT_MAX));
|
||||
|
||||
Log::Comment(L"Resize back down to the original size");
|
||||
VERIFY_SUCCEEDED(terminal.UserResize({ initialVisibleColCount, initialVisibleRowCount }));
|
||||
VERIFY_ARE_EQUAL(terminal.GetTextBuffer().TotalRowCount(), static_cast<unsigned int>(historySize + initialVisibleRowCount));
|
||||
}
|
||||
|
||||
@@ -113,6 +113,7 @@ class ConptyOutputTests
|
||||
TEST_METHOD(SimpleWriteOutputTest);
|
||||
TEST_METHOD(WriteTwoLinesUsesNewline);
|
||||
TEST_METHOD(WriteAFewSimpleLines);
|
||||
TEST_METHOD(InvalidateUntilOneBeforeEnd);
|
||||
|
||||
private:
|
||||
bool _writeCallback(const char* const pch, size_t const cch);
|
||||
@@ -124,10 +125,14 @@ private:
|
||||
|
||||
bool ConptyOutputTests::_writeCallback(const char* const pch, size_t const cch)
|
||||
{
|
||||
// Since rendering happens on a background thread that doesn't have the exception handler on it
|
||||
// we need to rely on VERIFY's return codes instead of exceptions.
|
||||
const WEX::TestExecution::DisableVerifyExceptions disableExceptionsScope;
|
||||
|
||||
std::string actualString = std::string(pch, cch);
|
||||
VERIFY_IS_GREATER_THAN(expectedOutput.size(),
|
||||
static_cast<size_t>(0),
|
||||
NoThrowString().Format(L"writing=\"%hs\", expecting %u strings", actualString.c_str(), expectedOutput.size()));
|
||||
RETURN_BOOL_IF_FALSE(VERIFY_IS_GREATER_THAN(expectedOutput.size(),
|
||||
static_cast<size_t>(0),
|
||||
NoThrowString().Format(L"writing=\"%hs\", expecting %u strings", actualString.c_str(), expectedOutput.size())));
|
||||
|
||||
std::string first = expectedOutput.front();
|
||||
expectedOutput.pop_front();
|
||||
@@ -135,8 +140,8 @@ bool ConptyOutputTests::_writeCallback(const char* const pch, size_t const cch)
|
||||
Log::Comment(NoThrowString().Format(L"Expected =\t\"%hs\"", first.c_str()));
|
||||
Log::Comment(NoThrowString().Format(L"Actual =\t\"%hs\"", actualString.c_str()));
|
||||
|
||||
VERIFY_ARE_EQUAL(first.length(), cch);
|
||||
VERIFY_ARE_EQUAL(first, actualString);
|
||||
RETURN_BOOL_IF_FALSE(VERIFY_ARE_EQUAL(first.length(), cch));
|
||||
RETURN_BOOL_IF_FALSE(VERIFY_ARE_EQUAL(first, actualString));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -314,3 +319,55 @@ void ConptyOutputTests::WriteAFewSimpleLines()
|
||||
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
}
|
||||
|
||||
void ConptyOutputTests::InvalidateUntilOneBeforeEnd()
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Make sure we don't use EL and wipe out the last column of text"));
|
||||
VERIFY_IS_NOT_NULL(_pVtRenderEngine.get());
|
||||
|
||||
auto& g = ServiceLocator::LocateGlobals();
|
||||
auto& renderer = *g.pRender;
|
||||
auto& gci = g.getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer();
|
||||
auto& sm = si.GetStateMachine();
|
||||
auto& tb = si.GetTextBuffer();
|
||||
|
||||
_flushFirstFrame();
|
||||
|
||||
// Move the cursor to width-15, draw 15 characters
|
||||
sm.ProcessString(L"\x1b[1;66H");
|
||||
sm.ProcessString(L"ABCDEFGHIJKLMNO");
|
||||
|
||||
{
|
||||
auto iter = tb.GetCellDataAt({ 78, 0 });
|
||||
VERIFY_ARE_EQUAL(L"N", (iter++)->Chars());
|
||||
VERIFY_ARE_EQUAL(L"O", (iter++)->Chars());
|
||||
}
|
||||
|
||||
expectedOutput.push_back("\x1b[65C");
|
||||
expectedOutput.push_back("ABCDEFGHIJKLMNO");
|
||||
expectedOutput.push_back("\x1b[1;80H"); // we move the cursor to the end of the line after paint
|
||||
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
// overstrike the first with X and the middle 8 with spaces
|
||||
sm.ProcessString(L"\x1b[1;66H");
|
||||
// ABCDEFGHIJKLMNO
|
||||
sm.ProcessString(L"X ");
|
||||
|
||||
{
|
||||
auto iter = tb.GetCellDataAt({ 78, 0 });
|
||||
VERIFY_ARE_EQUAL(L" ", (iter++)->Chars());
|
||||
VERIFY_ARE_EQUAL(L"O", (iter++)->Chars());
|
||||
}
|
||||
|
||||
expectedOutput.push_back("\x1b[1;66H");
|
||||
expectedOutput.push_back("X"); // sequence optimizer should choose ECH here
|
||||
expectedOutput.push_back("\x1b[13X");
|
||||
expectedOutput.push_back("\x1b[13C");
|
||||
|
||||
expectedOutput.push_back("\x1b[?25h"); // we turn the cursor back on for good measure
|
||||
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
}
|
||||
|
||||
@@ -6,13 +6,11 @@
|
||||
#include "til/at.h"
|
||||
#include "til/color.h"
|
||||
#include "til/some.h"
|
||||
#include "til/point.h"
|
||||
#include "til/size.h"
|
||||
#include "til/point.h"
|
||||
#include "til/rectangle.h"
|
||||
#include "til/u8u16convert.h"
|
||||
|
||||
//#include "til/operators.h"
|
||||
|
||||
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,347 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "size.h"
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
class BitmapTests;
|
||||
#endif
|
||||
|
||||
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
class const_bitterator // Bit Iterator. Bitterator.
|
||||
{
|
||||
public:
|
||||
const_bitterator(const std::vector<bool>& values, size_t pos) :
|
||||
_map(values),
|
||||
_pos(pos)
|
||||
{
|
||||
}
|
||||
|
||||
const_bitterator operator+(const ptrdiff_t movement)
|
||||
{
|
||||
auto copy = *this;
|
||||
copy += movement;
|
||||
return copy;
|
||||
}
|
||||
|
||||
const_bitterator operator-(const ptrdiff_t movement)
|
||||
{
|
||||
auto copy = *this;
|
||||
copy -= movement;
|
||||
return copy;
|
||||
}
|
||||
|
||||
const_bitterator& operator++()
|
||||
{
|
||||
++_pos;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
const_bitterator& operator--()
|
||||
{
|
||||
--_pos;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
const_bitterator& operator+=(const ptrdiff_t& movement)
|
||||
{
|
||||
_pos += movement;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
const_bitterator& operator-=(const ptrdiff_t& movement)
|
||||
{
|
||||
_pos -= movement;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
bool operator==(const const_bitterator& other) const
|
||||
{
|
||||
return _pos == other._pos && _map == other._map;
|
||||
}
|
||||
|
||||
bool operator!=(const const_bitterator& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool operator<(const const_bitterator& other) const
|
||||
{
|
||||
return _pos < other._pos;
|
||||
}
|
||||
|
||||
bool operator>(const const_bitterator& other) const
|
||||
{
|
||||
return _pos > other._pos;
|
||||
}
|
||||
|
||||
bool operator*() const
|
||||
{
|
||||
return _map[_pos];
|
||||
}
|
||||
|
||||
private:
|
||||
size_t _pos;
|
||||
const std::vector<bool>& _map;
|
||||
};
|
||||
|
||||
class const_runerator // Run Iterator. Runerator.
|
||||
{
|
||||
public:
|
||||
const_runerator(const std::vector<bool>& values, til::size sz, size_t pos) :
|
||||
_values(values),
|
||||
_size(sz),
|
||||
_pos(pos)
|
||||
{
|
||||
_calculateArea();
|
||||
}
|
||||
|
||||
const_runerator& operator++()
|
||||
{
|
||||
_pos = _nextPos;
|
||||
_calculateArea();
|
||||
return (*this);
|
||||
}
|
||||
|
||||
bool operator==(const const_runerator& other) const
|
||||
{
|
||||
return _pos == other._pos && _values == other._values;
|
||||
}
|
||||
|
||||
bool operator!=(const const_runerator& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool operator<(const const_runerator& other) const
|
||||
{
|
||||
return _pos < other._pos;
|
||||
}
|
||||
|
||||
bool operator>(const const_runerator& other) const
|
||||
{
|
||||
return _pos > other._pos;
|
||||
}
|
||||
|
||||
til::rectangle operator*() const
|
||||
{
|
||||
return _run;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<bool>& _values;
|
||||
const til::size _size;
|
||||
size_t _pos;
|
||||
size_t _nextPos;
|
||||
til::rectangle _run;
|
||||
|
||||
til::point _indexToPoint(size_t index)
|
||||
{
|
||||
return til::point{ (ptrdiff_t)index % _size.width(), (ptrdiff_t)index / _size.width() };
|
||||
}
|
||||
|
||||
void _calculateArea()
|
||||
{
|
||||
const size_t end = (size_t)_size.area();
|
||||
|
||||
_nextPos = _pos;
|
||||
|
||||
while (_nextPos < end && !_values.at(_nextPos))
|
||||
{
|
||||
++_nextPos;
|
||||
}
|
||||
|
||||
if (_nextPos < end)
|
||||
{
|
||||
// pos is now at the first on bit.
|
||||
const auto runStart = _indexToPoint(_nextPos);
|
||||
const size_t rowEndIndex = (size_t)((runStart.y() + 1) * _size.width());
|
||||
|
||||
ptrdiff_t runLength = 0;
|
||||
|
||||
do
|
||||
{
|
||||
++_nextPos;
|
||||
++runLength;
|
||||
} while (_nextPos < end && _nextPos < rowEndIndex && _values.at(_nextPos));
|
||||
|
||||
_run = til::rectangle{ runStart, til::size{ runLength, static_cast<ptrdiff_t>(1) } };
|
||||
}
|
||||
else
|
||||
{
|
||||
_pos = _nextPos;
|
||||
_run = til::rectangle{};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class bitmap
|
||||
{
|
||||
public:
|
||||
using const_iterator = const const_bitterator;
|
||||
|
||||
bitmap() :
|
||||
bitmap(0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
bitmap(size_t width, size_t height) :
|
||||
bitmap(til::size{ width, height })
|
||||
{
|
||||
}
|
||||
|
||||
bitmap(til::size sz) :
|
||||
_size(sz),
|
||||
_bits(sz.area(), true),
|
||||
_empty(false)
|
||||
{
|
||||
}
|
||||
|
||||
const_iterator begin() const
|
||||
{
|
||||
return const_bitterator(_bits, 0);
|
||||
}
|
||||
|
||||
const_iterator end() const
|
||||
{
|
||||
return const_bitterator(_bits, _size.area());
|
||||
}
|
||||
|
||||
const_iterator begin_row(size_t row) const
|
||||
{
|
||||
return const_bitterator(_bits, row * _size.width());
|
||||
}
|
||||
|
||||
const_iterator end_row(size_t row) const
|
||||
{
|
||||
return const_bitterator(_bits, (row + 1) * _size.width());
|
||||
}
|
||||
|
||||
const_runerator begin_runs() const
|
||||
{
|
||||
return const_runerator(_bits, _size, 0);
|
||||
}
|
||||
|
||||
const_runerator end_runs() const
|
||||
{
|
||||
return const_runerator(_bits, _size, _size.area());
|
||||
}
|
||||
|
||||
void set(til::point pt)
|
||||
{
|
||||
_bits[pt.y() * _size.width() + pt.x()] = true;
|
||||
_empty = false;
|
||||
}
|
||||
|
||||
void reset(til::point pt)
|
||||
{
|
||||
_bits[pt.y() * _size.width() + pt.x()] = false;
|
||||
}
|
||||
|
||||
void set(til::rectangle rc)
|
||||
{
|
||||
for (auto pt : rc)
|
||||
{
|
||||
set(pt);
|
||||
}
|
||||
}
|
||||
|
||||
void reset(til::rectangle rc)
|
||||
{
|
||||
for (auto pt : rc)
|
||||
{
|
||||
reset(pt);
|
||||
}
|
||||
}
|
||||
|
||||
void set_all()
|
||||
{
|
||||
// .clear() then .resize(_size(), true) throws an assert (unsupported operation)
|
||||
// .assign(_size(), true) throws an assert (unsupported operation)
|
||||
|
||||
set(til::rectangle{ til::point{ 0, 0 }, _size });
|
||||
}
|
||||
|
||||
void reset_all()
|
||||
{
|
||||
// .clear() then .resize(_size(), false) throws an assert (unsupported operation)
|
||||
// .assign(_size(), false) throws an assert (unsupported operation)
|
||||
reset(til::rectangle{ til::point{ 0, 0 }, _size });
|
||||
_empty = true;
|
||||
}
|
||||
|
||||
void resize(til::size size)
|
||||
{
|
||||
// Don't resize if it's not different as we mark the whole thing dirty on resize.
|
||||
// TODO: marking it dirty might not be necessary or we should be smart about it
|
||||
// (mark none of it dirty on resize down, mark just the edges on up?)
|
||||
if (_size != size)
|
||||
{
|
||||
_size = size;
|
||||
// .resize(_size(), true) throws an assert (unsupported operation)
|
||||
_bits = std::vector<bool>(_size.area(), true);
|
||||
}
|
||||
}
|
||||
|
||||
void resize(size_t width, size_t height)
|
||||
{
|
||||
resize(til::size{ width, height });
|
||||
}
|
||||
|
||||
constexpr bool empty() const
|
||||
{
|
||||
return _empty;
|
||||
}
|
||||
|
||||
const til::size& size() const
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
operator bool() const noexcept
|
||||
{
|
||||
return !_bits.empty();
|
||||
}
|
||||
|
||||
bitmap operator+(const point& pt) const
|
||||
{
|
||||
auto temp = *this;
|
||||
return temp += pt;
|
||||
}
|
||||
|
||||
bitmap& operator+=(const point& pt)
|
||||
{
|
||||
// early return if nothing to do.
|
||||
if (pt.x() == 0 && pt.y() == 0)
|
||||
{
|
||||
return (*this);
|
||||
}
|
||||
|
||||
// If we're told to shift the whole thing by an entire width or height,
|
||||
// the effect is to just clear the whole bitmap.
|
||||
if (pt.x() >= _size.width() || pt.y() >= _size.height())
|
||||
{
|
||||
reset_all();
|
||||
return (*this);
|
||||
}
|
||||
|
||||
// TODO: any way to reconcile this with walk directions from scrolling apis?
|
||||
// TODO: actually implement translation.
|
||||
|
||||
return (*this);
|
||||
}
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class ::BitmapTests;
|
||||
#endif
|
||||
|
||||
private:
|
||||
bool _empty;
|
||||
til::size _size;
|
||||
std::vector<bool> _bits;
|
||||
};
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
class BitteratorTests;
|
||||
#endif
|
||||
|
||||
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
};
|
||||
@@ -1,256 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rectangle.h"
|
||||
#include "size.h"
|
||||
#include "bitmap.h"
|
||||
|
||||
#define _TIL_INLINEPREFIX __declspec(noinline) inline
|
||||
|
||||
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
// RECTANGLE VS SIZE
|
||||
// ADD will grow the total area of the rectangle. The sign is the direction to grow.
|
||||
_TIL_INLINEPREFIX rectangle operator+(const rectangle& lhs, const size& rhs)
|
||||
{
|
||||
// Fetch the pieces of the rectangle.
|
||||
auto l = lhs.left();
|
||||
auto r = lhs.right();
|
||||
auto t = lhs.top();
|
||||
auto b = lhs.bottom();
|
||||
|
||||
// Fetch the scale factors we're using.
|
||||
const auto width = rhs.width();
|
||||
const auto height = rhs.height();
|
||||
|
||||
// Since this is the add operation versus a size, the result
|
||||
// should grow the total rectangle area.
|
||||
// The sign determines which edge of the rectangle moves.
|
||||
// We use the magnitude as how far to move.
|
||||
if (width > 0)
|
||||
{
|
||||
// Adding the positive makes the rectangle "grow"
|
||||
// because right stretches outward (to the right).
|
||||
//
|
||||
// Example with adding width 3...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x------------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| |------------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckAdd(r, width).AssignIfValid(&r));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Adding the negative makes the rectangle "grow"
|
||||
// because left stretches outward (to the left).
|
||||
//
|
||||
// Example with adding width -3...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| |--x---------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| |------------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckAdd(l, width).AssignIfValid(&l));
|
||||
}
|
||||
|
||||
if (height > 0)
|
||||
{
|
||||
// Adding the positive makes the rectangle "grow"
|
||||
// because bottom stretches outward (to the down).
|
||||
//
|
||||
// Example with adding height 2...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x---------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| | |
|
||||
// | |
|
||||
// |---------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckAdd(b, height).AssignIfValid(&b));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Adding the negative makes the rectangle "grow"
|
||||
// because top stretches outward (to the up).
|
||||
//
|
||||
// Example with adding height -2...
|
||||
// |-- x = origin
|
||||
// |
|
||||
// | |---------|
|
||||
// V | |
|
||||
// x---------| x |
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| |---------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckAdd(t, height).AssignIfValid(&t));
|
||||
}
|
||||
|
||||
return rectangle{ til::point{ l, t }, til::point{ r, b } };
|
||||
}
|
||||
|
||||
_TIL_INLINEPREFIX rectangle& operator+=(rectangle& lhs, const size& rhs)
|
||||
{
|
||||
lhs = lhs + rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
// SUB will shrink the total area of the rectangle. The sign is the direction to shrink.
|
||||
_TIL_INLINEPREFIX rectangle operator-(const rectangle& lhs, const size& rhs)
|
||||
{
|
||||
// Fetch the pieces of the rectangle.
|
||||
auto l = lhs.left();
|
||||
auto r = lhs.right();
|
||||
auto t = lhs.top();
|
||||
auto b = lhs.bottom();
|
||||
|
||||
// Fetch the scale factors we're using.
|
||||
const auto width = rhs.width();
|
||||
const auto height = rhs.height();
|
||||
|
||||
// Since this is the subtract operation versus a size, the result
|
||||
// should shrink the total rectangle area.
|
||||
// The sign determines which edge of the rectangle moves.
|
||||
// We use the magnitude as how far to move.
|
||||
if (width > 0)
|
||||
{
|
||||
// Subtracting the positive makes the rectangle "shrink"
|
||||
// because right pulls inward (to the left).
|
||||
//
|
||||
// Example with subtracting width 3...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| |------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckSub(r, width).AssignIfValid(&r));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subtracting the negative makes the rectangle "shrink"
|
||||
// because left pulls inward (to the right).
|
||||
//
|
||||
// Example with subtracting width -3...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x |------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| |------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckSub(l, width).AssignIfValid(&l));
|
||||
}
|
||||
|
||||
if (height > 0)
|
||||
{
|
||||
// Subtracting the positive makes the rectangle "shrink"
|
||||
// because bottom pulls inward (to the up).
|
||||
//
|
||||
// Example with subtracting height 2...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x---------|
|
||||
// | | |---------|
|
||||
// | |
|
||||
// |---------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckSub(b, height).AssignIfValid(&b));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subtracting the positive makes the rectangle "shrink"
|
||||
// because top pulls inward (to the down).
|
||||
//
|
||||
// Example with subtracting height -2...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x
|
||||
// | |
|
||||
// | | |---------|
|
||||
// |---------| |---------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckSub(t, height).AssignIfValid(&t));
|
||||
}
|
||||
|
||||
return rectangle{ til::point{ l, t }, til::point{ r, b } };
|
||||
}
|
||||
|
||||
_TIL_INLINEPREFIX rectangle& operator-=(rectangle& lhs, const size& rhs)
|
||||
{
|
||||
lhs = lhs - rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
// MUL will scale the entire rectangle by the size L/R * WIDTH and T/B * HEIGHT.
|
||||
_TIL_INLINEPREFIX rectangle operator*(const rectangle& lhs, const size& rhs)
|
||||
{
|
||||
ptrdiff_t l;
|
||||
THROW_HR_IF(E_ABORT, !(base::MakeCheckedNum(lhs.left()) * rhs.width()).AssignIfValid(&l));
|
||||
|
||||
ptrdiff_t t;
|
||||
THROW_HR_IF(E_ABORT, !(base::MakeCheckedNum(lhs.top()) * rhs.height()).AssignIfValid(&t));
|
||||
|
||||
ptrdiff_t r;
|
||||
THROW_HR_IF(E_ABORT, !(base::MakeCheckedNum(lhs.right()) * rhs.width()).AssignIfValid(&r));
|
||||
|
||||
ptrdiff_t b;
|
||||
THROW_HR_IF(E_ABORT, !(base::MakeCheckedNum(lhs.bottom()) * rhs.height()).AssignIfValid(&b));
|
||||
|
||||
return til::rectangle{ l, t, r, b };
|
||||
}
|
||||
|
||||
// POINT VS SIZE
|
||||
// This is a convenience and will take X vs WIDTH and Y vs HEIGHT.
|
||||
_TIL_INLINEPREFIX point operator+(const point& lhs, const size& rhs)
|
||||
{
|
||||
return lhs + til::point{ rhs.width(), rhs.height() };
|
||||
}
|
||||
|
||||
_TIL_INLINEPREFIX point operator-(const point& lhs, const size& rhs)
|
||||
{
|
||||
return lhs - til::point{ rhs.width(), rhs.height() };
|
||||
}
|
||||
|
||||
_TIL_INLINEPREFIX point operator*(const point& lhs, const size& rhs)
|
||||
{
|
||||
return lhs * til::point{ rhs.width(), rhs.height() };
|
||||
}
|
||||
|
||||
_TIL_INLINEPREFIX point operator/(const point& lhs, const size& rhs)
|
||||
{
|
||||
return lhs / til::point{ rhs.width(), rhs.height() };
|
||||
}
|
||||
|
||||
// SIZE VS POINT
|
||||
// This is a convenience and will take WIDTH vs X and HEIGHT vs Y.
|
||||
_TIL_INLINEPREFIX size operator+(const size& lhs, const point& rhs)
|
||||
{
|
||||
return lhs + til::size(rhs.x(), rhs.y());
|
||||
}
|
||||
|
||||
_TIL_INLINEPREFIX size operator-(const size& lhs, const point& rhs)
|
||||
{
|
||||
return lhs - til::size(rhs.x(), rhs.y());
|
||||
}
|
||||
|
||||
_TIL_INLINEPREFIX size operator*(const size& lhs, const point& rhs)
|
||||
{
|
||||
return lhs * til::size(rhs.x(), rhs.y());
|
||||
}
|
||||
|
||||
_TIL_INLINEPREFIX size operator/(const size& lhs, const point& rhs)
|
||||
{
|
||||
return lhs / til::size(rhs.x(), rhs.y());
|
||||
}
|
||||
}
|
||||
@@ -39,14 +39,14 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
}
|
||||
|
||||
// This template will convert to point from anything that has an X and a Y field that appear convertible to an integer value
|
||||
// This template will convert to size from anything that has an X and a Y field that appear convertible to an integer value
|
||||
template<typename TOther>
|
||||
constexpr point(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().X)> && std::is_integral_v<decltype(std::declval<TOther>().Y)>, int> /*sentinel*/ = 0) :
|
||||
point(static_cast<ptrdiff_t>(other.X), static_cast<ptrdiff_t>(other.Y))
|
||||
{
|
||||
}
|
||||
|
||||
// This template will convert to point from anything that has a x and a y field that appear convertible to an integer value
|
||||
// This template will convert to size from anything that has a x and a y field that appear convertible to an integer value
|
||||
template<typename TOther>
|
||||
constexpr point(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().x)> && std::is_integral_v<decltype(std::declval<TOther>().y)>, int> /*sentinel*/ = 0) :
|
||||
point(static_cast<ptrdiff_t>(other.x), static_cast<ptrdiff_t>(other.y))
|
||||
@@ -64,7 +64,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
constexpr operator bool() const noexcept
|
||||
operator bool() const noexcept
|
||||
{
|
||||
return _x != 0 || _y != 0;
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
#include "size.h"
|
||||
#include "some.h"
|
||||
|
||||
#include "recterator.h"
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
class RectangleTests;
|
||||
#endif
|
||||
@@ -18,8 +16,6 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
class rectangle
|
||||
{
|
||||
public:
|
||||
using const_iterator = recterator;
|
||||
|
||||
constexpr rectangle() noexcept :
|
||||
rectangle(til::point{ 0, 0 }, til::point{ 0, 0 })
|
||||
{
|
||||
@@ -102,13 +98,6 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
}
|
||||
|
||||
constexpr rectangle& operator=(const rectangle other) noexcept
|
||||
{
|
||||
_topLeft = other._topLeft;
|
||||
_bottomRight = other._bottomRight;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
constexpr bool operator==(const rectangle& other) const noexcept
|
||||
{
|
||||
return _topLeft == other._topLeft &&
|
||||
@@ -126,16 +115,6 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
_topLeft.y() < _bottomRight.y();
|
||||
}
|
||||
|
||||
const_iterator begin() const
|
||||
{
|
||||
return recterator(_topLeft, size());
|
||||
}
|
||||
|
||||
const_iterator end() const
|
||||
{
|
||||
return recterator(_topLeft, size(), { _topLeft.x(), _topLeft.y() + height() });
|
||||
}
|
||||
|
||||
// OR = union
|
||||
constexpr rectangle operator|(const rectangle& other) const noexcept
|
||||
{
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "point.h"
|
||||
#include "size.h"
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
class RecteratorTests;
|
||||
#endif
|
||||
|
||||
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
class recterator
|
||||
{
|
||||
public:
|
||||
recterator(point topLeft, size size) :
|
||||
_topLeft(topLeft),
|
||||
_size(size),
|
||||
_current(topLeft)
|
||||
{
|
||||
}
|
||||
|
||||
recterator(point topLeft, size size, point start) :
|
||||
_topLeft(topLeft),
|
||||
_size(size),
|
||||
_current(start)
|
||||
{
|
||||
}
|
||||
|
||||
recterator& operator++()
|
||||
{
|
||||
if (_current.x() + 1 >= _topLeft.x() + _size.width())
|
||||
{
|
||||
_current = { _topLeft.x(), _current.y() + 1 };
|
||||
}
|
||||
else
|
||||
{
|
||||
_current = { _current.x() + 1, _current.y() };
|
||||
}
|
||||
|
||||
return (*this);
|
||||
}
|
||||
|
||||
bool operator==(const recterator& other) const
|
||||
{
|
||||
return _current == other._current &&
|
||||
_topLeft == other._topLeft &&
|
||||
_size == other._size;
|
||||
}
|
||||
|
||||
bool operator!=(const recterator& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool operator<(const recterator& other) const
|
||||
{
|
||||
return _current < other._current;
|
||||
}
|
||||
|
||||
bool operator>(const recterator& other) const
|
||||
{
|
||||
return _current > other._current;
|
||||
}
|
||||
|
||||
point operator*() const
|
||||
{
|
||||
return _current;
|
||||
}
|
||||
|
||||
protected:
|
||||
point _current;
|
||||
const point _topLeft;
|
||||
const size _size;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class ::RecteratorTests;
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
#ifdef __WEX_COMMON_H__
|
||||
namespace WEX::TestExecution
|
||||
{
|
||||
template<>
|
||||
class VerifyOutputTraits<::til::recterator>
|
||||
{
|
||||
public:
|
||||
static WEX::Common::NoThrowString ToString(const ::til::recterator& /*rect*/)
|
||||
{
|
||||
return WEX::Common::NoThrowString().Format(L"Yep that's a recterator.");
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class VerifyCompareTraits<::til::recterator, ::til::recterator>
|
||||
{
|
||||
public:
|
||||
static bool AreEqual(const ::til::recterator& expected, const ::til::recterator& actual) noexcept
|
||||
{
|
||||
return expected == actual;
|
||||
}
|
||||
|
||||
static bool AreSame(const ::til::recterator& expected, const ::til::recterator& actual) noexcept
|
||||
{
|
||||
return &expected == &actual;
|
||||
}
|
||||
|
||||
static bool IsLessThan(const ::til::recterator& expectedLess, const ::til::recterator& expectedGreater) = delete;
|
||||
|
||||
static bool IsGreaterThan(const ::til::recterator& expectedGreater, const ::til::recterator& expectedLess) = delete;
|
||||
|
||||
static bool IsNull(const ::til::recterator& object) noexcept = delete;
|
||||
};
|
||||
|
||||
};
|
||||
#endif
|
||||
@@ -64,11 +64,6 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
constexpr operator bool() const noexcept
|
||||
{
|
||||
return _width != 0 || _height != 0;
|
||||
}
|
||||
|
||||
size operator+(const size& other) const
|
||||
{
|
||||
ptrdiff_t width;
|
||||
|
||||
@@ -232,7 +232,7 @@ BgfxEngine::BgfxEngine(PVOID SharedViewBase, LONG DisplayHeight, LONG DisplayWid
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
std::vector<til::rectangle> BgfxEngine::GetDirtyArea()
|
||||
std::vector<SMALL_RECT> BgfxEngine::GetDirtyArea()
|
||||
{
|
||||
SMALL_RECT r;
|
||||
r.Bottom = _displayHeight > 0 ? (SHORT)(_displayHeight - 1) : 0;
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Microsoft::Console::Render
|
||||
|
||||
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override;
|
||||
|
||||
std::vector<til::rectangle> GetDirtyArea() override;
|
||||
std::vector<SMALL_RECT> GetDirtyArea() override;
|
||||
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
|
||||
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
|
||||
|
||||
|
||||
@@ -862,10 +862,8 @@ void Renderer::_PaintOverlay(IRenderEngine& engine,
|
||||
// Set it up in a Viewport helper structure and trim it the IME viewport to be within the full console viewport.
|
||||
Viewport viewConv = Viewport::FromInclusive(srCaView);
|
||||
|
||||
for (auto rect : engine.GetDirtyArea())
|
||||
for (auto srDirty : engine.GetDirtyArea())
|
||||
{
|
||||
SMALL_RECT srDirty = rect;
|
||||
|
||||
// Dirty is an inclusive rectangle, but oddly enough the IME was an exclusive one, so correct it.
|
||||
srDirty.Bottom++;
|
||||
srDirty.Right++;
|
||||
|
||||
@@ -65,27 +65,26 @@ using namespace Microsoft::Console::Types;
|
||||
// TODO GH 2683: The default constructor should not throw.
|
||||
DxEngine::DxEngine() :
|
||||
RenderEngineBase(),
|
||||
_invalidMap{},
|
||||
/*_isInvalidUsed{ false },
|
||||
_invalidRect{ 0 },*/
|
||||
_isInvalidUsed{ false },
|
||||
_invalidRect{ 0 },
|
||||
_invalidScroll{ 0 },
|
||||
_presentParams{ 0 },
|
||||
_presentReady{ false },
|
||||
_presentScroll{ 0 },
|
||||
/*_presentDirty{ 0 },*/
|
||||
_presentDirty{ 0 },
|
||||
_presentOffset{ 0 },
|
||||
_isEnabled{ false },
|
||||
_isPainting{ false },
|
||||
_displaySizePixels{},
|
||||
_displaySizePixels{ 0 },
|
||||
_foregroundColor{ 0 },
|
||||
_backgroundColor{ 0 },
|
||||
_selectionBackground{},
|
||||
_glyphCell{ 1, 1 },
|
||||
_glyphCell{ 0 },
|
||||
_haveDeviceResources{ false },
|
||||
_retroTerminalEffects{ false },
|
||||
_antialiasingMode{ D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE },
|
||||
_hwndTarget{ static_cast<HWND>(INVALID_HANDLE_VALUE) },
|
||||
_sizeTarget{},
|
||||
_sizeTarget{ 0 },
|
||||
_dpi{ USER_DEFAULT_SCREEN_DPI },
|
||||
_scale{ 1.0f },
|
||||
_chainMode{ SwapChainMode::ForComposition },
|
||||
@@ -239,8 +238,8 @@ HRESULT DxEngine::_SetupTerminalEffects()
|
||||
|
||||
// Setup the viewport.
|
||||
D3D11_VIEWPORT vp;
|
||||
vp.Width = _displaySizePixels.width<FLOAT>();
|
||||
vp.Height = _displaySizePixels.height<FLOAT>();
|
||||
vp.Width = static_cast<FLOAT>(_displaySizePixels.cx);
|
||||
vp.Height = static_cast<FLOAT>(_displaySizePixels.cy);
|
||||
vp.MinDepth = 0.0f;
|
||||
vp.MaxDepth = 1.0f;
|
||||
vp.TopLeftX = 0;
|
||||
@@ -410,8 +409,6 @@ void DxEngine::_ComputePixelShaderSettings() noexcept
|
||||
|
||||
_displaySizePixels = _GetClientSize();
|
||||
|
||||
_invalidMap.resize(_displaySizePixels / _glyphCell);
|
||||
|
||||
if (createSwapChain)
|
||||
{
|
||||
DXGI_SWAP_CHAIN_DESC1 SwapChainDesc = { 0 };
|
||||
@@ -430,15 +427,11 @@ void DxEngine::_ComputePixelShaderSettings() noexcept
|
||||
case SwapChainMode::ForHwnd:
|
||||
{
|
||||
// use the HWND's dimensions for the swap chain dimensions.
|
||||
til::rectangle clientRect;
|
||||
{
|
||||
RECT rect = { 0 };
|
||||
RETURN_IF_WIN32_BOOL_FALSE(GetClientRect(_hwndTarget, &rect));
|
||||
clientRect = rect;
|
||||
}
|
||||
RECT rect = { 0 };
|
||||
RETURN_IF_WIN32_BOOL_FALSE(GetClientRect(_hwndTarget, &rect));
|
||||
|
||||
SwapChainDesc.Width = clientRect.width<UINT>();
|
||||
SwapChainDesc.Height = clientRect.height<UINT>();
|
||||
SwapChainDesc.Width = rect.right - rect.left;
|
||||
SwapChainDesc.Height = rect.bottom - rect.top;
|
||||
|
||||
// We can't do alpha for HWNDs. Set to ignore. It will fail otherwise.
|
||||
SwapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
|
||||
@@ -464,8 +457,8 @@ void DxEngine::_ComputePixelShaderSettings() noexcept
|
||||
case SwapChainMode::ForComposition:
|
||||
{
|
||||
// Use the given target size for compositions.
|
||||
SwapChainDesc.Width = _displaySizePixels.width<UINT>();
|
||||
SwapChainDesc.Height = _displaySizePixels.height<UINT>();
|
||||
SwapChainDesc.Width = _displaySizePixels.cx;
|
||||
SwapChainDesc.Height = _displaySizePixels.cy;
|
||||
|
||||
// We're doing advanced composition pretty much for the purpose of pretty alpha, so turn it on.
|
||||
SwapChainDesc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
|
||||
@@ -635,8 +628,8 @@ void DxEngine::_ReleaseDeviceResources() noexcept
|
||||
return _dwriteFactory->CreateTextLayout(string,
|
||||
gsl::narrow<UINT32>(stringLength),
|
||||
_dwriteTextFormat.Get(),
|
||||
_displaySizePixels.width<float>(),
|
||||
_glyphCell.height() != 0 ? _glyphCell.height<float>() : _displaySizePixels.height<float>(),
|
||||
gsl::narrow<float>(_displaySizePixels.cx),
|
||||
_glyphCell.cy != 0 ? _glyphCell.cy : gsl::narrow<float>(_displaySizePixels.cy),
|
||||
ppTextLayout);
|
||||
}
|
||||
|
||||
@@ -693,11 +686,7 @@ Microsoft::WRL::ComPtr<IDXGISwapChain1> DxEngine::GetSwapChain()
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, psrRegion);
|
||||
|
||||
SMALL_RECT inclusive = *psrRegion;
|
||||
inclusive.Right--;
|
||||
inclusive.Bottom--;
|
||||
|
||||
_InvalidOr(inclusive);
|
||||
_InvalidOr(*psrRegion);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -711,7 +700,7 @@ Microsoft::WRL::ComPtr<IDXGISwapChain1> DxEngine::GetSwapChain()
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, pcoordCursor);
|
||||
|
||||
const SMALL_RECT sr = Microsoft::Console::Types::Viewport::FromCoord(*pcoordCursor).ToExclusive();
|
||||
const SMALL_RECT sr = Microsoft::Console::Types::Viewport::FromCoord(*pcoordCursor).ToInclusive();
|
||||
return Invalidate(&sr);
|
||||
}
|
||||
|
||||
@@ -759,42 +748,39 @@ Microsoft::WRL::ComPtr<IDXGISwapChain1> DxEngine::GetSwapChain()
|
||||
{
|
||||
try
|
||||
{
|
||||
/*til::point delta(*pcoordDelta);*/
|
||||
// TODO: do this.
|
||||
POINT delta = { 0 };
|
||||
delta.x = pcoordDelta->X * _glyphCell.cx;
|
||||
delta.y = pcoordDelta->Y * _glyphCell.cy;
|
||||
|
||||
//POINT delta = { 0 };
|
||||
//delta.x = pcoordDelta->X * _glyphCell.cx;
|
||||
//delta.y = pcoordDelta->Y * _glyphCell.cy;
|
||||
_InvalidOffset(delta);
|
||||
|
||||
//_InvalidOffset(delta);
|
||||
_invalidScroll.cx += delta.x;
|
||||
_invalidScroll.cy += delta.y;
|
||||
|
||||
//_invalidScroll.cx += delta.x;
|
||||
//_invalidScroll.cy += delta.y;
|
||||
// Add the revealed portion of the screen from the scroll to the invalid area.
|
||||
const RECT display = _GetDisplayRect();
|
||||
RECT reveal = display;
|
||||
|
||||
//// Add the revealed portion of the screen from the scroll to the invalid area.
|
||||
//const RECT display = _GetDisplayRect();
|
||||
//RECT reveal = display;
|
||||
// X delta first
|
||||
OffsetRect(&reveal, delta.x, 0);
|
||||
IntersectRect(&reveal, &reveal, &display);
|
||||
SubtractRect(&reveal, &display, &reveal);
|
||||
|
||||
//// X delta first
|
||||
//OffsetRect(&reveal, delta.x, 0);
|
||||
//IntersectRect(&reveal, &reveal, &display);
|
||||
//SubtractRect(&reveal, &display, &reveal);
|
||||
if (!IsRectEmpty(&reveal))
|
||||
{
|
||||
_InvalidOr(reveal);
|
||||
}
|
||||
|
||||
//if (!IsRectEmpty(&reveal))
|
||||
//{
|
||||
// _InvalidOr(reveal);
|
||||
//}
|
||||
// Y delta second (subtract rect won't work if you move both)
|
||||
reveal = display;
|
||||
OffsetRect(&reveal, 0, delta.y);
|
||||
IntersectRect(&reveal, &reveal, &display);
|
||||
SubtractRect(&reveal, &display, &reveal);
|
||||
|
||||
//// Y delta second (subtract rect won't work if you move both)
|
||||
//reveal = display;
|
||||
//OffsetRect(&reveal, 0, delta.y);
|
||||
//IntersectRect(&reveal, &reveal, &display);
|
||||
//SubtractRect(&reveal, &display, &reveal);
|
||||
|
||||
//if (!IsRectEmpty(&reveal))
|
||||
//{
|
||||
// _InvalidOr(reveal);
|
||||
//}
|
||||
if (!IsRectEmpty(&reveal))
|
||||
{
|
||||
_InvalidOr(reveal);
|
||||
}
|
||||
}
|
||||
CATCH_RETURN();
|
||||
}
|
||||
@@ -810,9 +796,8 @@ Microsoft::WRL::ComPtr<IDXGISwapChain1> DxEngine::GetSwapChain()
|
||||
// - S_OK
|
||||
[[nodiscard]] HRESULT DxEngine::InvalidateAll() noexcept
|
||||
{
|
||||
_invalidMap.set_all();
|
||||
/*const RECT screen = _GetDisplayRect();
|
||||
_InvalidOr(screen);*/
|
||||
const RECT screen = _GetDisplayRect();
|
||||
_InvalidOr(screen);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -837,7 +822,7 @@ Microsoft::WRL::ComPtr<IDXGISwapChain1> DxEngine::GetSwapChain()
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - X by Y area in pixels of the surface
|
||||
[[nodiscard]] til::size DxEngine::_GetClientSize() const noexcept
|
||||
[[nodiscard]] SIZE DxEngine::_GetClientSize() const noexcept
|
||||
{
|
||||
switch (_chainMode)
|
||||
{
|
||||
@@ -846,42 +831,39 @@ Microsoft::WRL::ComPtr<IDXGISwapChain1> DxEngine::GetSwapChain()
|
||||
RECT clientRect = { 0 };
|
||||
LOG_IF_WIN32_BOOL_FALSE(GetClientRect(_hwndTarget, &clientRect));
|
||||
|
||||
til::rectangle client{ clientRect };
|
||||
SIZE clientSize = { 0 };
|
||||
clientSize.cx = clientRect.right - clientRect.left;
|
||||
clientSize.cy = clientRect.bottom - clientRect.top;
|
||||
|
||||
return client.size();
|
||||
return clientSize;
|
||||
}
|
||||
case SwapChainMode::ForComposition:
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: fix scale
|
||||
/*return _sizeTarget * _scale;*/
|
||||
return _sizeTarget;
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
return _sizeTarget;
|
||||
SIZE size = _sizeTarget;
|
||||
size.cx = static_cast<LONG>(size.cx * _scale);
|
||||
size.cy = static_cast<LONG>(size.cy * _scale);
|
||||
return size;
|
||||
}
|
||||
default:
|
||||
FAIL_FAST_HR(E_NOTIMPL);
|
||||
}
|
||||
}
|
||||
|
||||
//// Routine Description:
|
||||
//// - Helper to multiply all parameters of a rectangle by the font size
|
||||
//// to convert from characters to pixels.
|
||||
//// Arguments:
|
||||
//// - cellsToPixels - rectangle to update
|
||||
//// - fontSize - scaling factors
|
||||
//// Return Value:
|
||||
//// - <none> - Updates reference
|
||||
//void _ScaleByFont(RECT& cellsToPixels, SIZE fontSize) noexcept
|
||||
//{
|
||||
// cellsToPixels.left *= fontSize.cx;
|
||||
// cellsToPixels.right *= fontSize.cx;
|
||||
// cellsToPixels.top *= fontSize.cy;
|
||||
// cellsToPixels.bottom *= fontSize.cy;
|
||||
//}
|
||||
// Routine Description:
|
||||
// - Helper to multiply all parameters of a rectangle by the font size
|
||||
// to convert from characters to pixels.
|
||||
// Arguments:
|
||||
// - cellsToPixels - rectangle to update
|
||||
// - fontSize - scaling factors
|
||||
// Return Value:
|
||||
// - <none> - Updates reference
|
||||
void _ScaleByFont(RECT& cellsToPixels, SIZE fontSize) noexcept
|
||||
{
|
||||
cellsToPixels.left *= fontSize.cx;
|
||||
cellsToPixels.right *= fontSize.cx;
|
||||
cellsToPixels.top *= fontSize.cy;
|
||||
cellsToPixels.bottom *= fontSize.cy;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Retrieves a rectangle representation of the pixel size of the
|
||||
@@ -890,9 +872,9 @@ Microsoft::WRL::ComPtr<IDXGISwapChain1> DxEngine::GetSwapChain()
|
||||
// - <none>
|
||||
// Return Value;
|
||||
// - Origin-placed rectangle representing the pixel size of the surface
|
||||
[[nodiscard]] til::rectangle DxEngine::_GetDisplayRect() const noexcept
|
||||
[[nodiscard]] RECT DxEngine::_GetDisplayRect() const noexcept
|
||||
{
|
||||
return til::rectangle{ til::point{ 0, 0 }, _displaySizePixels };
|
||||
return { 0, 0, _displaySizePixels.cx, _displaySizePixels.cy };
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -905,25 +887,22 @@ Microsoft::WRL::ComPtr<IDXGISwapChain1> DxEngine::GetSwapChain()
|
||||
// - <none>
|
||||
void DxEngine::_InvalidOffset(POINT delta)
|
||||
{
|
||||
til::point pt{ delta };
|
||||
_invalidMap += pt;
|
||||
if (_isInvalidUsed)
|
||||
{
|
||||
// Copy the existing invalid rect
|
||||
RECT invalidNew = _invalidRect;
|
||||
|
||||
//if (_isInvalidUsed)
|
||||
//{
|
||||
// // Copy the existing invalid rect
|
||||
// RECT invalidNew = _invalidRect;
|
||||
// Offset it to the new position
|
||||
THROW_IF_WIN32_BOOL_FALSE(OffsetRect(&invalidNew, delta.x, delta.y));
|
||||
|
||||
// // Offset it to the new position
|
||||
// THROW_IF_WIN32_BOOL_FALSE(OffsetRect(&invalidNew, delta.x, delta.y));
|
||||
// Get the rect representing the display
|
||||
const RECT rectScreen = _GetDisplayRect();
|
||||
|
||||
// // Get the rect representing the display
|
||||
// const RECT rectScreen = _GetDisplayRect();
|
||||
// Ensure that the new invalid rectangle is still on the display
|
||||
IntersectRect(&invalidNew, &invalidNew, &rectScreen);
|
||||
|
||||
// // Ensure that the new invalid rectangle is still on the display
|
||||
// IntersectRect(&invalidNew, &invalidNew, &rectScreen);
|
||||
|
||||
// _invalidRect = invalidNew;
|
||||
//}
|
||||
_invalidRect = invalidNew;
|
||||
}
|
||||
}
|
||||
|
||||
// Routine description:
|
||||
@@ -935,22 +914,17 @@ void DxEngine::_InvalidOffset(POINT delta)
|
||||
// - <none>
|
||||
void DxEngine::_InvalidOr(SMALL_RECT sr) noexcept
|
||||
{
|
||||
if (_invalidMap)
|
||||
{
|
||||
_invalidMap.set(sr);
|
||||
}
|
||||
RECT region;
|
||||
region.left = sr.Left;
|
||||
region.top = sr.Top;
|
||||
region.right = sr.Right;
|
||||
region.bottom = sr.Bottom;
|
||||
_ScaleByFont(region, _glyphCell);
|
||||
|
||||
//RECT region;
|
||||
//region.left = sr.Left;
|
||||
//region.top = sr.Top;
|
||||
//region.right = sr.Right;
|
||||
//region.bottom = sr.Bottom;
|
||||
//_ScaleByFont(region, _glyphCell);
|
||||
region.right += _glyphCell.cx;
|
||||
region.bottom += _glyphCell.cy;
|
||||
|
||||
//region.right += _glyphCell.cx;
|
||||
//region.bottom += _glyphCell.cy;
|
||||
|
||||
//_InvalidOr(region);
|
||||
_InvalidOr(region);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -959,20 +933,20 @@ void DxEngine::_InvalidOr(SMALL_RECT sr) noexcept
|
||||
// - rc - Dirty pixel rectangle
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void DxEngine::_InvalidOr(RECT /*rc*/) noexcept
|
||||
void DxEngine::_InvalidOr(RECT rc) noexcept
|
||||
{
|
||||
//if (_isInvalidUsed)
|
||||
//{
|
||||
// UnionRect(&_invalidRect, &_invalidRect, &rc);
|
||||
if (_isInvalidUsed)
|
||||
{
|
||||
UnionRect(&_invalidRect, &_invalidRect, &rc);
|
||||
|
||||
// const RECT rcScreen = _GetDisplayRect();
|
||||
// IntersectRect(&_invalidRect, &_invalidRect, &rcScreen);
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// _invalidRect = rc;
|
||||
// _isInvalidUsed = true;
|
||||
//}
|
||||
const RECT rcScreen = _GetDisplayRect();
|
||||
IntersectRect(&_invalidRect, &_invalidRect, &rcScreen);
|
||||
}
|
||||
else
|
||||
{
|
||||
_invalidRect = rc;
|
||||
_isInvalidUsed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -997,8 +971,25 @@ void DxEngine::_InvalidOr(RECT /*rc*/) noexcept
|
||||
// - Any DirectX error, a memory error, etc.
|
||||
[[nodiscard]] HRESULT DxEngine::StartPaint() noexcept
|
||||
{
|
||||
FAIL_FAST_IF_FAILED(InvalidateAll());
|
||||
RETURN_HR_IF(E_NOT_VALID_STATE, _isPainting); // invalid to start a paint while painting.
|
||||
|
||||
#pragma warning(suppress : 26477 26485 26494 26482 26446 26447) // We don't control TraceLoggingWrite
|
||||
TraceLoggingWrite(g_hDxRenderProvider,
|
||||
"Invalid",
|
||||
TraceLoggingInt32(_invalidRect.bottom - _invalidRect.top, "InvalidHeight"),
|
||||
TraceLoggingInt32((_invalidRect.bottom - _invalidRect.top) / _glyphCell.cy, "InvalidHeightChars"),
|
||||
TraceLoggingInt32(_invalidRect.right - _invalidRect.left, "InvalidWidth"),
|
||||
TraceLoggingInt32((_invalidRect.right - _invalidRect.left) / _glyphCell.cx, "InvalidWidthChars"),
|
||||
TraceLoggingInt32(_invalidRect.left, "InvalidX"),
|
||||
TraceLoggingInt32(_invalidRect.left / _glyphCell.cx, "InvalidXChars"),
|
||||
TraceLoggingInt32(_invalidRect.top, "InvalidY"),
|
||||
TraceLoggingInt32(_invalidRect.top / _glyphCell.cy, "InvalidYChars"),
|
||||
TraceLoggingInt32(_invalidScroll.cx, "ScrollWidth"),
|
||||
TraceLoggingInt32(_invalidScroll.cx / _glyphCell.cx, "ScrollWidthChars"),
|
||||
TraceLoggingInt32(_invalidScroll.cy, "ScrollHeight"),
|
||||
TraceLoggingInt32(_invalidScroll.cy / _glyphCell.cy, "ScrollHeightChars"));
|
||||
|
||||
if (_isEnabled)
|
||||
{
|
||||
try
|
||||
@@ -1008,7 +999,8 @@ void DxEngine::_InvalidOr(RECT /*rc*/) noexcept
|
||||
{
|
||||
RETURN_IF_FAILED(_CreateDeviceResources(true));
|
||||
}
|
||||
else if (_displaySizePixels != clientSize)
|
||||
else if (_displaySizePixels.cy != clientSize.cy ||
|
||||
_displaySizePixels.cx != clientSize.cx)
|
||||
{
|
||||
// OK, we're going to play a dangerous game here for the sake of optimizing resize
|
||||
// First, set up a complete clear of all device resources if something goes terribly wrong.
|
||||
@@ -1021,7 +1013,7 @@ void DxEngine::_InvalidOr(RECT /*rc*/) noexcept
|
||||
_d2dRenderTarget.Reset();
|
||||
|
||||
// Change the buffer size and recreate the render target (and surface)
|
||||
RETURN_IF_FAILED(_dxgiSwapChain->ResizeBuffers(2, clientSize.width<UINT>(), clientSize.height<UINT>(), DXGI_FORMAT_B8G8R8A8_UNORM, 0));
|
||||
RETURN_IF_FAILED(_dxgiSwapChain->ResizeBuffers(2, clientSize.cx, clientSize.cy, DXGI_FORMAT_B8G8R8A8_UNORM, 0));
|
||||
RETURN_IF_FAILED(_PrepareRenderTarget());
|
||||
|
||||
// OK we made it past the parts that can cause errors. We can release our failure handler.
|
||||
@@ -1029,32 +1021,10 @@ void DxEngine::_InvalidOr(RECT /*rc*/) noexcept
|
||||
|
||||
// And persist the new size.
|
||||
_displaySizePixels = clientSize;
|
||||
|
||||
_invalidMap.resize(_displaySizePixels / _glyphCell);
|
||||
}
|
||||
|
||||
_d2dRenderTarget->BeginDraw();
|
||||
_isPainting = true;
|
||||
|
||||
// Walk the map for dirty rectangles and store them up.
|
||||
// We're going to have to go over them multiple times, so don't spend all the iteration
|
||||
// work multiple times.
|
||||
for (auto it = _invalidMap.begin_runs(); it < _invalidMap.end_runs(); ++it)
|
||||
{
|
||||
auto rect = *it;
|
||||
|
||||
#pragma warning(suppress : 26477 26485 26494 26482 26446 26447) // We don't control TraceLoggingWrite
|
||||
TraceLoggingWrite(g_hDxRenderProvider,
|
||||
"Invalid",
|
||||
TraceLoggingInt32((LONG)rect.height(), "InvalidHeightChars"),
|
||||
TraceLoggingInt32((LONG)rect.width(), "InvalidWidthChars"),
|
||||
TraceLoggingInt32((LONG)rect.left(), "InvalidXChars"),
|
||||
TraceLoggingInt32((LONG)rect.top(), "InvalidYChars"),
|
||||
TraceLoggingInt32(_invalidScroll.cx, "ScrollWidthChars"),
|
||||
TraceLoggingInt32(_invalidScroll.cy, "ScrollHeightChars"));
|
||||
|
||||
_dirtyRects.push_back(rect);
|
||||
}
|
||||
}
|
||||
CATCH_RETURN();
|
||||
}
|
||||
@@ -1084,19 +1054,15 @@ void DxEngine::_InvalidOr(RECT /*rc*/) noexcept
|
||||
{
|
||||
if (_invalidScroll.cy != 0 || _invalidScroll.cx != 0)
|
||||
{
|
||||
// The scroll rect is the entire screen minus the revealed areas.
|
||||
// Get the entire screen into a rectangle.
|
||||
til::rectangle scrollArea = _GetDisplayRect();
|
||||
_presentDirty = _invalidRect;
|
||||
|
||||
// Reduce the size of the rectangle by the scroll
|
||||
scrollArea -= _invalidScroll;
|
||||
|
||||
_presentScroll = scrollArea;
|
||||
const RECT display = _GetDisplayRect();
|
||||
SubtractRect(&_presentScroll, &display, &_presentDirty);
|
||||
_presentOffset.x = _invalidScroll.cx;
|
||||
_presentOffset.y = _invalidScroll.cy;
|
||||
|
||||
_presentParams.DirtyRectsCount = gsl::narrow<UINT>(_dirtyRectRects.size());
|
||||
_presentParams.pDirtyRects = _dirtyRectRects.data();
|
||||
_presentParams.DirtyRectsCount = 1;
|
||||
_presentParams.pDirtyRects = &_presentDirty;
|
||||
|
||||
_presentParams.pScrollOffset = &_presentOffset;
|
||||
_presentParams.pScrollRect = &_presentScroll;
|
||||
@@ -1117,11 +1083,8 @@ void DxEngine::_InvalidOr(RECT /*rc*/) noexcept
|
||||
}
|
||||
}
|
||||
|
||||
_dirtyRects.clear();
|
||||
_invalidMap.reset_all();
|
||||
|
||||
/*_invalidRect = { 0 };
|
||||
_isInvalidUsed = false;*/
|
||||
_invalidRect = { 0 };
|
||||
_isInvalidUsed = false;
|
||||
|
||||
_invalidScroll = { 0 };
|
||||
|
||||
@@ -1198,8 +1161,7 @@ void DxEngine::_InvalidOr(RECT /*rc*/) noexcept
|
||||
RETURN_IF_FAILED(_CopyFrontToBack());
|
||||
_presentReady = false;
|
||||
|
||||
_dirtyRectRects.clear();
|
||||
/*_presentDirty = { 0 };*/
|
||||
_presentDirty = { 0 };
|
||||
_presentOffset = { 0 };
|
||||
_presentScroll = { 0 };
|
||||
_presentParams = { 0 };
|
||||
@@ -1222,22 +1184,27 @@ void DxEngine::_InvalidOr(RECT /*rc*/) noexcept
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - This paints in the back most layer of the frame with clear/nothing so it can
|
||||
// be transparent if it wants to be.
|
||||
// - This paints in the back most layer of the frame with the background color.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - S_OK
|
||||
[[nodiscard]] HRESULT DxEngine::PaintBackground() noexcept
|
||||
{
|
||||
const D2D1_COLOR_F nothing = { 0 }; // 0 alpha and color is black.
|
||||
for (const D2D1_RECT_F rect : _dirtyRects)
|
||||
switch (_chainMode)
|
||||
{
|
||||
_d2dRenderTarget->SetTransform(D2D1::Matrix3x2F::Scale(_glyphCell));
|
||||
_d2dRenderTarget->PushAxisAlignedClip(rect, D2D1_ANTIALIAS_MODE_ALIASED);
|
||||
case SwapChainMode::ForHwnd:
|
||||
_d2dRenderTarget->FillRectangle(D2D1::RectF(static_cast<float>(_invalidRect.left),
|
||||
static_cast<float>(_invalidRect.top),
|
||||
static_cast<float>(_invalidRect.right),
|
||||
static_cast<float>(_invalidRect.bottom)),
|
||||
_d2dBrushBackground.Get());
|
||||
break;
|
||||
case SwapChainMode::ForComposition:
|
||||
D2D1_COLOR_F nothing = { 0 };
|
||||
|
||||
_d2dRenderTarget->Clear(nothing);
|
||||
_d2dRenderTarget->PopAxisAlignedClip();
|
||||
_d2dRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
|
||||
break;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
@@ -1258,10 +1225,10 @@ void DxEngine::_InvalidOr(RECT /*rc*/) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
const til::point cellPoint{ coord };
|
||||
|
||||
// Calculate positioning of our origin.
|
||||
const D2D1_POINT_2F origin = til::point{ coord } * _glyphCell;
|
||||
D2D1_POINT_2F origin;
|
||||
origin.x = static_cast<float>(coord.X * _glyphCell.cx);
|
||||
origin.y = static_cast<float>(coord.Y * _glyphCell.cy);
|
||||
|
||||
// Create the text layout
|
||||
CustomTextLayout layout(_dwriteFactory.Get(),
|
||||
@@ -1269,7 +1236,7 @@ void DxEngine::_InvalidOr(RECT /*rc*/) noexcept
|
||||
_dwriteTextFormat.Get(),
|
||||
_dwriteFontFace.Get(),
|
||||
clusters,
|
||||
_glyphCell.width());
|
||||
_glyphCell.cx);
|
||||
|
||||
// Get the baseline for this font as that's where we draw from
|
||||
DWRITE_LINE_SPACING spacing;
|
||||
@@ -1281,7 +1248,7 @@ void DxEngine::_InvalidOr(RECT /*rc*/) noexcept
|
||||
_d2dBrushBackground.Get(),
|
||||
_dwriteFactory.Get(),
|
||||
spacing,
|
||||
_glyphCell,
|
||||
D2D1::SizeF(gsl::narrow<FLOAT>(_glyphCell.cx), gsl::narrow<FLOAT>(_glyphCell.cy)),
|
||||
D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT);
|
||||
|
||||
// Layout then render the text
|
||||
@@ -1382,18 +1349,25 @@ void DxEngine::_InvalidOr(RECT /*rc*/) noexcept
|
||||
// - rect - Rectangle to invert or highlight to make the selection area
|
||||
// Return Value:
|
||||
// - S_OK or relevant DirectX error.
|
||||
[[nodiscard]] HRESULT DxEngine::PaintSelection(SMALL_RECT rect) noexcept
|
||||
[[nodiscard]] HRESULT DxEngine::PaintSelection(const SMALL_RECT rect) noexcept
|
||||
{
|
||||
const auto existingColor = _d2dBrushForeground->GetColor();
|
||||
|
||||
_d2dBrushForeground->SetColor(_selectionBackground);
|
||||
const auto resetColorOnExit = wil::scope_exit([&]() noexcept { _d2dBrushForeground->SetColor(existingColor); });
|
||||
|
||||
rect.Bottom--;
|
||||
rect.Right--;
|
||||
RECT pixels;
|
||||
pixels.left = rect.Left * _glyphCell.cx;
|
||||
pixels.top = rect.Top * _glyphCell.cy;
|
||||
pixels.right = rect.Right * _glyphCell.cx;
|
||||
pixels.bottom = rect.Bottom * _glyphCell.cy;
|
||||
|
||||
D2D1_RECT_F draw = { 0 };
|
||||
draw.left = static_cast<float>(pixels.left);
|
||||
draw.top = static_cast<float>(pixels.top);
|
||||
draw.right = static_cast<float>(pixels.right);
|
||||
draw.bottom = static_cast<float>(pixels.bottom);
|
||||
|
||||
/* rect is SMALL_RECT */
|
||||
const D2D1_RECT_F draw = til::rectangle{ rect } * _glyphCell;
|
||||
_d2dRenderTarget->FillRectangle(draw, _d2dBrushForeground.Get());
|
||||
|
||||
return S_OK;
|
||||
@@ -1421,12 +1395,16 @@ enum class CursorPaintType
|
||||
return S_FALSE;
|
||||
}
|
||||
// Create rectangular block representing where the cursor can fill.
|
||||
D2D1_RECT_F rect = til::rectangle{ til::point{ options.coordCursor } } * _glyphCell;
|
||||
D2D1_RECT_F rect = { 0 };
|
||||
rect.left = static_cast<float>(options.coordCursor.X * _glyphCell.cx);
|
||||
rect.top = static_cast<float>(options.coordCursor.Y * _glyphCell.cy);
|
||||
rect.right = static_cast<float>(rect.left + _glyphCell.cx);
|
||||
rect.bottom = static_cast<float>(rect.top + _glyphCell.cy);
|
||||
|
||||
// If we're double-width, make it one extra glyph wider
|
||||
if (options.fIsDoubleWidth)
|
||||
{
|
||||
rect.right += _glyphCell.width();
|
||||
rect.right += _glyphCell.cx;
|
||||
}
|
||||
|
||||
CursorPaintType paintType = CursorPaintType::Fill;
|
||||
@@ -1438,7 +1416,7 @@ enum class CursorPaintType
|
||||
// Enforce min/max cursor height
|
||||
ULONG ulHeight = std::clamp(options.ulCursorHeightPercent, s_ulMinCursorHeightPercent, s_ulMaxCursorHeightPercent);
|
||||
|
||||
ulHeight = gsl::narrow<ULONG>((_glyphCell.height() * ulHeight) / 100);
|
||||
ulHeight = gsl::narrow<ULONG>((_glyphCell.cy * ulHeight) / 100);
|
||||
rect.top = rect.bottom - ulHeight;
|
||||
break;
|
||||
}
|
||||
@@ -1608,9 +1586,10 @@ CATCH_RETURN()
|
||||
|
||||
try
|
||||
{
|
||||
_glyphCell = fiFontInfo.GetSize();
|
||||
const auto size = fiFontInfo.GetSize();
|
||||
|
||||
_invalidMap.resize(_displaySizePixels / _glyphCell);
|
||||
_glyphCell.cx = size.X;
|
||||
_glyphCell.cy = size.Y;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
@@ -1619,9 +1598,10 @@ CATCH_RETURN()
|
||||
|
||||
[[nodiscard]] Viewport DxEngine::GetViewportInCharacters(const Viewport& viewInPixels) noexcept
|
||||
{
|
||||
const auto cellSize = til::size{ viewInPixels.Dimensions() } / _glyphCell;
|
||||
const short widthInChars = gsl::narrow_cast<short>(viewInPixels.Width() / _glyphCell.cx);
|
||||
const short heightInChars = gsl::narrow_cast<short>(viewInPixels.Height() / _glyphCell.cy);
|
||||
|
||||
return Viewport::FromDimensions(viewInPixels.Origin(), cellSize);
|
||||
return Viewport::FromDimensions(viewInPixels.Origin(), { widthInChars, heightInChars });
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -1706,10 +1686,19 @@ float DxEngine::GetScaling() const noexcept
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - Rectangle describing dirty area in characters.
|
||||
// TODO: maybe this should be returning a ref... not a copy...
|
||||
[[nodiscard]] std::vector<til::rectangle> DxEngine::GetDirtyArea()
|
||||
[[nodiscard]] std::vector<SMALL_RECT> DxEngine::GetDirtyArea()
|
||||
{
|
||||
return _dirtyRects;
|
||||
SMALL_RECT r;
|
||||
r.Top = gsl::narrow<SHORT>(floor(_invalidRect.top / _glyphCell.cy));
|
||||
r.Left = gsl::narrow<SHORT>(floor(_invalidRect.left / _glyphCell.cx));
|
||||
r.Bottom = gsl::narrow<SHORT>(floor(_invalidRect.bottom / _glyphCell.cy));
|
||||
r.Right = gsl::narrow<SHORT>(floor(_invalidRect.right / _glyphCell.cx));
|
||||
|
||||
// Exclusive to inclusive
|
||||
r.Bottom--;
|
||||
r.Right--;
|
||||
|
||||
return { r };
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -1721,7 +1710,7 @@ float DxEngine::GetScaling() const noexcept
|
||||
// - Nearest integer short x and y values for each cell.
|
||||
[[nodiscard]] COORD DxEngine::_GetFontSize() const noexcept
|
||||
{
|
||||
return _glyphCell;
|
||||
return { gsl::narrow<SHORT>(_glyphCell.cx), gsl::narrow<SHORT>(_glyphCell.cy) };
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -1757,7 +1746,7 @@ float DxEngine::GetScaling() const noexcept
|
||||
_dwriteTextFormat.Get(),
|
||||
_dwriteFontFace.Get(),
|
||||
{ &cluster, 1 },
|
||||
_glyphCell.width());
|
||||
_glyphCell.cx);
|
||||
|
||||
UINT32 columns = 0;
|
||||
RETURN_IF_FAILED(layout.GetColumns(&columns));
|
||||
|
||||
@@ -27,9 +27,6 @@
|
||||
|
||||
#include <TraceLoggingProvider.h>
|
||||
|
||||
#include "til/bitmap.h"
|
||||
#include "til/operators.h"
|
||||
|
||||
TRACELOGGING_DECLARE_PROVIDER(g_hDxRenderProvider);
|
||||
|
||||
namespace Microsoft::Console::Render
|
||||
@@ -98,7 +95,7 @@ namespace Microsoft::Console::Render
|
||||
|
||||
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override;
|
||||
|
||||
[[nodiscard]] std::vector<til::rectangle> GetDirtyArea() override;
|
||||
[[nodiscard]] std::vector<SMALL_RECT> GetDirtyArea() override;
|
||||
|
||||
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
|
||||
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
|
||||
@@ -124,7 +121,7 @@ namespace Microsoft::Console::Render
|
||||
SwapChainMode _chainMode;
|
||||
|
||||
HWND _hwndTarget;
|
||||
til::size _sizeTarget;
|
||||
SIZE _sizeTarget;
|
||||
int _dpi;
|
||||
float _scale;
|
||||
|
||||
@@ -133,8 +130,8 @@ namespace Microsoft::Console::Render
|
||||
bool _isEnabled;
|
||||
bool _isPainting;
|
||||
|
||||
til::size _displaySizePixels;
|
||||
til::size _glyphCell;
|
||||
SIZE _displaySizePixels;
|
||||
SIZE _glyphCell;
|
||||
|
||||
D2D1_COLOR_F _defaultForegroundColor;
|
||||
D2D1_COLOR_F _defaultBackgroundColor;
|
||||
@@ -143,14 +140,10 @@ namespace Microsoft::Console::Render
|
||||
D2D1_COLOR_F _backgroundColor;
|
||||
D2D1_COLOR_F _selectionBackground;
|
||||
|
||||
[[nodiscard]] til::rectangle _GetDisplayRect() const noexcept;
|
||||
[[nodiscard]] RECT _GetDisplayRect() const noexcept;
|
||||
|
||||
til::bitmap _invalidMap;
|
||||
std::vector<til::rectangle> _dirtyRects;
|
||||
std::vector<RECT> _dirtyRectRects;
|
||||
|
||||
//bool _isInvalidUsed;
|
||||
//RECT _invalidRect;
|
||||
bool _isInvalidUsed;
|
||||
RECT _invalidRect;
|
||||
SIZE _invalidScroll;
|
||||
|
||||
void _InvalidOr(SMALL_RECT sr) noexcept;
|
||||
@@ -159,7 +152,7 @@ namespace Microsoft::Console::Render
|
||||
void _InvalidOffset(POINT pt);
|
||||
|
||||
bool _presentReady;
|
||||
/*RECT _presentDirty;*/
|
||||
RECT _presentDirty;
|
||||
RECT _presentScroll;
|
||||
POINT _presentOffset;
|
||||
DXGI_PRESENT_PARAMETERS _presentParams;
|
||||
@@ -253,7 +246,7 @@ namespace Microsoft::Console::Render
|
||||
|
||||
[[nodiscard]] COORD _GetFontSize() const noexcept;
|
||||
|
||||
[[nodiscard]] til::size _GetClientSize() const noexcept;
|
||||
[[nodiscard]] SIZE _GetClientSize() const noexcept;
|
||||
|
||||
[[nodiscard]] D2D1_COLOR_F _ColorFFromColorRef(const COLORREF color) noexcept;
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#pragma once
|
||||
|
||||
// This includes support libraries from the CRT, STL, WIL, and GSL
|
||||
#define BLOCK_TIL
|
||||
#include "LibraryIncludes.h"
|
||||
|
||||
#include <windows.h>
|
||||
@@ -35,7 +34,4 @@
|
||||
#include <dwrite_2.h>
|
||||
#include <dwrite_3.h>
|
||||
|
||||
// Include TIL after DX so we can use the DX conversions.
|
||||
#include "til.h"
|
||||
|
||||
#pragma hdrstop
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace Microsoft::Console::Render
|
||||
_Out_ FontInfo& Font,
|
||||
const int iDpi) noexcept override;
|
||||
|
||||
[[nodiscard]] std::vector<til::rectangle> GetDirtyArea() override;
|
||||
[[nodiscard]] std::vector<SMALL_RECT> GetDirtyArea() override;
|
||||
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
|
||||
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ using namespace Microsoft::Console::Render;
|
||||
// Return Value:
|
||||
// - The character dimensions of the current dirty area of the frame.
|
||||
// This is an Inclusive rect.
|
||||
std::vector<til::rectangle> GdiEngine::GetDirtyArea()
|
||||
std::vector<SMALL_RECT> GdiEngine::GetDirtyArea()
|
||||
{
|
||||
RECT rc = _psInvalidData.rcPaint;
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ Author(s):
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "til/rectangle.h"
|
||||
#include "../../inc/conattrs.hpp"
|
||||
#include "Cluster.hpp"
|
||||
#include "FontInfoDesired.hpp"
|
||||
@@ -118,7 +117,7 @@ namespace Microsoft::Console::Render
|
||||
_Out_ FontInfo& FontInfo,
|
||||
const int iDpi) noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual std::vector<til::rectangle> GetDirtyArea() = 0;
|
||||
virtual std::vector<SMALL_RECT> GetDirtyArea() = 0;
|
||||
[[nodiscard]] virtual HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT UpdateTitle(const std::wstring& newTitle) noexcept = 0;
|
||||
|
||||
@@ -403,7 +403,7 @@ UiaEngine::UiaEngine(IUiaEventDispatcher* dispatcher) :
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - Rectangle describing dirty area in characters.
|
||||
[[nodiscard]] std::vector<til::rectangle> UiaEngine::GetDirtyArea()
|
||||
[[nodiscard]] std::vector<SMALL_RECT> UiaEngine::GetDirtyArea()
|
||||
{
|
||||
return { Viewport::Empty().ToInclusive() };
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace Microsoft::Console::Render
|
||||
|
||||
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override;
|
||||
|
||||
[[nodiscard]] std::vector<til::rectangle> GetDirtyArea() override;
|
||||
[[nodiscard]] std::vector<SMALL_RECT> GetDirtyArea() override;
|
||||
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
|
||||
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
|
||||
|
||||
|
||||
@@ -144,11 +144,6 @@ using namespace Microsoft::Console::Render;
|
||||
// - S_OK, else an appropriate HRESULT for failing to allocate or write.
|
||||
[[nodiscard]] HRESULT VtEngine::_InvalidCombine(const Viewport invalid) noexcept
|
||||
{
|
||||
if (_invalidMap)
|
||||
{
|
||||
_invalidMap.set(invalid.ToInclusive());
|
||||
}
|
||||
|
||||
if (!_fInvalidRectUsed)
|
||||
{
|
||||
_invalidRect = invalid;
|
||||
|
||||
@@ -17,9 +17,14 @@ using namespace Microsoft::Console::Types;
|
||||
// Return Value:
|
||||
// - The character dimensions of the current dirty area of the frame.
|
||||
// This is an Inclusive rect.
|
||||
std::vector<til::rectangle> VtEngine::GetDirtyArea()
|
||||
std::vector<SMALL_RECT> VtEngine::GetDirtyArea()
|
||||
{
|
||||
return _invalidRects;
|
||||
SMALL_RECT dirty = _invalidRect.ToInclusive();
|
||||
if (dirty.Top < _virtualTop)
|
||||
{
|
||||
dirty.Top = _virtualTop;
|
||||
}
|
||||
return { dirty };
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
||||
@@ -27,7 +27,6 @@ using namespace Microsoft::Console::Types;
|
||||
|
||||
// If there's nothing to do, quick return
|
||||
bool somethingToDo = _fInvalidRectUsed ||
|
||||
!_invalidMap.empty() ||
|
||||
(_scrollDelta.X != 0 || _scrollDelta.Y != 0) ||
|
||||
_cursorMoved ||
|
||||
_titleChanged;
|
||||
@@ -35,14 +34,6 @@ using namespace Microsoft::Console::Types;
|
||||
_quickReturn = !somethingToDo;
|
||||
_trace.TraceStartPaint(_quickReturn, _fInvalidRectUsed, _invalidRect, _lastViewport, _scrollDelta, _cursorMoved);
|
||||
|
||||
if (somethingToDo)
|
||||
{
|
||||
for (auto it = _invalidMap.begin_runs(); it < _invalidMap.end_runs(); ++it)
|
||||
{
|
||||
_invalidRects.push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
return _quickReturn ? S_FALSE : S_OK;
|
||||
}
|
||||
|
||||
@@ -59,8 +50,6 @@ using namespace Microsoft::Console::Types;
|
||||
{
|
||||
_trace.TraceEndPaint();
|
||||
|
||||
_invalidMap.reset_all();
|
||||
_invalidRects.clear();
|
||||
_invalidRect = Viewport::Empty();
|
||||
_fInvalidRectUsed = false;
|
||||
_scrollDelta = { 0 };
|
||||
@@ -540,7 +529,7 @@ using namespace Microsoft::Console::Types;
|
||||
// before we need to print new text.
|
||||
_deferredCursorPos = { _lastText.X + sNumSpaces, _lastText.Y };
|
||||
|
||||
if (_deferredCursorPos.X < _lastViewport.RightInclusive())
|
||||
if (_deferredCursorPos.X <= _lastViewport.RightInclusive())
|
||||
{
|
||||
RETURN_IF_FAILED(_EraseCharacter(sNumSpaces));
|
||||
}
|
||||
|
||||
@@ -36,8 +36,6 @@ VtEngine::VtEngine(_In_ wil::unique_hfile pipe,
|
||||
_lastWasBold(false),
|
||||
_lastViewport(initialViewport),
|
||||
_invalidRect(Viewport::Empty()),
|
||||
_invalidMap(initialViewport.Dimensions()),
|
||||
_invalidRects(),
|
||||
_fInvalidRectUsed(false),
|
||||
_lastRealCursor({ 0 }),
|
||||
_lastText({ 0 }),
|
||||
@@ -295,8 +293,6 @@ CATCH_RETURN();
|
||||
|
||||
if ((oldView.Height() != newView.Height()) || (oldView.Width() != newView.Width()))
|
||||
{
|
||||
_invalidMap.resize(_lastViewport.Dimensions());
|
||||
|
||||
// Don't emit a resize event if we've requested it be suppressed
|
||||
if (!_suppressResizeRepaint)
|
||||
{
|
||||
|
||||
@@ -24,8 +24,6 @@ Author(s):
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
#include "til/bitmap.h"
|
||||
|
||||
// fwdecl unittest classes
|
||||
#ifdef UNIT_TESTING
|
||||
namespace TerminalCoreUnitTests
|
||||
@@ -91,7 +89,7 @@ namespace Microsoft::Console::Render
|
||||
_Out_ FontInfo& Font,
|
||||
const int iDpi) noexcept override;
|
||||
|
||||
std::vector<til::rectangle> GetDirtyArea() override;
|
||||
std::vector<SMALL_RECT> GetDirtyArea() override;
|
||||
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
|
||||
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
|
||||
|
||||
@@ -122,8 +120,6 @@ namespace Microsoft::Console::Render
|
||||
COLORREF _LastBG;
|
||||
bool _lastWasBold;
|
||||
|
||||
til::bitmap _invalidMap;
|
||||
std::vector<til::rectangle> _invalidRects;
|
||||
Microsoft::Console::Types::Viewport _lastViewport;
|
||||
Microsoft::Console::Types::Viewport _invalidRect;
|
||||
|
||||
|
||||
@@ -355,7 +355,7 @@ bool WddmConEngine::IsInitialized()
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
std::vector<til::rectangle> WddmConEngine::GetDirtyArea()
|
||||
std::vector<SMALL_RECT> WddmConEngine::GetDirtyArea()
|
||||
{
|
||||
SMALL_RECT r;
|
||||
r.Bottom = _displayHeight > 0 ? (SHORT)(_displayHeight - 1) : 0;
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace Microsoft::Console::Render
|
||||
|
||||
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override;
|
||||
|
||||
std::vector<til::rectangle> GetDirtyArea() override;
|
||||
std::vector<SMALL_RECT> GetDirtyArea() override;
|
||||
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
|
||||
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ public:
|
||||
TEST_METHOD(TerminalInputModifierKeyTests);
|
||||
TEST_METHOD(TerminalInputNullKeyTests);
|
||||
TEST_METHOD(DifferentModifiersTest);
|
||||
TEST_METHOD(CtrlNumTest);
|
||||
|
||||
wchar_t GetModifierChar(const bool fShift, const bool fAlt, const bool fCtrl)
|
||||
{
|
||||
@@ -511,6 +512,13 @@ void InputTest::TerminalInputModifierKeyTests()
|
||||
s_pwsInputBuffer[1] = wchShifted;
|
||||
fExpectedKeyHandled = true;
|
||||
}
|
||||
else if (ControlPressed(uiKeystate) && (vkey >= '1' && vkey <= '9'))
|
||||
{
|
||||
// The C-# keys get translated into very specific control
|
||||
// characters that don't play nicely with this test. These keys
|
||||
// are tested in the CtrlNumTest Test instead.
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
fExpectedKeyHandled = false;
|
||||
@@ -684,4 +692,79 @@ void InputTest::DifferentModifiersTest()
|
||||
TestKey(pInput, uiKeystate, vkey, L'/');
|
||||
uiKeystate = RIGHT_ALT_PRESSED;
|
||||
TestKey(pInput, uiKeystate, vkey, L'/');
|
||||
|
||||
// See https://github.com/microsoft/terminal/pull/4947#issuecomment-600382856
|
||||
// C-? -> DEL -> 0x7f
|
||||
Log::Comment(NoThrowString().Format(L"Checking C-?"));
|
||||
// Use SHIFT_PRESSED to force us into differentiating between '/' and '?'
|
||||
vkey = LOBYTE(VkKeyScan(L'?'));
|
||||
s_pwszInputExpected = L"\x7f";
|
||||
TestKey(pInput, SHIFT_PRESSED | LEFT_CTRL_PRESSED, vkey, L'?');
|
||||
TestKey(pInput, SHIFT_PRESSED | RIGHT_CTRL_PRESSED, vkey, L'?');
|
||||
|
||||
// C-M-/ -> 0x1b0x1f
|
||||
Log::Comment(NoThrowString().Format(L"Checking C-M-/"));
|
||||
uiKeystate = LEFT_CTRL_PRESSED | LEFT_ALT_PRESSED;
|
||||
vkey = LOBYTE(VkKeyScan(L'/'));
|
||||
s_pwszInputExpected = L"\x1b\x1f";
|
||||
TestKey(pInput, LEFT_CTRL_PRESSED | LEFT_ALT_PRESSED, vkey, L'/');
|
||||
TestKey(pInput, RIGHT_CTRL_PRESSED | LEFT_ALT_PRESSED, vkey, L'/');
|
||||
// LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED is skipped because that's AltGr
|
||||
TestKey(pInput, RIGHT_CTRL_PRESSED | RIGHT_ALT_PRESSED, vkey, L'/');
|
||||
|
||||
// C-M-? -> 0x1b0x7f
|
||||
Log::Comment(NoThrowString().Format(L"Checking C-M-?"));
|
||||
uiKeystate = LEFT_CTRL_PRESSED | LEFT_ALT_PRESSED;
|
||||
vkey = LOBYTE(VkKeyScan(L'?'));
|
||||
s_pwszInputExpected = L"\x1b\x7f";
|
||||
TestKey(pInput, SHIFT_PRESSED | LEFT_CTRL_PRESSED | LEFT_ALT_PRESSED, vkey, L'?');
|
||||
TestKey(pInput, SHIFT_PRESSED | RIGHT_CTRL_PRESSED | LEFT_ALT_PRESSED, vkey, L'?');
|
||||
// LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED is skipped because that's AltGr
|
||||
TestKey(pInput, SHIFT_PRESSED | RIGHT_CTRL_PRESSED | RIGHT_ALT_PRESSED, vkey, L'?');
|
||||
}
|
||||
|
||||
void InputTest::CtrlNumTest()
|
||||
{
|
||||
Log::Comment(L"Starting test...");
|
||||
|
||||
const TerminalInput* const pInput = new TerminalInput(s_TerminalInputTestCallback);
|
||||
|
||||
Log::Comment(L"Sending the various Ctrl+Num keys.");
|
||||
|
||||
unsigned int uiKeystate = LEFT_CTRL_PRESSED;
|
||||
BYTE vkey = static_cast<WORD>('1');
|
||||
s_pwszInputExpected = L"1";
|
||||
TestKey(pInput, uiKeystate, vkey);
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Skipping Ctrl+2, since that's supposed to send NUL, and doesn't play "
|
||||
L"nicely with this test. Ctrl+2 is covered by other tests in this class."));
|
||||
|
||||
vkey = static_cast<WORD>('3');
|
||||
s_pwszInputExpected = L"\x1b";
|
||||
TestKey(pInput, uiKeystate, vkey);
|
||||
|
||||
vkey = static_cast<WORD>('4');
|
||||
s_pwszInputExpected = L"\x1c";
|
||||
TestKey(pInput, uiKeystate, vkey);
|
||||
|
||||
vkey = static_cast<WORD>('5');
|
||||
s_pwszInputExpected = L"\x1d";
|
||||
TestKey(pInput, uiKeystate, vkey);
|
||||
|
||||
vkey = static_cast<WORD>('6');
|
||||
s_pwszInputExpected = L"\x1e";
|
||||
TestKey(pInput, uiKeystate, vkey);
|
||||
|
||||
vkey = static_cast<WORD>('7');
|
||||
s_pwszInputExpected = L"\x1f";
|
||||
TestKey(pInput, uiKeystate, vkey);
|
||||
|
||||
vkey = static_cast<WORD>('8');
|
||||
s_pwszInputExpected = L"\x7f";
|
||||
TestKey(pInput, uiKeystate, vkey);
|
||||
|
||||
vkey = static_cast<WORD>('9');
|
||||
s_pwszInputExpected = L"9";
|
||||
TestKey(pInput, uiKeystate, vkey);
|
||||
}
|
||||
|
||||
@@ -185,19 +185,40 @@ static constexpr std::array<TermKeyMap, 22> s_modifierKeyMapping{
|
||||
// These sequences are not later updated to encode the modifier state in the
|
||||
// sequence itself, they are just weird exceptional cases to the general
|
||||
// rules above.
|
||||
static constexpr std::array<TermKeyMap, 6> s_simpleModifiedKeyMapping{
|
||||
static constexpr std::array<TermKeyMap, 14> s_simpleModifiedKeyMapping{
|
||||
TermKeyMap{ VK_BACK, CTRL_PRESSED, L"\x8" },
|
||||
TermKeyMap{ VK_BACK, ALT_PRESSED, L"\x1b\x7f" },
|
||||
TermKeyMap{ VK_BACK, CTRL_PRESSED | ALT_PRESSED, L"\x1b\x8" },
|
||||
TermKeyMap{ VK_TAB, CTRL_PRESSED, L"\t" },
|
||||
TermKeyMap{ VK_TAB, SHIFT_PRESSED, L"\x1b[Z" },
|
||||
TermKeyMap{ VK_DIVIDE, CTRL_PRESSED, L"\x1F" },
|
||||
|
||||
// GH#3507 - We should also be encoding Ctrl+# according to the following table:
|
||||
// https://vt100.net/docs/vt220-rm/table3-5.html
|
||||
// * 1 and 9 do not send any special characters, but they _should_ send
|
||||
// through the character unmodified.
|
||||
// * 0 doesn't seem to send even an unmodified '0' through.
|
||||
// * Ctrl+2 is already special-cased below in `HandleKey`, so it's not
|
||||
// included here.
|
||||
TermKeyMap{ static_cast<WORD>('1'), CTRL_PRESSED, L"1" },
|
||||
// TermKeyMap{ static_cast<WORD>('2'), CTRL_PRESSED, L"\x00" },
|
||||
TermKeyMap{ static_cast<WORD>('3'), CTRL_PRESSED, L"\x1B" },
|
||||
TermKeyMap{ static_cast<WORD>('4'), CTRL_PRESSED, L"\x1C" },
|
||||
TermKeyMap{ static_cast<WORD>('5'), CTRL_PRESSED, L"\x1D" },
|
||||
TermKeyMap{ static_cast<WORD>('6'), CTRL_PRESSED, L"\x1E" },
|
||||
TermKeyMap{ static_cast<WORD>('7'), CTRL_PRESSED, L"\x1F" },
|
||||
TermKeyMap{ static_cast<WORD>('8'), CTRL_PRESSED, L"\x7F" },
|
||||
TermKeyMap{ static_cast<WORD>('9'), CTRL_PRESSED, L"9" },
|
||||
|
||||
// These two are not implemented here, because they are system keys.
|
||||
// TermKeyMap{ VK_TAB, ALT_PRESSED, L""}, This is the Windows system shortcut for switching windows.
|
||||
// TermKeyMap{ VK_ESCAPE, ALT_PRESSED, L""}, This is another Windows system shortcut for switching windows.
|
||||
};
|
||||
|
||||
const wchar_t* const CTRL_SLASH_SEQUENCE = L"\x1f";
|
||||
const wchar_t* const CTRL_QUESTIONMARK_SEQUENCE = L"\x7F";
|
||||
const wchar_t* const CTRL_ALT_SLASH_SEQUENCE = L"\x1b\x1f";
|
||||
const wchar_t* const CTRL_ALT_QUESTIONMARK_SEQUENCE = L"\x1b\x7F";
|
||||
|
||||
void TerminalInput::ChangeKeypadMode(const bool applicationMode) noexcept
|
||||
{
|
||||
@@ -323,13 +344,73 @@ static bool _searchWithModifier(const KeyEvent& keyEvent, InputSender sender)
|
||||
}
|
||||
else
|
||||
{
|
||||
// One last check: C-/ is supposed to be C-_
|
||||
// But '/' is not the same VKEY on all keyboards. So we have to
|
||||
// figure out the vkey at runtime.
|
||||
const BYTE slashVkey = LOBYTE(VkKeyScan(L'/'));
|
||||
if (keyEvent.GetVirtualKeyCode() == slashVkey && keyEvent.IsCtrlPressed())
|
||||
// One last check:
|
||||
// * C-/ is supposed to be ^_ (the C0 character US)
|
||||
// * C-? is supposed to be DEL
|
||||
// * C-M-/ is supposed to be ^[^_
|
||||
// * C-M-? is supposed to be ^[^?
|
||||
//
|
||||
// But this whole scenario is tricky. '/' is not the same VKEY on
|
||||
// all keyboards. On USASCII keyboards, '/' and '?' share the _same_
|
||||
// key. So we have to figure out the vkey at runtime, and we have to
|
||||
// determine if the key that was pressed was '?' with some
|
||||
// modifiers, or '/' with some modifiers.
|
||||
//
|
||||
// These translations are not in s_simpleModifiedKeyMapping, because
|
||||
// the aformentioned fact that they aren't the same VKEY on all
|
||||
// keyboards.
|
||||
//
|
||||
// See GH#3079 for details.
|
||||
// Also see https://github.com/microsoft/terminal/pull/4947#issuecomment-600382856
|
||||
|
||||
// VkKeyScan will give us both the Vkey of the key needed for this
|
||||
// character, and the modifiers the user might need to press to get
|
||||
// this character.
|
||||
const auto slashKeyScan = VkKeyScan(L'/'); // On USASCII: 0x00bf
|
||||
const auto questionMarkKeyScan = VkKeyScan(L'?'); //On USASCII: 0x01bf
|
||||
|
||||
const auto slashVkey = LOBYTE(slashKeyScan);
|
||||
const auto questionMarkVkey = LOBYTE(questionMarkKeyScan);
|
||||
|
||||
const auto ctrl = keyEvent.IsCtrlPressed();
|
||||
const auto alt = keyEvent.IsAltPressed();
|
||||
const bool shift = keyEvent.IsShiftPressed();
|
||||
|
||||
// From the KeyEvent we're translating, synthesize the equivalent VkKeyScan result
|
||||
const auto vkey = keyEvent.GetVirtualKeyCode();
|
||||
const short keyScanFromEvent = vkey |
|
||||
(shift ? 0x100 : 0) |
|
||||
(ctrl ? 0x200 : 0) |
|
||||
(alt ? 0x400 : 0);
|
||||
|
||||
// Make sure the VKEY is an _exact_ match, and that the modifier
|
||||
// bits also match. This handles the hypothetical case we get a
|
||||
// keyscan back that's ctrl+alt+some_random_VK, and some_random_VK
|
||||
// has bits that are a superset of the bits set for question mark.
|
||||
const bool wasQuestionMark = vkey == questionMarkVkey && WI_AreAllFlagsSet(keyScanFromEvent, questionMarkKeyScan);
|
||||
const bool wasSlash = vkey == slashVkey && WI_AreAllFlagsSet(keyScanFromEvent, slashKeyScan);
|
||||
|
||||
// If the key pressed was exactly the ? key, then try to send the
|
||||
// appropriate sequence for a modified '?'. Otherwise, check if this
|
||||
// was a modified '/' keypress. These mappings don't need to be
|
||||
// changed at all.
|
||||
if ((ctrl && alt) && wasQuestionMark)
|
||||
{
|
||||
sender(CTRL_ALT_QUESTIONMARK_SEQUENCE);
|
||||
success = true;
|
||||
}
|
||||
else if (ctrl && wasQuestionMark)
|
||||
{
|
||||
sender(CTRL_QUESTIONMARK_SEQUENCE);
|
||||
success = true;
|
||||
}
|
||||
else if ((ctrl && alt) && wasSlash)
|
||||
{
|
||||
sender(CTRL_ALT_SLASH_SEQUENCE);
|
||||
success = true;
|
||||
}
|
||||
else if (ctrl && wasSlash)
|
||||
{
|
||||
// This mapping doesn't need to be changed at all.
|
||||
sender(CTRL_SLASH_SEQUENCE);
|
||||
success = true;
|
||||
}
|
||||
|
||||
@@ -1167,8 +1167,13 @@ void StateMachine::ProcessCharacter(const wchar_t wch)
|
||||
_trace.TraceCharInput(wch);
|
||||
|
||||
// Process "from anywhere" events first.
|
||||
if (wch == AsciiChars::CAN ||
|
||||
wch == AsciiChars::SUB)
|
||||
const bool isFromAnywhereChar = (wch == AsciiChars::CAN || wch == AsciiChars::SUB);
|
||||
|
||||
// GH#4201 - If this sequence was ^[^X or ^[^Z, then we should
|
||||
// _ActionExecuteFromEscape, as to send a Ctrl+Alt+key key. We should only
|
||||
// do this for the InputStateMachineEngine - the OutputEngine should execute
|
||||
// these from any state.
|
||||
if (isFromAnywhereChar && !(_state == VTStates::Escape && _engine->DispatchControlCharsFromEscape()))
|
||||
{
|
||||
_ActionExecute(wch);
|
||||
_EnterGround();
|
||||
|
||||
@@ -259,6 +259,7 @@ class Microsoft::Console::VirtualTerminal::InputEngineTest
|
||||
TEST_METHOD(SGRMouseTest_Modifiers);
|
||||
TEST_METHOD(SGRMouseTest_Movement);
|
||||
TEST_METHOD(SGRMouseTest_Scroll);
|
||||
TEST_METHOD(CtrlAltZCtrlAltXTest);
|
||||
|
||||
friend class TestInteractDispatch;
|
||||
};
|
||||
@@ -1141,3 +1142,68 @@ void InputEngineTest::SGRMouseTest_Scroll()
|
||||
// clang-format on
|
||||
VerifySGRMouseData(testData);
|
||||
}
|
||||
|
||||
void InputEngineTest::CtrlAltZCtrlAltXTest()
|
||||
{
|
||||
auto pfn = std::bind(&TestState::TestInputCallback, &testState, std::placeholders::_1);
|
||||
|
||||
auto dispatch = std::make_unique<TestInteractDispatch>(pfn, &testState);
|
||||
auto inputEngine = std::make_unique<InputStateMachineEngine>(std::move(dispatch));
|
||||
auto _stateMachine = std::make_unique<StateMachine>(std::move(inputEngine));
|
||||
VERIFY_IS_NOT_NULL(_stateMachine);
|
||||
testState._stateMachine = _stateMachine.get();
|
||||
|
||||
// This is a test for GH#4201. See that issue for more details.
|
||||
Log::Comment(L"Test Ctrl+Alt+Z and Ctrl+Alt+X, which execute from anywhere "
|
||||
L"in the output engine, but should be Escape-Executed in the "
|
||||
L"input engine.");
|
||||
|
||||
DisableVerifyExceptions disable;
|
||||
|
||||
{
|
||||
auto inputSeq = L"\x1b\x1a"; // ^[^Z
|
||||
|
||||
wchar_t expectedWch = L'Z';
|
||||
short keyscan = VkKeyScanW(expectedWch);
|
||||
short vkey = keyscan & 0xff;
|
||||
WORD scanCode = (WORD)MapVirtualKeyW(vkey, MAPVK_VK_TO_VSC);
|
||||
|
||||
INPUT_RECORD inputRec;
|
||||
|
||||
inputRec.EventType = KEY_EVENT;
|
||||
inputRec.Event.KeyEvent.bKeyDown = TRUE;
|
||||
inputRec.Event.KeyEvent.dwControlKeyState = LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED;
|
||||
inputRec.Event.KeyEvent.wRepeatCount = 1;
|
||||
inputRec.Event.KeyEvent.wVirtualKeyCode = vkey;
|
||||
inputRec.Event.KeyEvent.wVirtualScanCode = scanCode;
|
||||
inputRec.Event.KeyEvent.uChar.UnicodeChar = expectedWch - 0x40;
|
||||
|
||||
testState.vExpectedInput.push_back(inputRec);
|
||||
|
||||
_stateMachine->ProcessString(inputSeq);
|
||||
}
|
||||
{
|
||||
auto inputSeq = L"\x1b\x18"; // ^[^X
|
||||
|
||||
wchar_t expectedWch = L'X';
|
||||
short keyscan = VkKeyScanW(expectedWch);
|
||||
short vkey = keyscan & 0xff;
|
||||
WORD scanCode = (WORD)MapVirtualKeyW(vkey, MAPVK_VK_TO_VSC);
|
||||
|
||||
INPUT_RECORD inputRec;
|
||||
|
||||
inputRec.EventType = KEY_EVENT;
|
||||
inputRec.Event.KeyEvent.bKeyDown = TRUE;
|
||||
inputRec.Event.KeyEvent.dwControlKeyState = LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED;
|
||||
inputRec.Event.KeyEvent.wRepeatCount = 1;
|
||||
inputRec.Event.KeyEvent.wVirtualKeyCode = vkey;
|
||||
inputRec.Event.KeyEvent.wVirtualScanCode = scanCode;
|
||||
inputRec.Event.KeyEvent.uChar.UnicodeChar = expectedWch - 0x40;
|
||||
|
||||
testState.vExpectedInput.push_back(inputRec);
|
||||
|
||||
_stateMachine->ProcessString(inputSeq);
|
||||
}
|
||||
|
||||
VerifyExpectedInputDrained();
|
||||
}
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
#include "til/bitmap.h"
|
||||
|
||||
using namespace WEX::Common;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
|
||||
class BitmapTests
|
||||
{
|
||||
TEST_CLASS(BitmapTests);
|
||||
|
||||
TEST_METHOD(Construct)
|
||||
{
|
||||
COORD foo;
|
||||
foo.X = 12;
|
||||
foo.Y = 14;
|
||||
til::point p(foo);
|
||||
VERIFY_ARE_EQUAL(foo.X, p.x());
|
||||
VERIFY_ARE_EQUAL(foo.Y, p.y());
|
||||
|
||||
POINT pt;
|
||||
pt.x = 88;
|
||||
pt.y = 98;
|
||||
til::point q(pt);
|
||||
VERIFY_ARE_EQUAL(pt.x, q.x());
|
||||
VERIFY_ARE_EQUAL(pt.y, q.y());
|
||||
|
||||
SIZE sz;
|
||||
sz.cx = 11;
|
||||
sz.cy = 13;
|
||||
til::size r(sz);
|
||||
VERIFY_ARE_EQUAL(sz.cx, r.width());
|
||||
VERIFY_ARE_EQUAL(sz.cy, r.height());
|
||||
|
||||
COORD bar;
|
||||
bar.X = 57;
|
||||
bar.Y = 15;
|
||||
til::size s(bar);
|
||||
VERIFY_ARE_EQUAL(bar.X, s.width());
|
||||
VERIFY_ARE_EQUAL(bar.Y, s.height());
|
||||
|
||||
SIZE mapSize{ 10, 10 };
|
||||
til::bitmap x(mapSize);
|
||||
x.set({ 4, 4 });
|
||||
|
||||
til::rectangle area(til::point{ 5, 5 }, til::size{ 2, 2 });
|
||||
x.set(area);
|
||||
|
||||
Log::Comment(L"Row 4!");
|
||||
for (auto it = x.begin_row(4); it < x.end_row(4); ++it)
|
||||
{
|
||||
if (*it)
|
||||
{
|
||||
Log::Comment(L"True");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::Comment(L"False");
|
||||
}
|
||||
}
|
||||
|
||||
Log::Comment(L"All!");
|
||||
auto start = x.begin();
|
||||
auto end = x.end();
|
||||
|
||||
for (const auto& y : x)
|
||||
{
|
||||
if (y)
|
||||
{
|
||||
Log::Comment(L"True");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::Comment(L"False");
|
||||
}
|
||||
}
|
||||
|
||||
SMALL_RECT smrc;
|
||||
smrc.Top = 31;
|
||||
smrc.Bottom = 41;
|
||||
smrc.Left = 59;
|
||||
smrc.Right = 265;
|
||||
|
||||
til::rectangle smrectangle(smrc);
|
||||
|
||||
VERIFY_ARE_EQUAL(smrc.Top, smrectangle.top());
|
||||
VERIFY_ARE_EQUAL(smrc.Bottom, smrectangle.bottom());
|
||||
VERIFY_ARE_EQUAL(smrc.Left, smrectangle.left());
|
||||
VERIFY_ARE_EQUAL(smrc.Right, smrectangle.right());
|
||||
|
||||
RECT bgrc;
|
||||
bgrc.top = 3;
|
||||
bgrc.bottom = 5;
|
||||
bgrc.left = 8;
|
||||
bgrc.right = 9;
|
||||
|
||||
til::rectangle bgrectangle(bgrc);
|
||||
|
||||
VERIFY_ARE_EQUAL(bgrc.top, bgrectangle.top());
|
||||
VERIFY_ARE_EQUAL(bgrc.bottom, bgrectangle.bottom());
|
||||
VERIFY_ARE_EQUAL(bgrc.left, bgrectangle.left());
|
||||
VERIFY_ARE_EQUAL(bgrc.right, bgrectangle.right());
|
||||
}
|
||||
|
||||
TEST_METHOD(Runerator)
|
||||
{
|
||||
til::bitmap foo{ til::size{ 4, 8 } };
|
||||
foo.reset_all();
|
||||
foo.set(til::rectangle{ til::point{ 1, 1 }, til::size{ 2, 2 } });
|
||||
|
||||
foo.set(til::rectangle{ til::point{ 3, 5 } });
|
||||
foo.set(til::rectangle{ til::point{ 0, 6 } });
|
||||
|
||||
std::deque<til::rectangle> expectedRects;
|
||||
expectedRects.push_back(til::rectangle{ til::point{ 1, 1 }, til::size{ 2, 1 } });
|
||||
expectedRects.push_back(til::rectangle{ til::point{ 1, 2 }, til::size{ 2, 1 } });
|
||||
expectedRects.push_back(til::rectangle{ til::point{ 3, 5 } });
|
||||
expectedRects.push_back(til::rectangle{ til::point{ 0, 6 } });
|
||||
|
||||
for (auto it = foo.begin_runs(); it < foo.end_runs(); ++it)
|
||||
{
|
||||
const auto actual = *it;
|
||||
const auto expected = expectedRects.front();
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
|
||||
expectedRects.pop_front();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,171 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
#include "til/operators.h"
|
||||
|
||||
using namespace WEX::Common;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
|
||||
class OperatorTests
|
||||
{
|
||||
TEST_CLASS(OperatorTests);
|
||||
|
||||
TEST_METHOD(RectangleAdditionSize)
|
||||
{
|
||||
const til::rectangle start{ 10, 20, 30, 40 };
|
||||
|
||||
Log::Comment(L"1.) Add size to bottom and right");
|
||||
{
|
||||
const til::size scale{ 3, 7 };
|
||||
const til::rectangle expected{ 10, 20, 33, 47 };
|
||||
const auto actual = start + scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Add size to top and left");
|
||||
{
|
||||
const til::size scale{ -3, -7 };
|
||||
const til::rectangle expected{ 7, 13, 30, 40 };
|
||||
const auto actual = start + scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"3.) Add size to bottom and left");
|
||||
{
|
||||
const til::size scale{ -3, 7 };
|
||||
const til::rectangle expected{ 7, 20, 30, 47 };
|
||||
const auto actual = start + scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"4.) Add size to top and right");
|
||||
{
|
||||
const til::size scale{ 3, -7 };
|
||||
const til::rectangle expected{ 10, 13, 33, 40 };
|
||||
const auto actual = start + scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(RectangleInplaceAdditionSize)
|
||||
{
|
||||
const til::rectangle start{ 10, 20, 30, 40 };
|
||||
|
||||
Log::Comment(L"1.) Add size to bottom and right");
|
||||
{
|
||||
auto actual = start;
|
||||
const til::size scale{ 3, 7 };
|
||||
const til::rectangle expected{ 10, 20, 33, 47 };
|
||||
actual += scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Add size to top and left");
|
||||
{
|
||||
auto actual = start;
|
||||
const til::size scale{ -3, -7 };
|
||||
const til::rectangle expected{ 7, 13, 30, 40 };
|
||||
actual += scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"3.) Add size to bottom and left");
|
||||
{
|
||||
auto actual = start;
|
||||
const til::size scale{ -3, 7 };
|
||||
const til::rectangle expected{ 7, 20, 30, 47 };
|
||||
actual += scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"4.) Add size to top and right");
|
||||
{
|
||||
auto actual = start;
|
||||
const til::size scale{ 3, -7 };
|
||||
const til::rectangle expected{ 10, 13, 33, 40 };
|
||||
actual += scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(RectangleSubtractionSize)
|
||||
{
|
||||
const til::rectangle start{ 10, 20, 30, 40 };
|
||||
|
||||
Log::Comment(L"1.) Subtract size from bottom and right");
|
||||
{
|
||||
const til::size scale{ 3, 7 };
|
||||
const til::rectangle expected{ 10, 20, 27, 33 };
|
||||
const auto actual = start - scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Subtract size from top and left");
|
||||
{
|
||||
const til::size scale{ -3, -7 };
|
||||
const til::rectangle expected{ 13, 27, 30, 40 };
|
||||
const auto actual = start - scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"3.) Subtract size from bottom and left");
|
||||
{
|
||||
const til::size scale{ -3, 7 };
|
||||
const til::rectangle expected{ 13, 20, 30, 33 };
|
||||
const auto actual = start - scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"4.) Subtract size from top and right");
|
||||
{
|
||||
const til::size scale{ 3, -6 };
|
||||
const til::rectangle expected{ 10, 26, 27, 40 };
|
||||
const auto actual = start - scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(RectangleInplaceSubtractionSize)
|
||||
{
|
||||
const til::rectangle start{ 10, 20, 30, 40 };
|
||||
|
||||
Log::Comment(L"1.) Subtract size from bottom and right");
|
||||
{
|
||||
auto actual = start;
|
||||
const til::size scale{ 3, 7 };
|
||||
const til::rectangle expected{ 10, 20, 27, 33 };
|
||||
actual -= scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Subtract size from top and left");
|
||||
{
|
||||
auto actual = start;
|
||||
const til::size scale{ -3, -7 };
|
||||
const til::rectangle expected{ 13, 27, 30, 40 };
|
||||
actual -= scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"3.) Subtract size from bottom and left");
|
||||
{
|
||||
auto actual = start;
|
||||
const til::size scale{ -3, 7 };
|
||||
const til::rectangle expected{ 13, 20, 30, 33 };
|
||||
actual -= scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"4.) Subtract size from top and right");
|
||||
{
|
||||
auto actual = start;
|
||||
const til::size scale{ 3, -6 };
|
||||
const til::rectangle expected{ 10, 26, 27, 40 };
|
||||
actual -= scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -168,21 +168,6 @@ class SizeTests
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(Boolean)
|
||||
{
|
||||
const til::size empty;
|
||||
VERIFY_IS_FALSE(empty);
|
||||
|
||||
const til::size heightOnly{ 0, 10 };
|
||||
VERIFY_IS_TRUE(heightOnly);
|
||||
|
||||
const til::size widthOnly{ 10, 0 };
|
||||
VERIFY_IS_TRUE(widthOnly);
|
||||
|
||||
const til::size both{ 10, 10 };
|
||||
VERIFY_IS_TRUE(both);
|
||||
}
|
||||
|
||||
TEST_METHOD(Addition)
|
||||
{
|
||||
Log::Comment(L"0.) Addition of two things that should be in bounds.");
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BitmapTests.cpp" />
|
||||
<ClCompile Include="OperatorTests.cpp" />
|
||||
<ClCompile Include="PointTests.cpp" />
|
||||
<ClCompile Include="RectangleTests.cpp" />
|
||||
<ClCompile Include="SizeTests.cpp" />
|
||||
|
||||
@@ -4,12 +4,10 @@
|
||||
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BitmapTests.cpp" />
|
||||
<ClCompile Include="SomeTests.cpp" />
|
||||
<ClCompile Include="..\precomp.cpp" />
|
||||
<ClCompile Include="u8u16convertTests.cpp" />
|
||||
<ClCompile Include="SizeTests.cpp" />
|
||||
<ClCompile Include="OperatorTests.cpp" />
|
||||
<ClCompile Include="ColorTests.cpp" />
|
||||
<ClCompile Include="PointTests.cpp" />
|
||||
<ClCompile Include="RectangleTests.cpp" />
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
#include "WexTestClass.h"
|
||||
|
||||
using namespace WEX::Common;
|
||||
using namespace WEX::Logging;
|
||||
|
||||
Reference in New Issue
Block a user