diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json
index 9de676a965a..95ce2ff9990 100644
--- a/doc/cascadia/profiles.schema.json
+++ b/doc/cascadia/profiles.schema.json
@@ -1708,6 +1708,11 @@
"description": "Force the terminal to use the legacy input encoding. Certain keys in some applications may stop working when enabling this setting.",
"type": "boolean"
},
+ "experimental.useBackgroundImageForWindow": {
+ "default": false,
+ "description": "When set to true, the background image for the currently focused profile is expanded to encompass the entire window, beneath other panes.",
+ "type": "boolean"
+ },
"initialCols": {
"default": 120,
"description": "The number of columns displayed in the window upon first load. If \"launchMode\" is set to \"maximized\" (or \"maximizedFocus\"), this property is ignored.",
diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw
index b2133af1816..a0a653927e3 100644
--- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw
+++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw
@@ -709,6 +709,9 @@
Successfully exported terminal content
+
+ Find
+
Plain Text
diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp
index d744cb09fd2..47cfa70d7b2 100644
--- a/src/cascadia/TerminalApp/TabManagement.cpp
+++ b/src/cascadia/TerminalApp/TabManagement.cpp
@@ -209,6 +209,16 @@ namespace winrt::TerminalApp::implementation
}
});
+ newTabImpl->FindRequested([weakTab, weakThis{ get_weak() }]() {
+ auto page{ weakThis.get() };
+ auto tab{ weakTab.get() };
+
+ if (page && tab)
+ {
+ page->_Find();
+ }
+ });
+
auto tabViewItem = newTabImpl->TabViewItem();
_tabView.TabItems().Append(tabViewItem);
diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp
index c5c6cd2f5b2..64cbd16dc4f 100644
--- a/src/cascadia/TerminalApp/TerminalTab.cpp
+++ b/src/cascadia/TerminalApp/TerminalTab.cpp
@@ -1273,6 +1273,23 @@ namespace winrt::TerminalApp::implementation
exportTabMenuItem.Icon(exportTabSymbol);
}
+ Controls::MenuFlyoutItem findMenuItem;
+ {
+ // "Split Tab"
+ Controls::FontIcon findSymbol;
+ findSymbol.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
+ findSymbol.Glyph(L"\xF78B"); // SearchMedium
+
+ findMenuItem.Click([weakThis](auto&&, auto&&) {
+ if (auto tab{ weakThis.get() })
+ {
+ tab->_FindRequestedHandlers();
+ }
+ });
+ findMenuItem.Text(RS_(L"FindText"));
+ findMenuItem.Icon(findSymbol);
+ }
+
// Build the menu
Controls::MenuFlyout contextMenuFlyout;
Controls::MenuFlyoutSeparator menuSeparator;
@@ -1281,6 +1298,7 @@ namespace winrt::TerminalApp::implementation
contextMenuFlyout.Items().Append(duplicateTabMenuItem);
contextMenuFlyout.Items().Append(splitTabMenuItem);
contextMenuFlyout.Items().Append(exportTabMenuItem);
+ contextMenuFlyout.Items().Append(findMenuItem);
contextMenuFlyout.Items().Append(menuSeparator);
// GH#5750 - When the context menu is dismissed with ESC, toss the focus
@@ -1291,7 +1309,7 @@ namespace winrt::TerminalApp::implementation
// GH#10112 - if we're opening the tab renamer, don't
// immediately toss focus to the control. We don't want to steal
// focus from the tab renamer.
- if (!tab->_headerControl.InRename())
+ if (!tab->_headerControl.InRename() && !tab->GetActiveTerminalControl().SearchBoxEditInFocus())
{
tab->_RequestFocusActiveControlHandlers();
}
diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h
index 09922f2f1ad..f5ea3b0cd41 100644
--- a/src/cascadia/TerminalApp/TerminalTab.h
+++ b/src/cascadia/TerminalApp/TerminalTab.h
@@ -102,6 +102,7 @@ namespace winrt::TerminalApp::implementation
WINRT_CALLBACK(TabRaiseVisualBell, winrt::delegate<>);
WINRT_CALLBACK(DuplicateRequested, winrt::delegate<>);
WINRT_CALLBACK(SplitTabRequested, winrt::delegate<>);
+ WINRT_CALLBACK(FindRequested, winrt::delegate<>);
WINRT_CALLBACK(ExportTabRequested, winrt::delegate<>);
TYPED_EVENT(TaskbarProgressChanged, IInspectable, IInspectable);
diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp
index 3a0f6cba84d..fdb19c31acf 100644
--- a/src/cascadia/TerminalControl/TermControl.cpp
+++ b/src/cascadia/TerminalControl/TermControl.cpp
@@ -197,6 +197,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
+ // Method Description:
+ // Find if search box text edit currently is in focus
+ // Return Value:
+ // - true, if search box text edit is in focus
+ bool TermControl::SearchBoxEditInFocus() const
+ {
+ if (!_searchBox)
+ {
+ return false;
+ }
+
+ return _searchBox->TextBox().FocusState() == FocusState::Keyboard;
+ }
+
// Method Description:
// - Search text in text buffer. This is triggered if the user click
// search button or press enter.
diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h
index c76c4b58703..656e1f68451 100644
--- a/src/cascadia/TerminalControl/TermControl.h
+++ b/src/cascadia/TerminalControl/TermControl.h
@@ -90,6 +90,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void SearchMatch(const bool goForward);
+ bool SearchBoxEditInFocus() const;
+
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
bool OnMouseWheel(const Windows::Foundation::Point location, const int32_t delta, const bool leftButtonDown, const bool midButtonDown, const bool rightButtonDown);
diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl
index b235962a73f..8633931c7df 100644
--- a/src/cascadia/TerminalControl/TermControl.idl
+++ b/src/cascadia/TerminalControl/TermControl.idl
@@ -62,6 +62,7 @@ namespace Microsoft.Terminal.Control
void ScrollViewport(Int32 viewTop);
void CreateSearchBoxControl();
+ Boolean SearchBoxEditInFocus();
void SearchMatch(Boolean goForward);
diff --git a/src/host/getset.cpp b/src/host/getset.cpp
index e9057d12693..0b3356242ca 100644
--- a/src/host/getset.cpp
+++ b/src/host/getset.cpp
@@ -719,6 +719,10 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont
position.Y < 0));
// clang-format on
+ // MSFT: 15813316 - Try to use this SetCursorPosition call to inherit the cursor position.
+ auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
+ RETURN_IF_FAILED(gci.GetVtIo()->SetCursorPosition(position));
+
RETURN_IF_NTSTATUS_FAILED(buffer.SetCursorPosition(position, true));
LOG_IF_FAILED(ConsoleImeResizeCompStrView());
diff --git a/src/inc/til/at.h b/src/inc/til/at.h
index 592129634ae..28a715be86b 100644
--- a/src/inc/til/at.h
+++ b/src/inc/til/at.h
@@ -34,24 +34,24 @@ namespace til
// gsl::at will do the check again. As will .at(). And using [] will have a warning in audit.
// This template is explicitly disabled if T is of type gsl::span, as it would interfere with
// the overload below.
- template::value, int> = 0>
- constexpr auto at(T& cont, const size_t i) -> decltype(cont[cont.size()])
+ template
+ constexpr auto at(T&& cont, const I i) noexcept -> decltype(auto)
{
-#pragma warning(suppress : 26482) // Suppress bounds.2 check for indexing with constant expressions
-#pragma warning(suppress : 26446) // Suppress bounds.4 check for subscript operator.
-#pragma warning(suppress : 26445) // Suppress lifetime check for a reference to gsl::span or std::string_view
- return cont[i];
- }
-
#ifdef GSL_SPAN_H
- // This is an overload of til::at for span that access its backing buffer directly (UNCHECKED)
- template
- constexpr auto at(gsl::span span, const std::ptrdiff_t i) -> decltype(span[span.size()])
- {
+ if constexpr (details::is_span::value)
+ {
#pragma warning(suppress : 26481) // Suppress bounds.1 check for doing pointer arithmetic
#pragma warning(suppress : 26482) // Suppress bounds.2 check for indexing with constant expressions
#pragma warning(suppress : 26446) // Suppress bounds.4 check for subscript operator.
- return span.data()[i];
- }
+ return cont.data()[i];
+ }
+ else
#endif
+ {
+#pragma warning(suppress : 26482) // Suppress bounds.2 check for indexing with constant expressions
+#pragma warning(suppress : 26446) // Suppress bounds.4 check for subscript operator.
+#pragma warning(suppress : 26445) // Suppress lifetime check for a reference to gsl::span or std::string_view
+ return cont[i];
+ }
+ }
}
diff --git a/src/inc/til/bit.h b/src/inc/til/bit.h
index dc15d4bce44..a3ce7775a3b 100644
--- a/src/inc/til/bit.h
+++ b/src/inc/til/bit.h
@@ -5,10 +5,20 @@
namespace til
{
+ // bit_cast is a backport of the STL's std::bit_cast to C++17.
template, std::is_trivially_copyable, std::is_trivially_copyable>, int> = 0>
[[nodiscard]] constexpr To bit_cast(const From& _Val) noexcept
{
// TODO: Replace til::bit_cast and __builtin_bit_cast with std::bit_cast
return __builtin_bit_cast(To, _Val);
}
+
+ // When you cast a signed integer to an unsigned one, the compiler will use "sign extension"
+ // so that -1 translates to all bits being set, no matter the size of the target type.
+ // Sometimes you don't need or want that, which is when you can use this function.
+ template
+ [[nodiscard]] constexpr auto as_unsigned(const T& v) noexcept
+ {
+ return bit_cast>(v);
+ }
}
diff --git a/src/inc/til/hash.h b/src/inc/til/hash.h
index c30a30e4ade..a96f26dcf1b 100644
--- a/src/inc/til/hash.h
+++ b/src/inc/til/hash.h
@@ -3,6 +3,8 @@
#pragma once
+#include "bit.h"
+
namespace til
{
template
@@ -129,7 +131,11 @@ namespace til
{
// This runs murmurhash3's finalizer (fmix32/fmix64) on a single integer.
// It's fast, public domain and produces good results.
- auto h = static_cast(v);
+ //
+ // Using til::as_unsigned here allows the compiler to drop the first
+ // `>> 33` mix for all Ts which are >= 32 bits.
+ // The existence of sign extension shouldn't change hash quality.
+ size_t h = til::as_unsigned(v);
if constexpr (sizeof(size_t) == 4)
{
h ^= h >> 16;
diff --git a/src/inc/til/point.h b/src/inc/til/point.h
index f8eea99ccef..ea0d9a4416d 100644
--- a/src/inc/til/point.h
+++ b/src/inc/til/point.h
@@ -65,6 +65,11 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
return __builtin_memcmp(this, &rhs, sizeof(rhs)) != 0;
}
+ constexpr explicit operator bool() const noexcept
+ {
+ return (x > 0) & (y > 0);
+ }
+
constexpr bool operator<(const point other) const noexcept
{
return y < other.y || (y == other.y && x < other.x);
@@ -87,62 +92,61 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
constexpr point operator+(const point other) const
{
- return point{
- details::extract(::base::CheckAdd(x, other.x)),
- details::extract(::base::CheckAdd(y, other.y)),
- };
+ auto copy = *this;
+ copy += other;
+ return copy;
}
constexpr point& operator+=(const point other)
{
- *this = *this + other;
+ x = details::extract(::base::CheckAdd(x, other.x));
+ y = details::extract(::base::CheckAdd(y, other.y));
return *this;
}
constexpr point operator-(const point other) const
{
- return point{
- details::extract(::base::CheckSub(x, other.x)),
- details::extract(::base::CheckSub(y, other.y)),
- };
+ auto copy = *this;
+ copy -= other;
+ return copy;
}
constexpr point& operator-=(const point other)
{
- *this = *this - other;
+ x = details::extract(::base::CheckSub(x, other.x));
+ y = details::extract(::base::CheckSub(y, other.y));
return *this;
}
constexpr point operator*(const point other) const
{
- return point{
- details::extract(::base::CheckMul(x, other.x)),
- details::extract(::base::CheckMul(y, other.y)),
- };
+ auto copy = *this;
+ copy *= other;
+ return copy;
}
constexpr point& operator*=(const point other)
{
- *this = *this * other;
+ x = details::extract(::base::CheckMul(x, other.x));
+ y = details::extract(::base::CheckMul(y, other.y));
return *this;
}
constexpr point operator/(const point other) const
{
- return point{
- details::extract(::base::CheckDiv(x, other.x)),
- details::extract(::base::CheckDiv(y, other.y)),
- };
+ auto copy = *this;
+ copy /= other;
+ return copy;
}
constexpr point& operator/=(const point other)
{
- *this = *this / other;
+ x = details::extract(::base::CheckDiv(x, other.x));
+ y = details::extract(::base::CheckDiv(y, other.y));
return *this;
}
- template>>
- constexpr point operator*(const T scale) const
+ constexpr point operator*(const til::CoordType scale) const
{
return point{
details::extract(::base::CheckMul(x, scale)),
@@ -150,8 +154,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
};
}
- template>>
- constexpr point operator/(const T scale) const
+ constexpr point operator/(const til::CoordType scale) const
{
return point{
details::extract(::base::CheckDiv(x, scale)),
@@ -193,6 +196,19 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
return { x, y };
}
+
+ // til::point and POINT have the exact same layout at the time of writing,
+ // so this function lets you unsafely "view" this point as a POINT
+ // if you need to pass it to a Win32 function.
+ //
+ // Use as_win32_point() as sparingly as possible because it'll be a pain to hack
+ // it out of this code base once til::point and POINT aren't the same anymore.
+ // Prefer casting to POINT and back to til::point instead if possible.
+ POINT* as_win32_point() noexcept
+ {
+#pragma warning(suppress : 26490) // Don't use reinterpret_cast (type.1).
+ return std::launder(reinterpret_cast(this));
+ }
#endif
#ifdef DCOMMON_H_INCLUDED
diff --git a/src/inc/til/rect.h b/src/inc/til/rect.h
index fb51d1877f7..4d1fca122e9 100644
--- a/src/inc/til/rect.h
+++ b/src/inc/til/rect.h
@@ -47,6 +47,12 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
return __builtin_memcmp(this, &rhs, sizeof(rhs)) != 0;
}
+
+ explicit constexpr operator bool() const noexcept
+ {
+ return (left >= 0) & (top >= 0) &
+ (right >= left) & (bottom >= top);
+ }
};
constexpr inclusive_rect wrap_small_rect(const SMALL_RECT& rect) noexcept
@@ -416,22 +422,22 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
const rect l{ left, intersect.top, intersect.left, intersect.bottom };
const rect r{ intersect.right, intersect.top, right, intersect.bottom };
- if (!t.empty())
+ if (t)
{
result.push_back(t);
}
- if (!b.empty())
+ if (b)
{
result.push_back(b);
}
- if (!l.empty())
+ if (l)
{
result.push_back(l);
}
- if (!r.empty())
+ if (r)
{
result.push_back(r);
}
@@ -477,210 +483,43 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
#pragma endregion
#pragma region RECTANGLE VS SIZE
- // ADD will grow the total area of the rect. The sign is the direction to grow.
- constexpr rect operator+(const size size) const
- {
- // Fetch the pieces of the rect.
- auto l = left;
- auto r = right;
- auto t = top;
- auto b = bottom;
-
- // Fetch the scale factors we're using.
- const auto width = size.width;
- const auto height = size.height;
-
- // Since this is the add operation versus a size, the result
- // should grow the total rect area.
- // The sign determines which edge of the rect moves.
- // We use the magnitude as how far to move.
- if (width > 0)
- {
- // Adding the positive makes the rect "grow"
- // because right stretches outward (to the right).
- //
- // Example with adding width 3...
- // |-- x = origin
- // V
- // x---------| x------------|
- // | | | |
- // | | | |
- // |---------| |------------|
- // BEFORE AFTER
- r = details::extract(::base::CheckAdd(r, width));
- }
- else
- {
- // Adding the negative makes the rect "grow"
- // because left stretches outward (to the left).
- //
- // Example with adding width -3...
- // |-- x = origin
- // V
- // x---------| |--x---------|
- // | | | |
- // | | | |
- // |---------| |------------|
- // BEFORE AFTER
- l = details::extract(::base::CheckAdd(l, width));
- }
-
- if (height > 0)
- {
- // Adding the positive makes the rect "grow"
- // because bottom stretches outward (to the down).
- //
- // Example with adding height 2...
- // |-- x = origin
- // V
- // x---------| x---------|
- // | | | |
- // | | | |
- // |---------| | |
- // | |
- // |---------|
- // BEFORE AFTER
- b = details::extract(::base::CheckAdd(b, height));
- }
- else
- {
- // Adding the negative makes the rect "grow"
- // because top stretches outward (to the up).
- //
- // Example with adding height -2...
- // |-- x = origin
- // |
- // | |---------|
- // V | |
- // x---------| x |
- // | | | |
- // | | | |
- // |---------| |---------|
- // BEFORE AFTER
- t = details::extract(::base::CheckAdd(t, height));
- }
-
- return rect{ point{ l, t }, point{ r, b } };
- }
-
- constexpr rect& operator+=(const size size)
- {
- *this = *this + size;
- return *this;
- }
-
- // SUB will shrink the total area of the rect. The sign is the direction to shrink.
- constexpr rect operator-(const size size) const
- {
- // Fetch the pieces of the rect.
- auto l = left;
- auto r = right;
- auto t = top;
- auto b = bottom;
-
- // Fetch the scale factors we're using.
- const auto width = size.width;
- const auto height = size.height;
-
- // Since this is the subtract operation versus a size, the result
- // should shrink the total rect area.
- // The sign determines which edge of the rect moves.
- // We use the magnitude as how far to move.
- if (width > 0)
- {
- // Subtracting the positive makes the rect "shrink"
- // because right pulls inward (to the left).
- //
- // Example with subtracting width 3...
- // |-- x = origin
- // V
- // x---------| x------|
- // | | | |
- // | | | |
- // |---------| |------|
- // BEFORE AFTER
- r = details::extract(::base::CheckSub(r, width));
- }
- else
- {
- // Subtracting the negative makes the rect "shrink"
- // because left pulls inward (to the right).
- //
- // Example with subtracting width -3...
- // |-- x = origin
- // V
- // x---------| x |------|
- // | | | |
- // | | | |
- // |---------| |------|
- // BEFORE AFTER
- l = details::extract(::base::CheckSub(l, width));
- }
-
- if (height > 0)
- {
- // Subtracting the positive makes the rect "shrink"
- // because bottom pulls inward (to the up).
- //
- // Example with subtracting height 2...
- // |-- x = origin
- // V
- // x---------| x---------|
- // | | |---------|
- // | |
- // |---------|
- // BEFORE AFTER
- b = details::extract(::base::CheckSub(b, height));
- }
- else
- {
- // Subtracting the positive makes the rect "shrink"
- // because top pulls inward (to the down).
- //
- // Example with subtracting height -2...
- // |-- x = origin
- // V
- // x---------| x
- // | |
- // | | |---------|
- // |---------| |---------|
- // BEFORE AFTER
- t = details::extract(::base::CheckSub(t, height));
- }
-
- return rect{ point{ l, t }, point{ r, b } };
- }
-
- constexpr rect& operator-=(const size size)
- {
- *this = *this - size;
- return *this;
- }
// scale_up will scale the entire rect up by the size factor
- // This includes moving the origin.
constexpr rect scale_up(const size size) const
{
- const auto topLeft = point{ left, top } * size;
- const auto bottomRight = point{ right, bottom } * size;
- return rect{ topLeft, bottomRight };
+ return rect{
+ details::extract(::base::CheckMul(left, size.width)),
+ details::extract(::base::CheckMul(top, size.height)),
+ details::extract(::base::CheckMul(right, size.width)),
+ details::extract(::base::CheckMul(bottom, size.height)),
+ };
}
- // scale_down will scale the entire rect down by the size factor,
- // but rounds the bottom-right corner out.
- // This includes moving the origin.
+ // scale_down will scale the entire rect down by the size factor.
+ // The top/left corner is rounded down (floor) and
+ // the bottom/right corner is rounded up (ceil).
constexpr rect scale_down(const size size) const
{
- auto topLeft = point{ left, top };
- auto bottomRight = point{ right, bottom };
- topLeft = topLeft / size;
-
- // Move bottom right point into a size
- // Use size specialization of divide_ceil to round up against the size given.
- // Add leading addition to point to convert it back into a point.
- bottomRight = point{} + til::size{ right, bottom }.divide_ceil(size);
+ // The integer ceil division `((a - 1) / b) + 1` only works for numbers >0.
+ // Support for negative numbers wasn't deemed useful at this point.
+ if ((left < 0) | (top < 0) | (right < 0) | (bottom < 0) | (size.width <= 0) | (size.height <= 0))
+ {
+ throw std::invalid_argument{ "invalid til::rect::scale_down" };
+ }
- return rect{ topLeft, bottomRight };
+ // Imagine a terminal of 120x30 "cells" with each cell being
+ // 5x10 pixels large. The terminal is therefore 600x300 pixels.
+ // Given a rectangle in pixel coordinates, what's the rectangle in cell coordinates?
+ // Clearly this requires us to floor() top/left and ceil() bottom/right to cover all pixels.
+ // And thus:
+ // {17, 24, 31, 38}.scale_down({5, 10}) == {3, 2, 7, 4}
+ // {3, 2, 7, 4}.scale_up({5, 10}) == {15, 20, 35, 40}
+ return rect{
+ left / size.width,
+ top / size.height,
+ right != 0 ? (right - 1) / size.width + 1 : 0,
+ bottom != 0 ? (bottom - 1) / size.height + 1 : 0,
+ };
}
#pragma endregion
@@ -738,7 +577,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
constexpr size size() const noexcept
{
- return til::size{ width(), height() };
+ return { width(), height() };
}
constexpr bool empty() const noexcept
@@ -841,6 +680,32 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
return { left, top, right, bottom };
}
+
+ // til::rect and RECT have the exact same layout at the time of writing,
+ // so this function lets you unsafely "view" this rect as a RECT
+ // if you need to pass it to a Win32 function.
+ //
+ // Use as_win32_rect() as sparingly as possible because it'll be a pain to hack
+ // it out of this code base once til::rect and RECT aren't the same anymore.
+ // Prefer casting to RECT and back to til::rect instead if possible.
+ RECT* as_win32_rect() noexcept
+ {
+#pragma warning(suppress : 26490) // Don't use reinterpret_cast (type.1).
+ return std::launder(reinterpret_cast(this));
+ }
+
+ // til::rect and POINT[2] have the exact same layout at the time of writing,
+ // so this function lets you unsafely "view" this rect as a POINT[2] array
+ // if you need to pass it to a Win32 function.
+ //
+ // Use as_win32_points() as sparingly as possible because it'll be a pain to hack
+ // it out of this code base once til::rect and POINT[2] aren't the same anymore.
+ // Prefer casting to POINT and back to til::rect instead if possible.
+ POINT* as_win32_points() noexcept
+ {
+#pragma warning(suppress : 26490) // Don't use reinterpret_cast (type.1).
+ return std::launder(reinterpret_cast(this));
+ }
#endif
#ifdef DCOMMON_H_INCLUDED
@@ -902,11 +767,60 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
return wil::str_printf(L"(L:%d, T:%d, R:%d, B:%d) [W:%d, H:%d]", left, top, right, bottom, width(), height());
}
};
+
+ constexpr rect wrap_exclusive_small_rect(const SMALL_RECT& rect) noexcept
+ {
+ return { rect.Left, rect.Top, rect.Right, rect.Bottom };
+ }
+
+ constexpr SMALL_RECT unwrap_exclusive_small_rect(const rect& rect)
+ {
+ return {
+ gsl::narrow(rect.left),
+ gsl::narrow(rect.top),
+ gsl::narrow(rect.right),
+ gsl::narrow(rect.bottom),
+ };
+ }
}
#ifdef __WEX_COMMON_H__
namespace WEX::TestExecution
{
+ template<>
+ class VerifyOutputTraits
+ {
+ public:
+ static WEX::Common::NoThrowString ToString(const til::inclusive_rect& rect)
+ {
+ return WEX::Common::NoThrowString().Format(L"(L:%d, T:%d, R:%d, B:%d) [W:%d, H:%d]", rect.left, rect.top, rect.right, rect.bottom, rect.right - rect.left, rect.bottom - rect.top);
+ }
+ };
+
+ template<>
+ class VerifyCompareTraits
+ {
+ public:
+ static bool AreEqual(const til::inclusive_rect& expected, const til::inclusive_rect& actual) noexcept
+ {
+ return expected == actual;
+ }
+
+ static bool AreSame(const til::inclusive_rect& expected, const til::inclusive_rect& actual) noexcept
+ {
+ return &expected == &actual;
+ }
+
+ static bool IsLessThan(const til::inclusive_rect& expectedLess, const til::inclusive_rect& expectedGreater) = delete;
+
+ static bool IsGreaterThan(const til::inclusive_rect& expectedGreater, const til::inclusive_rect& expectedLess) = delete;
+
+ static bool IsNull(const til::inclusive_rect& object) noexcept
+ {
+ return object == til::inclusive_rect{};
+ }
+ };
+
template<>
class VerifyOutputTraits
{
diff --git a/src/inc/til/size.h b/src/inc/til/size.h
index df0b893067e..2b21abd41de 100644
--- a/src/inc/til/size.h
+++ b/src/inc/til/size.h
@@ -9,8 +9,21 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
struct size
{
- CoordType width = 0;
- CoordType height = 0;
+ // **** TRANSITIONAL ****
+ // The old COORD type uses uppercase X/Y member names.
+ // We'll migrate to lowercase width/height in the future.
+ union
+ {
+ CoordType width = 0;
+ CoordType X;
+ CoordType cx;
+ };
+ union
+ {
+ CoordType height = 0;
+ CoordType Y;
+ CoordType cy;
+ };
constexpr size() noexcept = default;
@@ -42,7 +55,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
constexpr explicit operator bool() const noexcept
{
- return width > 0 && height > 0;
+ return (width > 0) & (height > 0);
}
constexpr size operator+(const size other) const
@@ -80,7 +93,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
template>>
constexpr size scale(TilMath math, const T scale) const
{
- return til::size{
+ return {
math,
width * scale,
height * scale,
@@ -89,49 +102,17 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
constexpr size divide_ceil(const size other) const
{
- // Divide normally to get the floor.
- const size floor = *this / other;
-
- CoordType adjWidth = 0;
- CoordType adjHeight = 0;
-
- // Check for width remainder, anything not 0.
- // If we multiply the floored number with the other, it will equal
- // the old width if there was no remainder.
- if (other.width * floor.width != width)
+ // The integer ceil division `((a - 1) / b) + 1` only works for numbers >0.
+ // Support for negative numbers wasn't deemed useful at this point.
+ if ((width < 0) | (height < 0) | (other.width <= 0) | (other.height <= 0))
{
- // If there was any remainder,
- // Grow the magnitude by 1 in the
- // direction of the sign.
- if (floor.width >= 0)
- {
- ++adjWidth;
- }
- else
- {
- --adjWidth;
- }
+ throw std::invalid_argument{ "invalid til::size::divide_ceil" };
}
- // Check for height remainder, anything not 0.
- // If we multiply the floored number with the other, it will equal
- // the old width if there was no remainder.
- if (other.height * floor.height != height)
- {
- // If there was any remainder,
- // Grow the magnitude by 1 in the
- // direction of the sign.
- if (height >= 0)
- {
- ++adjHeight;
- }
- else
- {
- --adjHeight;
- }
- }
-
- return floor + size{ adjWidth, adjHeight };
+ return {
+ width != 0 ? (width - 1) / other.width + 1 : 0,
+ height != 0 ? (height - 1) / other.height + 1 : 0,
+ };
}
template
@@ -174,6 +155,19 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
return { width, height };
}
+
+ // til::size and SIZE have the exact same layout at the time of writing,
+ // so this function lets you unsafely "view" this size as a SIZE
+ // if you need to pass it to a Win32 function.
+ //
+ // Use as_win32_size() as sparingly as possible because it'll be a pain to hack
+ // it out of this code base once til::size and SIZE aren't the same anymore.
+ // Prefer casting to SIZE and back to til::size instead if possible.
+ SIZE* as_win32_size() noexcept
+ {
+#pragma warning(suppress : 26490) // Don't use reinterpret_cast (type.1).
+ return std::launder(reinterpret_cast(this));
+ }
#endif
#ifdef DCOMMON_H_INCLUDED
diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp
index 3cfc35c363a..78ba30aaf74 100644
--- a/src/renderer/atlas/AtlasEngine.api.cpp
+++ b/src/renderer/atlas/AtlasEngine.api.cpp
@@ -174,9 +174,15 @@ constexpr HRESULT vec2_narrow(U x, U y, AtlasEngine::vec2& out) noexcept
[[nodiscard]] HRESULT AtlasEngine::UpdateViewport(const SMALL_RECT srNewViewport) noexcept
{
- _api.cellCount.x = gsl::narrow_cast(srNewViewport.Right - srNewViewport.Left + 1);
- _api.cellCount.y = gsl::narrow_cast(srNewViewport.Bottom - srNewViewport.Top + 1);
- WI_SetFlag(_api.invalidations, ApiInvalidations::Size);
+ const u16x2 cellCount{
+ gsl::narrow_cast(srNewViewport.Right - srNewViewport.Left + 1),
+ gsl::narrow_cast(srNewViewport.Bottom - srNewViewport.Top + 1),
+ };
+ if (_api.cellCount != cellCount)
+ {
+ _api.cellCount = cellCount;
+ WI_SetFlag(_api.invalidations, ApiInvalidations::Size);
+ }
return S_OK;
}
diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp
index 37febe8dba5..dec159cbe5e 100644
--- a/src/renderer/dx/DxRenderer.cpp
+++ b/src/renderer/dx/DxRenderer.cpp
@@ -1395,7 +1395,10 @@ try
til::rect scrollArea{ _invalidMap.size() * _fontRenderData->GlyphCell() };
// Reduce the size of the rectangle by the scroll.
- scrollArea -= til::size{} - scrollPixels;
+ scrollArea.left = std::clamp(scrollArea.left + scrollPixels.x, scrollArea.left, scrollArea.right);
+ scrollArea.top = std::clamp(scrollArea.top + scrollPixels.y, scrollArea.top, scrollArea.bottom);
+ scrollArea.right = std::clamp(scrollArea.right + scrollPixels.x, scrollArea.left, scrollArea.right);
+ scrollArea.bottom = std::clamp(scrollArea.bottom + scrollPixels.y, scrollArea.top, scrollArea.bottom);
// Assign the area to the present storage
_presentScroll = scrollArea.to_win32_rect();
diff --git a/src/renderer/vt/paint.cpp b/src/renderer/vt/paint.cpp
index 4483c03c775..1e026e356af 100644
--- a/src/renderer/vt/paint.cpp
+++ b/src/renderer/vt/paint.cpp
@@ -348,17 +348,17 @@ using namespace Microsoft::Console::Types;
_bufferLine.clear();
_bufferLine.reserve(clusters.size());
- short totalWidth = 0;
+ size_t totalWidth = 0;
for (const auto& cluster : clusters)
{
_bufferLine.append(cluster.GetText());
- RETURN_IF_FAILED(ShortAdd(totalWidth, gsl::narrow(cluster.GetColumns()), &totalWidth));
+ totalWidth += cluster.GetColumns();
}
RETURN_IF_FAILED(VtEngine::_WriteTerminalAscii(_bufferLine));
// Update our internal tracker of the cursor's position
- _lastText.X += totalWidth;
+ _lastText.X += gsl::narrow(totalWidth);
return S_OK;
}
@@ -384,38 +384,29 @@ using namespace Microsoft::Console::Types;
_bufferLine.clear();
_bufferLine.reserve(clusters.size());
- short totalWidth = 0;
+ size_t totalWidth = 0;
for (const auto& cluster : clusters)
{
_bufferLine.append(cluster.GetText());
- RETURN_IF_FAILED(ShortAdd(totalWidth, static_cast(cluster.GetColumns()), &totalWidth));
+ totalWidth += cluster.GetColumns();
}
const auto cchLine = _bufferLine.size();
- auto foundNonspace = false;
- size_t lastNonSpace = 0;
- for (size_t i = 0; i < cchLine; i++)
- {
- if (_bufferLine.at(i) != L'\x20')
- {
- lastNonSpace = i;
- foundNonspace = true;
- }
- }
+ const auto spaceIndex = _bufferLine.find_last_not_of(L' ');
+ const auto foundNonspace = spaceIndex != decltype(_bufferLine)::npos;
+ const auto nonSpaceLength = foundNonspace ? spaceIndex + 1 : 0;
+
// Examples:
// - " ":
- // cch = 2, lastNonSpace = 0, foundNonSpace = false
- // cch-lastNonSpace = 2 -> good
- // cch-lastNonSpace-(0) = 2 -> good
+ // cch = 2, spaceIndex = 0, foundNonSpace = false
+ // cch-nonSpaceLength = 2
// - "A "
- // cch = 2, lastNonSpace = 0, foundNonSpace = true
- // cch-lastNonSpace = 2 -> bad
- // cch-lastNonSpace-(1) = 1 -> good
+ // cch = 2, spaceIndex = 0, foundNonSpace = true
+ // cch-nonSpaceLength = 1
// - "AA"
- // cch = 2, lastNonSpace = 1, foundNonSpace = true
- // cch-lastNonSpace = 1 -> bad
- // cch-lastNonSpace-(1) = 0 -> good
- const auto numSpaces = cchLine - lastNonSpace - (foundNonspace ? 1 : 0);
+ // cch = 2, spaceIndex = 1, foundNonSpace = true
+ // cch-nonSpaceLength = 0
+ const auto numSpaces = cchLine - nonSpaceLength;
// Optimizations:
// If there are lots of spaces at the end of the line, we can try to Erase
@@ -460,9 +451,7 @@ using namespace Microsoft::Console::Types;
const auto removeSpaces = !lineWrapped && (useEraseChar ||
_clearedAllThisFrame ||
(_newBottomLine && printingBottomLine && bgMatched));
- const auto cchActual = removeSpaces ?
- (cchLine - numSpaces) :
- cchLine;
+ const auto cchActual = removeSpaces ? nonSpaceLength : cchLine;
const auto columnsActual = removeSpaces ?
(totalWidth - numSpaces) :
diff --git a/src/terminal/adapter/FontBuffer.cpp b/src/terminal/adapter/FontBuffer.cpp
index 02a115a1a4e..98d13d78319 100644
--- a/src/terminal/adapter/FontBuffer.cpp
+++ b/src/terminal/adapter/FontBuffer.cpp
@@ -478,7 +478,7 @@ std::tuple FontBuffer::_calculateDimensions() const
}
}
-void FontBuffer::_packAndCenterBitPatterns()
+void FontBuffer::_packAndCenterBitPatterns() noexcept
{
// If this is a text font, we'll clip the bits up to the text width and
// center them within the full cell width. For a full cell font we'll just
diff --git a/src/terminal/adapter/FontBuffer.hpp b/src/terminal/adapter/FontBuffer.hpp
index 275dab0ad04..4b5ddbef220 100644
--- a/src/terminal/adapter/FontBuffer.hpp
+++ b/src/terminal/adapter/FontBuffer.hpp
@@ -48,7 +48,7 @@ namespace Microsoft::Console::VirtualTerminal
void _endOfCharacter();
std::tuple _calculateDimensions() const;
- void _packAndCenterBitPatterns();
+ void _packAndCenterBitPatterns() noexcept;
void _fillUnusedCharacters();
std::array _generateErrorGlyph();
diff --git a/src/terminal/adapter/InteractDispatch.cpp b/src/terminal/adapter/InteractDispatch.cpp
index 6c205d3508b..8da0f121a54 100644
--- a/src/terminal/adapter/InteractDispatch.cpp
+++ b/src/terminal/adapter/InteractDispatch.cpp
@@ -150,15 +150,10 @@ bool InteractDispatch::MoveCursor(const VTInt row, const VTInt col)
const auto coordCursorShort = til::unwrap_coord(coordCursor);
- // MSFT: 15813316 - Try to use this MoveCursor call to inherit the cursor position.
- auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
- RETURN_IF_FAILED(gci.GetVtIo()->SetCursorPosition(coordCursorShort));
-
// Finally, attempt to set the adjusted cursor position back into the console.
- auto& cursor = _api.GetTextBuffer().GetCursor();
- cursor.SetPosition(coordCursorShort);
- cursor.SetHasMoved(true);
- return true;
+ const auto api = gsl::not_null{ ServiceLocator::LocateGlobals().api };
+ auto& info = ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer();
+ return SUCCEEDED(api->SetConsoleCursorPositionImpl(info, coordCursorShort));
}
// Routine Description:
diff --git a/src/til/ut_til/PointTests.cpp b/src/til/ut_til/PointTests.cpp
index 5d4cd2cee00..fca594abf13 100644
--- a/src/til/ut_til/PointTests.cpp
+++ b/src/til/ut_til/PointTests.cpp
@@ -9,6 +9,13 @@ using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
+// Ensure the "safety" of til::point::as_win32_point
+static_assert(
+ sizeof(til::point) == sizeof(POINT) &&
+ alignof(til::point) == alignof(POINT) &&
+ offsetof(til::point, x) == offsetof(POINT, x) &&
+ offsetof(til::point, y) == offsetof(POINT, y));
+
class PointTests
{
TEST_CLASS(PointTests);
diff --git a/src/til/ut_til/RectangleTests.cpp b/src/til/ut_til/RectangleTests.cpp
index 5611dc8a4cb..b59862db5a6 100644
--- a/src/til/ut_til/RectangleTests.cpp
+++ b/src/til/ut_til/RectangleTests.cpp
@@ -7,6 +7,23 @@ using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
+// Ensure the "safety" of til::rect::as_win32_rect
+static_assert(
+ sizeof(til::rect) == sizeof(RECT) &&
+ alignof(til::rect) == alignof(RECT) &&
+ offsetof(til::rect, left) == offsetof(RECT, left) &&
+ offsetof(til::rect, top) == offsetof(RECT, top) &&
+ offsetof(til::rect, right) == offsetof(RECT, right) &&
+ offsetof(til::rect, bottom) == offsetof(RECT, bottom));
+// Ensure the "safety" of til::rect::as_win32_points
+static_assert(
+ sizeof(til::rect) == 2 * sizeof(POINT) &&
+ alignof(til::rect) == alignof(POINT) &&
+ offsetof(til::rect, left) == offsetof(POINT, x) &&
+ offsetof(til::rect, top) == offsetof(POINT, y) &&
+ offsetof(til::rect, right) == offsetof(POINT, x) + sizeof(POINT) &&
+ offsetof(til::rect, bottom) == offsetof(POINT, y) + sizeof(POINT));
+
class RectangleTests
{
TEST_CLASS(RectangleTests);
@@ -512,162 +529,6 @@ class RectangleTests
VERIFY_ARE_EQUAL(expected, start);
}
- TEST_METHOD(AdditionSize)
- {
- const til::rect start{ 10, 20, 30, 40 };
-
- Log::Comment(L"Add size to bottom and right");
- {
- const til::size scale{ 3, 7 };
- const til::rect expected{ 10, 20, 33, 47 };
- const auto actual = start + scale;
- VERIFY_ARE_EQUAL(expected, actual);
- }
-
- Log::Comment(L"Add size to top and left");
- {
- const til::size scale{ -3, -7 };
- const til::rect expected{ 7, 13, 30, 40 };
- const auto actual = start + scale;
- VERIFY_ARE_EQUAL(expected, actual);
- }
-
- Log::Comment(L"Add size to bottom and left");
- {
- const til::size scale{ -3, 7 };
- const til::rect expected{ 7, 20, 30, 47 };
- const auto actual = start + scale;
- VERIFY_ARE_EQUAL(expected, actual);
- }
-
- Log::Comment(L"Add size to top and right");
- {
- const til::size scale{ 3, -7 };
- const til::rect expected{ 10, 13, 33, 40 };
- const auto actual = start + scale;
- VERIFY_ARE_EQUAL(expected, actual);
- }
- }
-
- TEST_METHOD(AdditionSizeInplace)
- {
- const til::rect start{ 10, 20, 30, 40 };
-
- Log::Comment(L"Add size to bottom and right");
- {
- auto actual = start;
- const til::size scale{ 3, 7 };
- const til::rect expected{ 10, 20, 33, 47 };
- actual += scale;
- VERIFY_ARE_EQUAL(expected, actual);
- }
-
- Log::Comment(L"Add size to top and left");
- {
- auto actual = start;
- const til::size scale{ -3, -7 };
- const til::rect expected{ 7, 13, 30, 40 };
- actual += scale;
- VERIFY_ARE_EQUAL(expected, actual);
- }
-
- Log::Comment(L"Add size to bottom and left");
- {
- auto actual = start;
- const til::size scale{ -3, 7 };
- const til::rect expected{ 7, 20, 30, 47 };
- actual += scale;
- VERIFY_ARE_EQUAL(expected, actual);
- }
-
- Log::Comment(L"Add size to top and right");
- {
- auto actual = start;
- const til::size scale{ 3, -7 };
- const til::rect expected{ 10, 13, 33, 40 };
- actual += scale;
- VERIFY_ARE_EQUAL(expected, actual);
- }
- }
-
- TEST_METHOD(SubtractionSize)
- {
- const til::rect start{ 10, 20, 30, 40 };
-
- Log::Comment(L"Subtract size from bottom and right");
- {
- const til::size scale{ 3, 7 };
- const til::rect expected{ 10, 20, 27, 33 };
- const auto actual = start - scale;
- VERIFY_ARE_EQUAL(expected, actual);
- }
-
- Log::Comment(L"Subtract size from top and left");
- {
- const til::size scale{ -3, -7 };
- const til::rect expected{ 13, 27, 30, 40 };
- const auto actual = start - scale;
- VERIFY_ARE_EQUAL(expected, actual);
- }
-
- Log::Comment(L"Subtract size from bottom and left");
- {
- const til::size scale{ -3, 7 };
- const til::rect expected{ 13, 20, 30, 33 };
- const auto actual = start - scale;
- VERIFY_ARE_EQUAL(expected, actual);
- }
-
- Log::Comment(L"Subtract size from top and right");
- {
- const til::size scale{ 3, -6 };
- const til::rect expected{ 10, 26, 27, 40 };
- const auto actual = start - scale;
- VERIFY_ARE_EQUAL(expected, actual);
- }
- }
-
- TEST_METHOD(SubtractionSizeInplace)
- {
- const til::rect start{ 10, 20, 30, 40 };
-
- Log::Comment(L"Subtract size from bottom and right");
- {
- auto actual = start;
- const til::size scale{ 3, 7 };
- const til::rect expected{ 10, 20, 27, 33 };
- actual -= scale;
- VERIFY_ARE_EQUAL(expected, actual);
- }
-
- Log::Comment(L"Subtract size from top and left");
- {
- auto actual = start;
- const til::size scale{ -3, -7 };
- const til::rect expected{ 13, 27, 30, 40 };
- actual -= scale;
- VERIFY_ARE_EQUAL(expected, actual);
- }
-
- Log::Comment(L"Subtract size from bottom and left");
- {
- auto actual = start;
- const til::size scale{ -3, 7 };
- const til::rect expected{ 13, 20, 30, 33 };
- actual -= scale;
- VERIFY_ARE_EQUAL(expected, actual);
- }
-
- Log::Comment(L"Subtract size from top and right");
- {
- auto actual = start;
- const til::size scale{ 3, -6 };
- const til::rect expected{ 10, 26, 27, 40 };
- actual -= scale;
- VERIFY_ARE_EQUAL(expected, actual);
- }
- }
-
TEST_METHOD(ScaleUpSize)
{
const til::rect start{ 10, 20, 30, 40 };
diff --git a/src/til/ut_til/SizeTests.cpp b/src/til/ut_til/SizeTests.cpp
index a5b18d2e2c0..34122c69bc9 100644
--- a/src/til/ut_til/SizeTests.cpp
+++ b/src/til/ut_til/SizeTests.cpp
@@ -9,6 +9,13 @@ using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
+// Ensure the "safety" of til::point::as_win32_size
+static_assert(
+ sizeof(til::size) == sizeof(SIZE) &&
+ alignof(til::size) == alignof(SIZE) &&
+ offsetof(til::size, width) == offsetof(SIZE, cx) &&
+ offsetof(til::size, height) == offsetof(SIZE, cy));
+
class SizeTests
{
TEST_CLASS(SizeTests);
@@ -44,26 +51,6 @@ class SizeTests
VERIFY_ARE_EQUAL(height, sz.height);
}
- TEST_METHOD(MixedRawTypeConstruct)
- {
- const auto a = -5;
- const auto b = -10;
-
- Log::Comment(L"Case 1: til::CoordType/int");
- {
- const til::size sz{ a, b };
- VERIFY_ARE_EQUAL(a, sz.width);
- VERIFY_ARE_EQUAL(b, sz.height);
- }
-
- Log::Comment(L"Case 2: int/til::CoordType");
- {
- const til::size sz{ b, a };
- VERIFY_ARE_EQUAL(b, sz.width);
- VERIFY_ARE_EQUAL(a, sz.height);
- }
- }
-
TEST_METHOD(CoordConstruct)
{
COORD coord{ -5, 10 };
@@ -371,11 +358,11 @@ class SizeTests
const til::size sz{ -10, -5 };
const til::size divisor{ 3, 2 };
- // -10 / 3 is -3.333, rounded up is -4.
- // -5 / 2 is -2.5, rounded up is -3.
- const til::size expected{ -4, -3 };
+ auto fn = [&]() {
+ sz.divide_ceil(divisor);
+ };
- VERIFY_ARE_EQUAL(expected, sz.divide_ceil(divisor));
+ VERIFY_THROWS(fn(), std::invalid_argument);
}
}
diff --git a/src/types/inc/Utf16Parser.hpp b/src/types/inc/Utf16Parser.hpp
index f48bdc0cda2..5ce8eba4e55 100644
--- a/src/types/inc/Utf16Parser.hpp
+++ b/src/types/inc/Utf16Parser.hpp
@@ -15,17 +15,9 @@ Author(s):
#pragma once
#include
-#include
-#include
class Utf16Parser final
{
-private:
- static constexpr unsigned short IndicatorBitCount = 6;
- static constexpr unsigned short WcharShiftAmount = sizeof(wchar_t) * 8 - IndicatorBitCount;
- static constexpr std::bitset LeadingSurrogateMask = { 54 }; // 110 110 indicates a leading surrogate
- static constexpr std::bitset TrailingSurrogateMask = { 55 }; // 110 111 indicates a trailing surrogate
-
public:
static std::vector> Parse(std::wstring_view wstr);
static std::wstring_view ParseNext(std::wstring_view wstr) noexcept;
@@ -36,11 +28,9 @@ class Utf16Parser final
// - wch - the wchar to check
// Return Value:
// - true if wch is a leading surrogate, false otherwise
- static inline bool IsLeadingSurrogate(const wchar_t wch) noexcept
+ static constexpr bool IsLeadingSurrogate(const wchar_t wch) noexcept
{
- const wchar_t bits = wch >> WcharShiftAmount;
- const std::bitset possBits = { bits };
- return (possBits ^ LeadingSurrogateMask).none();
+ return wch >= 0xD800 && wch <= 0xDBFF;
}
// Routine Description:
@@ -49,10 +39,8 @@ class Utf16Parser final
// - wch - the wchar to check
// Return Value:
// - true if wch is a trailing surrogate, false otherwise
- static inline bool IsTrailingSurrogate(const wchar_t wch) noexcept
+ static constexpr bool IsTrailingSurrogate(const wchar_t wch) noexcept
{
- const wchar_t bits = wch >> WcharShiftAmount;
- const std::bitset possBits = { bits };
- return (possBits ^ TrailingSurrogateMask).none();
+ return wch >= 0xDC00 && wch <= 0xDFFF;
}
};