Compare commits

..

2 Commits

Author SHA1 Message Date
Mike Griese
7b9c8c7055 Fix C-M-z, C-M-x in Conpty (#4940)
## Summary of the Pull Request

This PR ensures that Conpty properly treats `^[^Z` and `^[^X` as
<kbd>Ctrl+Alt+z</kbd> and <kbd>Ctrl+Alt+x</kbd>, instead of <kbd>Ctrl+z</kbd>
and <kbd>Ctrl+x</kbd>.

## References

## PR Checklist
* [x] Closes #4201
* [x] I work here
* [x] Tests added/passed
* [n/a] Requires documentation to be updated

## Detailed Description of the Pull Request / Additional comments

`^Z` and `^X` are special control characters, SUB and CAN. For the output state
machine, these characters are supposed to be executed from _any_ state. However,
we shouldn't do this for the input engine. With the current behavior, these
characters are immediately executed regardless of what state we're in. That
means we end up synthesizing <kbd>Ctrl+z/x</kbd> for these characters. However,
for the InputStateMachine engine, when these characters are preceeded by `^[`
(ESC), we want to treat them as <kbd>Ctrl+Alt+z/x</kbd>.

This just adds a check in `StateMachine` to see if we should immediately execute
these characters from any state, similar to many of the other exceptions we
already perform in the StateMachine for the input engine.

## Validation Steps Performed
* ran tests
* checked `showkey -a` in gnome-terminal
* checked `showkey -a` in conhost
* checked `showkey -a` in vt pipeterm (conhost as a conpty terminal)
* checked `showkey -a` in Windows Terminal
2020-03-16 16:59:48 +00:00
Carlos Zamora
860affd608 Make commands in doc appear as code (#4933)
Co-authored-by: Carlos Zamora <cazamor@microsoft.com>
2020-03-16 09:44:53 -07:00
35 changed files with 320 additions and 1378 deletions

View File

@@ -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

View File

@@ -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"
{
}

View File

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

View File

@@ -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"
{
};

View File

@@ -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());
}
}

View File

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

View File

@@ -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
{

View File

@@ -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

View File

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

View File

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

View File

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

View File

@@ -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++;

View File

@@ -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));

View File

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

View File

@@ -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

View File

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

View File

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

View File

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

View File

@@ -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() };
}

View File

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

View File

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

View File

@@ -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:

View File

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

View File

@@ -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)
{

View File

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

View File

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

View File

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

View File

@@ -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();

View File

@@ -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();
}

View File

@@ -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();
}
}
};

View File

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

View File

@@ -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.");

View File

@@ -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" />

View File

@@ -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" />

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
#include "precomp.h"
#include "WexTestClass.h"
using namespace WEX::Common;
using namespace WEX::Logging;