mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-10 00:48:23 -06:00
Merge remote-tracking branch 'origin/main' into feature/llm
This commit is contained in:
commit
efc4aee50f
5
.github/actions/spelling/allow/allow.txt
vendored
5
.github/actions/spelling/allow/allow.txt
vendored
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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`
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user