Merge remote-tracking branch 'origin/main' into feature/llm

This commit is contained in:
Console Service Bot 2025-06-17 01:32:28 +00:00
commit efc4aee50f
4 changed files with 115 additions and 64 deletions

View File

@ -26,8 +26,8 @@ dze
dzhe
Emacspeak
Fitt
FTCS
flac
FTCS
gantt
gfm
ghe
@ -68,8 +68,8 @@ Powerline
ptys
pwn
pwshw
QOL
qof
QOL
qps
Quarternary
quickfix
@ -90,6 +90,7 @@ stakeholders
subpage
sustainability
sxn
Tencent
TLDR
tonos
toolset

View File

@ -488,6 +488,39 @@ void CascadiaSettings::_validateAllSchemesExist()
}
}
static bool _validateSingleMediaResource(std::wstring_view resource)
{
// URI
try
{
winrt::Windows::Foundation::Uri resourceUri{ resource };
if (!resourceUri)
{
return false;
}
const auto scheme{ resourceUri.SchemeName() };
// Only file: URIs and ms-* URIs are permissible. http, https, ftp, gopher, etc. are not.
return til::equals_insensitive_ascii(scheme, L"file") || til::starts_with_insensitive_ascii(scheme, L"ms-");
}
catch (...)
{
// fall through
}
// Not a URI? Try a path.
try
{
std::filesystem::path resourcePath{ resource };
return std::filesystem::exists(resourcePath);
}
catch (...)
{
// fall through
}
return false;
}
// Method Description:
// - Ensures that all specified images resources (icons and background images) are valid URIs.
// This does not verify that the icon or background image files are encoded as an image.
@ -501,24 +534,26 @@ void CascadiaSettings::_validateAllSchemesExist()
// we find any invalid icon images.
void CascadiaSettings::_validateMediaResources()
{
auto invalidBackground{ false };
auto invalidIcon{ false };
auto warnInvalidBackground{ false };
auto warnInvalidIcon{ false };
for (auto profile : _allProfiles)
{
if (const auto path = profile.DefaultAppearance().ExpandedBackgroundImagePath(); !path.empty())
{
// Attempt to convert the path to a URI, the ctor will throw if it's invalid/unparseable.
// This covers file paths on the machine, app data, URLs, and other resource paths.
try
if (!_validateSingleMediaResource(path))
{
winrt::Windows::Foundation::Uri imagePath{ path };
}
catch (...)
{
// reset background image path
profile.DefaultAppearance().ClearBackgroundImagePath();
invalidBackground = true;
if (profile.DefaultAppearance().HasBackgroundImagePath())
{
// Only warn and delete if the user set this at the top level (do not warn for fragments, just clear it)
warnInvalidBackground = true;
profile.DefaultAppearance().ClearBackgroundImagePath();
}
else
{
// reset background image path (set it to blank as an override for any fragment value)
profile.DefaultAppearance().BackgroundImagePath({});
}
}
}
@ -526,17 +561,18 @@ void CascadiaSettings::_validateMediaResources()
{
if (const auto path = profile.UnfocusedAppearance().ExpandedBackgroundImagePath(); !path.empty())
{
// Attempt to convert the path to a URI, the ctor will throw if it's invalid/unparseable.
// This covers file paths on the machine, app data, URLs, and other resource paths.
try
if (!_validateSingleMediaResource(path))
{
winrt::Windows::Foundation::Uri imagePath{ path };
}
catch (...)
{
// reset background image path
profile.UnfocusedAppearance().ClearBackgroundImagePath();
invalidBackground = true;
if (profile.UnfocusedAppearance().HasBackgroundImagePath())
{
warnInvalidBackground = true;
profile.UnfocusedAppearance().ClearBackgroundImagePath();
}
else
{
// reset background image path (set it to blank as an override for any fragment value)
profile.UnfocusedAppearance().BackgroundImagePath({});
}
}
}
}
@ -552,24 +588,27 @@ void CascadiaSettings::_validateMediaResources()
if (const auto icon = profile.Icon(); icon.size() > 2 && icon != HideIconValue)
{
const auto iconPath{ wil::ExpandEnvironmentStringsW<std::wstring>(icon.c_str()) };
try
if (!_validateSingleMediaResource(iconPath))
{
winrt::Windows::Foundation::Uri imagePath{ iconPath };
}
catch (...)
{
profile.ClearIcon();
invalidIcon = true;
if (profile.HasIcon())
{
warnInvalidIcon = true;
profile.ClearIcon();
}
else
{
profile.Icon({});
}
}
}
}
if (invalidBackground)
if (warnInvalidBackground)
{
_warnings.Append(SettingsLoadWarnings::InvalidBackgroundImage);
}
if (invalidIcon)
if (warnInvalidIcon)
{
_warnings.Append(SettingsLoadWarnings::InvalidIcon);
}

View File

@ -4,18 +4,18 @@
```mermaid
graph TD
RenderThread["RenderThread (base/thread.cpp)\n<small>calls Renderer::PaintFrame() x times per sec</small>"]
Renderer["Renderer (base/renderer.cpp)\n<small>breaks the text buffer down into GDI-oriented graphics\nprimitives (#quot;change brush to color X#quot;, #quot;draw string Y#quot;, ...)</small>"]
RenderEngineBase[/"RenderEngineBase\n(base/RenderEngineBase.cpp)\n<small>abstracts 24 LOC 👻</small>"\]
RenderThread["RenderThread (base/thread.cpp)<br><small>calls Renderer::PaintFrame() x times per sec</small>"]
Renderer["Renderer (base/renderer.cpp)<br><small>breaks the text buffer down into GDI-oriented graphics<br>primitives (#quot;change brush to color X#quot;, #quot;draw string Y#quot;, ...)</small>"]
RenderEngineBase[/"RenderEngineBase<br>(base/RenderEngineBase.cpp)<br><small>abstracts 24 LOC 👻</small>"\]
GdiEngine["GdiEngine (gdi/...)"]
subgraph AtlasEngine["AtlasEngine (atlas/...)"]
AtlasEngine.cpp["AtlasEngine.cpp\n<small>Implements IRenderEngine text rendering API\nbreaks GDI graphics primitives down into DWRITE_GLYPH_RUNs</small>"]
AtlasEngine.api.cpp["AtlasEngine.api.cpp\n<small>Implements the parts run inside the console\nlock (many IRenderEngine setters)<small>"]
AtlasEngine.r.cpp["AtlasEngine.r.cpp\n<small>Implements the parts run\noutside of the console lock<small>"]
Backend.cpp["Backend.cpp\n<small>Implements common functionality/helpers</small>"]
BackendD2D.cpp["BackendD2D.cpp\n<small>Pure Direct2D text renderer (for low latency\nremote desktop and older/no GPUs)</small>"]
BackendD3D.cpp["BackendD3D.cpp\n<small>Custom, performant text renderer\nwith our own glyph cache</small>"]
AtlasEngine.cpp["AtlasEngine.cpp<br><small>Implements IRenderEngine text rendering API<br>breaks GDI graphics primitives down into DWRITE_GLYPH_RUNs</small>"]
AtlasEngine.api.cpp["AtlasEngine.api.cpp<br><small>Implements the parts run inside the console<br>lock (many IRenderEngine setters)<small>"]
AtlasEngine.r.cpp["AtlasEngine.r.cpp<br><small>Implements the parts run<br>outside of the console lock<small>"]
Backend.cpp["Backend.cpp<br><small>Implements common functionality/helpers</small>"]
BackendD2D.cpp["BackendD2D.cpp<br><small>Pure Direct2D text renderer (for low latency<br>remote desktop and older/no GPUs)</small>"]
BackendD3D.cpp["BackendD3D.cpp<br><small>Custom, performant text renderer<br>with our own glyph cache</small>"]
end
RenderThread --> Renderer
@ -63,8 +63,8 @@ graph TD
```mermaid
graph TD
Render --> _drawCursorPart1["_drawCursorPart1\nruns before _drawText\ndraws cursors that are behind the text"]
Render --> _drawCursorPart2["_drawCursorPart2\nruns after _drawText\ndraws inverted cursors"]
Render --> _drawCursorPart1["_drawCursorPart1<br>runs before _drawText<br>draws cursors that are behind the text"]
Render --> _drawCursorPart2["_drawCursorPart2<br>runs after _drawText<br>draws inverted cursors"]
_drawCursorPart1 -.->|_cursorRects| _drawCursorPart2
```
@ -81,26 +81,26 @@ graph TD
foreachFont --> foreachGlyph(("for each glyph"))
foreachGlyph --> foreachGlyph
foreachGlyph --> _glyphAtlasMap[("font/glyph-pair lookup in\nglyph cache hashmap")]
foreachGlyph --> _glyphAtlasMap[("font/glyph-pair lookup in<br>glyph cache hashmap")]
_glyphAtlasMap --> drawGlyph
drawGlyph --> _appendQuad["_appendQuad\n<small>stages the glyph for later drawing</small>"]
drawGlyph --> _appendQuad["_appendQuad<br><small>stages the glyph for later drawing</small>"]
_glyphAtlasMap --> _appendQuad
subgraph drawGlyph["if glyph is missing"]
_drawGlyph["_drawGlyph\n<small>(defers to _drawSoftFontGlyph for soft fonts)</small>"]
_drawGlyph["_drawGlyph<br><small>(defers to _drawSoftFontGlyph for soft fonts)</small>"]
_drawGlyph -.->|if glpyh cache is full| _drawGlyphPrepareRetry
_drawGlyphPrepareRetry --> _flushQuads["_flushQuads\n<small>draws the current state\ninto the render target</small>"]
_flushQuads --> _recreateInstanceBuffers["_recreateInstanceBuffers\n<small>allocates a GPU buffer\nfor our glyph instances</small>"]
_drawGlyphPrepareRetry --> _resetGlyphAtlas["_resetGlyphAtlas\n<small>clears the glyph texture</small>"]
_resetGlyphAtlas --> _resizeGlyphAtlas["_resizeGlyphAtlas\n<small>resizes the glyph texture if it's still small</small>"]
_drawGlyphPrepareRetry --> _flushQuads["_flushQuads<br><small>draws the current state<br>into the render target</small>"]
_flushQuads --> _recreateInstanceBuffers["_recreateInstanceBuffers<br><small>allocates a GPU buffer<br>for our glyph instances</small>"]
_drawGlyphPrepareRetry --> _resetGlyphAtlas["_resetGlyphAtlas<br><small>clears the glyph texture</small>"]
_resetGlyphAtlas --> _resizeGlyphAtlas["_resizeGlyphAtlas<br><small>resizes the glyph texture if it's still small</small>"]
_drawGlyph -.->|if it's a DECDHL glyph| _splitDoubleHeightGlyph["_splitDoubleHeightGlyph\n<small>DECDHL glyphs are split up into their\ntop/bottom halves to emulate clip rects</small>"]
_drawGlyph -.->|if it's a DECDHL glyph| _splitDoubleHeightGlyph["_splitDoubleHeightGlyph<br><small>DECDHL glyphs are split up into their<br>top/bottom halves to emulate clip rects</small>"]
end
foreachGlyph -.-> _drawTextOverlapSplit["_drawTextOverlapSplit\n<small>splits overly wide glyphs up into smaller chunks to support\nforeground color changes within the ligature</small>"]
foreachGlyph -.-> _drawTextOverlapSplit["_drawTextOverlapSplit<br><small>splits overly wide glyphs up into smaller chunks to support<br>foreground color changes within the ligature</small>"]
foreachRow -.->|if gridlines exist| _drawGridlineRow["_drawGridlineRow\n<small>draws underlines, etc.</small>"]
foreachRow -.->|if gridlines exist| _drawGridlineRow["_drawGridlineRow<br><small>draws underlines, etc.</small>"]
```
### `_drawSelection`

View File

@ -711,13 +711,28 @@ TextAttribute Implementation::_textAttributeFromAtom(TfGuidAtom atom) const
TF_DISPLAYATTRIBUTE da;
THROW_IF_FAILED(dai->GetAttributeInfo(&da));
if (da.crText.type != TF_CT_NONE)
// The Tencent QQPinyin IME creates TF_CT_COLORREF attributes with a color of 0x000000 (black).
// We respect their wish, which results in the preview text being invisible.
// (Note that sending this COLORREF is incorrect, and not a bug in our handling.)
//
// After some discussion, we realized that an IME which sets only one color but not
// the others is likely not properly tested anyway, so we reject those cases.
// After all, what behavior do we expect, if the IME sends e.g. foreground=blue,
// without knowing whether our terminal theme already uses a blue background?
if (da.crText.type == da.crBk.type && da.crText.type == da.crLine.type)
{
attr.SetForeground(_colorFromDisplayAttribute(da.crText));
}
if (da.crBk.type != TF_CT_NONE)
{
attr.SetBackground(_colorFromDisplayAttribute(da.crBk));
if (da.crText.type != TF_CT_NONE)
{
attr.SetForeground(_colorFromDisplayAttribute(da.crText));
}
if (da.crBk.type != TF_CT_NONE)
{
attr.SetBackground(_colorFromDisplayAttribute(da.crBk));
}
if (da.crLine.type != TF_CT_NONE)
{
attr.SetUnderlineColor(_colorFromDisplayAttribute(da.crLine));
}
}
if (da.lsStyle >= TF_LS_NONE && da.lsStyle <= TF_LS_SQUIGGLE)
{
@ -737,10 +752,6 @@ TextAttribute Implementation::_textAttributeFromAtom(TfGuidAtom atom) const
{
attr.SetUnderlineStyle(UnderlineStyle::DoublyUnderlined);
}
if (da.crLine.type != TF_CT_NONE)
{
attr.SetUnderlineColor(_colorFromDisplayAttribute(da.crLine));
}
return attr;
}