Compare commits

...

14 Commits

Author SHA1 Message Date
Leonard Hecker
47f1167af2 AtlasEngine: Fix uneven baselines when scaling glyphs (#14039)
This commit changes the glyph scale algorithm to prefer aligning glyphs to
their baseline. This improves the visual appearance of simulated italic glyphs.
However wide Emojis in narrow cells now look slightly worse without centering.

Closes #13987

## Validation Steps Performed
* Use FiraCode which has no italic variant and instead uses simulated italics
* Write italic text
* Baseline is consistent 

(cherry picked from commit 97dc5c8d75)
Service-Card-Id: 85767343
Service-Version: 1.16
2022-09-21 17:30:29 -05:00
Leonard Hecker
45310d77a8 Fix potential lags/deadlocks during tab close (#14041)
`ConptyClosePseudoConsole` blocks until OpenConsole exits.
This is problematic for the changes in 666c446, which stopped calling that
function on a background thread to solve a race condition. This commit fixes
the potential lags/deadlocks from waiting on OpenConsole's exit, by adding
`ConptyClosePseudoConsoleNoWait` which only closes the IO handles and allows
OpenConsole to exit naturally. This uncovered another potential deadlock
in `ServiceLocator::RundownAndExit` which might call itself recursively.

Closes #14032

## Validation Steps Performed
* Print tons of text and concurrently close the tab.
  Tab closes, OpenConsole/pwsh exits instantly 
* Use `Enter-VsDevShell` and close the tab.
  Tab closes instantly, OpenConsole/pwsh exits after ~5 seconds 

(cherry picked from commit 274bdb31da)
Service-Card-Id: 85767341
Service-Version: 1.16
2022-09-21 17:30:28 -05:00
Dustin L. Howett
3717fae714 OpenHere: Replace explorer window lookup code w/ site lookup (#14048)
When we first introduced the shell extension, it didn't work properly
for some folders (such as the Desktop, or perhaps any "background"
click) due to a bug in Windows. We worked around that bug with the help
of an awesome community member, who contributed code that would pull up
the topmost Explorer window and query its location.

That Windows bug was eventually fixed, but we still had trouble with
items appearing correctly. On Windows 11, the Open in Terminal menu item
appears and disappears at random when you right-click the desktop, but
it always appears when you right-click a folder. It sometimes appears
for Quick Access, even though it shouldn't.

We tried to fix that in #13206, but the fix caused more issues than it
solved. We reverted it for 1.15 and 1.16.

At the end of the day, it turns out that getting the path from the
toplevel explorer window is fragile. Fortunately, the shell does offer
us a way to get that information: the site chain.

This pull request replaces GetPathFromExplorer() with an implementation
of `IObjectWithSite`, which allows us to use the site chain to look up
from whence a context menu request was initiated. It also makes item
lookup generally more robust.

*  Tested on Windows 11
  *  Desktop
  *  Folder Background
  *  Folder Selected
  *  Quick Access (does not appear)
  *  This PC (does not appear)
*  Tested on Windows 10
  *  Desktop
  *  Folder Background
  *  Folder Selected
  *  Quick Access (does not appear)
  *  This PC (does not appear)

References #13206
References #13523
Closes #12578

Co-authored-by: John Lueders <johnlue@microsoft.com>
(cherry picked from commit 5027c8031d)
Service-Card-Id: 85788409
Service-Version: 1.16
2022-09-21 12:43:45 -05:00
Leonard Hecker
9d0346c2b3 AtlasEngine: Fix cursor invalidation (#14038)
There's a different behavior regarding cursors between conhost and Windows
Terminal. In case of the latter we don't necessarily call `PaintCursor`
during cursor movement, because the cursor blinker never stops "blinking".

Closes #14028

## Validation Steps Performed
* Enter text until after the line wraps
* Hold backspace until the line unwraps
* No leftover cursor on the second line 

(cherry picked from commit 08096b2343)
Service-Card-Id: 85767353
Service-Version: 1.16
2022-09-21 11:15:07 -05:00
Leonard Hecker
8876417f87 Fix font size rounding in the settings UI (#14040)
This fixes an issue with c51bb3a, where some fractional font
sizes are displayed as something like 13.600000000001.

Closes #14024

## Validation Steps Performed
* Enter a font size of 13.6 and save
* NumberBox displays "13.6" 

(cherry picked from commit f79276b21b)
Service-Card-Id: 85740785
Service-Version: 1.16
2022-09-21 11:15:06 -05:00
Leonard Hecker
9310db572d AtlasEngine: Fix bugs around bitmap font rendering (#14014)
This commit fixes several issues:
* Some fonts set a line-gap even though they behave as if they
  don't want any line-gaps. Since Terminals don't really have
  any gaps anyways, it'll now not be taken into account anymore.
* Center alignment breaks bitmap glyphs which expect left-alignment.
* Automatic "opsz" axis makes Terminus TTF's italic glyphs look quite
  weird. I disabled this feature as we might not need it anyways.

A complete fix depends on #14013
Closes #14006

## Validation Steps Performed
* Use Terminus TTF at 13.5pt
* Print UTF-8-demo.txt
* No gaps between block characters 

(cherry picked from commit bea13bddf1)
Service-Card-Id: 85767355
Service-Version: 1.16
2022-09-21 11:15:05 -05:00
Dustin L. Howett
9ec3e799ed Regenerate CodepointWidthDetector from Unicode 15.0 (#14001)
Closes #13999

(cherry picked from commit 88c6d7ff5b)
Service-Card-Id: 85767362
Service-Version: 1.16
2022-09-21 11:15:04 -05:00
Leonard Hecker
73ea629c18 Add support for fractional font sizes (#14013)
After this commit a user may specify fractional font sizes.
Support was only implemented for AtlasEngine however.
DxEngine continues to use rounded (integer) font sizes.

Closes #6678

## Validation Steps Performed
* Install a bitmap font that requires fractional font sizes
  (e.g. Terminus TTF, https://files.ax86.net/terminus-ttf/)
* Set font size to something integer (e.g. 14pt)
  Glyphs are blurry 
* Set font size to something fractional (e.g. 13.5pt)
  Glyphs are crisp 

(cherry picked from commit c51bb3a7a6)
Service-Card-Id: 85708971
Service-Version: 1.16
2022-09-16 17:39:20 -05:00
Leonard Hecker
8f013d7ae8 Stop DoSing users with renderer errors (#13995)
If a rendering engine constantly throws error we'll effectively
denial-of-service our users by drowning them in warning popups.
This commit fixes the issue by limiting the retries in all cases.

Issue found in: #13985

## Validation Steps Performed
* Add a `THROW_HR(E_INVALIDARG);` in `AtlasEngine::StartPaint()`
* Launch Windows Terminal
* Only one warning popup shows up 
* Rendering is disabled until one clicks "resume" 

(cherry picked from commit 81e2bc98d1)
Service-Card-Id: 85653502
Service-Version: 1.16
2022-09-16 11:31:43 -05:00
Leonard Hecker
c2c5f410f9 AtlasEngine: Fix a crash when drawing double width rows (#13966)
The `TileHashMap` refresh via `makeNewest()` in `StartPaint()` depends
on us filling the entire `cellGlyphMapping` row with valid data.
This commit makes sure to initialize the `cellGlyphMapping` buffer.
Additionally it clears the rest of the row with whitespace
until proper `LineRendition` support is added.

Closes #13962

## Validation Steps Performed
* vttest's "Test of double-sized characters" stops crashing 
* No weird leftover characters 

(cherry picked from commit 16aa79d78d)
Service-Card-Id: 85653281
Service-Version: 1.16
2022-09-16 11:31:41 -05:00
Leonard Hecker
5e9147e994 AtlasEngine: Properly detect shader model 4 support (#13994)
Direct3D 10.0 and 10.1 only have optional support for shader model 4.
This commit fixes our assumption that it's always present by checking
`ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x` first.

Closes #13985

## Validation Steps Performed
* Set feature level to 10.1 via `dxcpl`
* `CheckFeatureSupport` is called and doesn't throw 

(cherry picked from commit e2b2d9b92c)
Service-Card-Id: 85653388
Service-Version: 1.16
2022-09-16 11:31:39 -05:00
Mårten Rånge
b899d49a26 Relax shader strictness in RELEASE mode (#13998)
Disables strictness and warnings as errors for custom pixel shaders in
RELEASE. Windows terminal is not telling the user why the shader won't
compile which makes it very frustrating for the shader hacker.

After trying the recent preview none of my shaders loaded anymore in
Windows Terminal Preview which made me very sad. I had no idea what was
wrong with them. After cloning the git repo, building it, fighting an
issue that prevent DEBUG SDK from being used I finally was able to
identify some issues that were blocking my shaders.

> error X3556: integer modulus may be much slower, try using uints if possible.
> error X4000: use of potentially uninitialized variable (rayCylinder)

While the first one is a good warning I don't think it is an error and
the tools I use didn't flag it so was hard to know.

The second one I was staring at the code and was unable to identify what
exactly was causing the issues, I fumbled with the code a few times and
just felt the fun drain away.

IMHO: I want it to be fun to develop shaders for windows terminal.
Fighting invisible errors are not fun. I am not after building
production shaders for Windows Terminal, I want some cool effects. So
while I am as a .NET developer always runs with Warning as errors I
don't think it's the right option here. Especially since Windows
Terminal doesn't tell what is the problem.

However, I understand if the shaders you ship with Windows Terminal
should be free of errors and silly mistakes, so I kept the stricter
setting in DEBUG mode.

## Validation Steps Performed

Loaded Windows Terminal in RELEASE and DEBUG mode and validated that
RELEASE mode had reduced strictness but DEBUG retained the previous more
restrictive mode.

(cherry picked from commit b4b6636b49)
Service-Card-Id: 85660397
Service-Version: 1.16
2022-09-16 11:31:38 -05:00
Dustin L. Howett
b73e39ce17 vPack: fix submit branch, add github token from consvc (#13959)
Our Windows branch name changed, and I took this opportunity to resolve
an issue where vpack builds would occasionally fail due to GitHub rate
limiting the Azure DevOps IP addresses.

(cherry picked from commit 3958c938af)
Service-Card-Id: 85567589
Service-Version: 1.16
2022-09-09 13:30:40 -05:00
Dustin Howett
bccd97257e PGO: train 1.16 separately 2022-09-09 12:28:56 -05:00
45 changed files with 372 additions and 338 deletions

View File

@@ -4,7 +4,7 @@
"collection": "microsoft",
"project": "OS",
"repo": "os.2020",
"name": "official/rs_wdx_dxp_windev",
"name": "official/rs_we_adept_e4d2",
"workitem": "38106206",
"CheckinFiles": [
{
@@ -21,4 +21,4 @@
"sendOnErrorOnly": "False"
}
]
}
}

View File

@@ -9,7 +9,7 @@
<PropertyGroup>
<!-- Optional, defaults to main. Name of the branch which will be used for calculating branch point. -->
<PGOBranch>main</PGOBranch>
<PGOBranch>release-1.16</PGOBranch>
<!-- Mandatory. Name of the NuGet package which will contain PGO databases for consumption by build system. -->
<PGOPackageName>Microsoft.Internal.Windows.Terminal.PGODatabase</PGOPackageName>

View File

@@ -708,6 +708,7 @@ jobs:
description: VPack for the Windows Terminal Application
pushPkgName: WindowsTerminal.app
owner: conhost
githubToken: $(GitHubTokenForVpackProvenance)
- task: PublishPipelineArtifact@1
displayName: 'Copy VPack Manifest to Drop'
inputs:

View File

@@ -244,7 +244,7 @@
"default": 12,
"description": "Size of the font in points.",
"minimum": 1,
"type": "integer"
"type": "number"
},
"weight": {
"default": "normal",
@@ -2252,7 +2252,7 @@
"default": 12,
"description": "[deprecated] Define 'size' within the 'font' object instead.",
"minimum": 1,
"type": "integer",
"type": "number",
"deprecated": true
},
"fontWeight": {

View File

@@ -169,7 +169,7 @@ static bool RegisterTermClass(HINSTANCE hInstance) noexcept
}
HwndTerminal::HwndTerminal(HWND parentHwnd) :
_desiredFont{ L"Consolas", 0, DEFAULT_FONT_WEIGHT, { 0, 14 }, CP_UTF8 },
_desiredFont{ L"Consolas", 0, DEFAULT_FONT_WEIGHT, 14, CP_UTF8 },
_actualFont{ L"Consolas", 0, DEFAULT_FONT_WEIGHT, { 0, 14 }, CP_UTF8, false },
_uiaProvider{ nullptr },
_currentDpi{ USER_DEFAULT_SCREEN_DPI },
@@ -799,7 +799,7 @@ void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR font
publicTerminal->_terminal->SetCursorStyle(static_cast<DispatchTypes::CursorStyle>(theme.CursorStyle));
publicTerminal->_desiredFont = { fontFamily, 0, DEFAULT_FONT_WEIGHT, { 0, fontSize }, CP_UTF8 };
publicTerminal->_desiredFont = { fontFamily, 0, DEFAULT_FONT_WEIGHT, static_cast<float>(fontSize), CP_UTF8 };
publicTerminal->_UpdateFont(newDpi);
// When the font changes the terminal dimensions need to be recalculated since the available row and column

View File

@@ -25,37 +25,25 @@ static constexpr std::wstring_view VerbName{ L"WindowsTerminalOpenHere" };
// failure from an earlier HRESULT.
HRESULT OpenTerminalHere::Invoke(IShellItemArray* psiItemArray,
IBindCtx* /*pBindContext*/)
try
{
wil::com_ptr_nothrow<IShellItem> psi;
RETURN_IF_FAILED(GetBestLocationFromSelectionOrSite(psiItemArray, psi.put()));
if (!psi)
{
return S_FALSE;
}
wil::unique_cotaskmem_string pszName;
if (psiItemArray == nullptr)
{
// get the current path from explorer.exe
const auto path = this->_GetPathFromExplorer();
// no go, unable to get a reasonable path
if (path.empty())
{
return S_FALSE;
}
pszName = wil::make_cotaskmem_string(path.c_str(), path.length());
}
else
{
DWORD count;
psiItemArray->GetCount(&count);
winrt::com_ptr<IShellItem> psi;
RETURN_IF_FAILED(psiItemArray->GetItemAt(0, psi.put()));
RETURN_IF_FAILED(psi->GetDisplayName(SIGDN_FILESYSPATH, &pszName));
}
RETURN_IF_FAILED(psi->GetDisplayName(SIGDN_FILESYSPATH, &pszName));
{
wil::unique_process_information _piClient;
STARTUPINFOEX siEx{ 0 };
siEx.StartupInfo.cb = sizeof(STARTUPINFOEX);
auto cmdline{ wil::str_printf<std::wstring>(LR"-("%s" -d %s)-", GetWtExePath().c_str(), QuoteAndEscapeCommandlineArg(pszName.get()).c_str()) };
std::wstring cmdline;
RETURN_IF_FAILED(wil::str_printf_nothrow(cmdline, LR"-("%s" -d %s)-", GetWtExePath().c_str(), QuoteAndEscapeCommandlineArg(pszName.get()).c_str()));
RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(
nullptr, // lpApplicationName
cmdline.data(),
@@ -72,6 +60,7 @@ HRESULT OpenTerminalHere::Invoke(IShellItemArray* psiItemArray,
return S_OK;
}
CATCH_RETURN()
HRESULT OpenTerminalHere::GetToolTip(IShellItemArray* /*psiItemArray*/,
LPWSTR* ppszInfoTip)
@@ -109,21 +98,14 @@ HRESULT OpenTerminalHere::GetState(IShellItemArray* psiItemArray,
// We however don't need to bother with any of that.
// If no item was selected when the context menu was opened and Explorer
// is not at a valid path (e.g. This PC or Quick Access), we should hide
// is not at a valid location (e.g. This PC or Quick Access), we should hide
// the verb from the context menu.
if (psiItemArray == nullptr)
{
const auto path = this->_GetPathFromExplorer();
*pCmdState = path.empty() ? ECS_HIDDEN : ECS_ENABLED;
}
else
{
winrt::com_ptr<IShellItem> psi;
psiItemArray->GetItemAt(0, psi.put());
SFGAOF attributes;
const bool isFileSystemItem = (psi->GetAttributes(SFGAO_FILESYSTEM, &attributes) == S_OK);
*pCmdState = isFileSystemItem ? ECS_ENABLED : ECS_HIDDEN;
}
wil::com_ptr_nothrow<IShellItem> psi;
RETURN_IF_FAILED(GetBestLocationFromSelectionOrSite(psiItemArray, psi.put()));
SFGAOF attributes;
const bool isFileSystemItem = psi && (psi->GetAttributes(SFGAO_FILESYSTEM, &attributes) == S_OK);
*pCmdState = isFileSystemItem ? ECS_ENABLED : ECS_HIDDEN;
return S_OK;
}
@@ -160,102 +142,47 @@ HRESULT OpenTerminalHere::EnumSubCommands(IEnumExplorerCommand** ppEnum)
return E_NOTIMPL;
}
std::wstring OpenTerminalHere::_GetPathFromExplorer() const
IFACEMETHODIMP OpenTerminalHere::SetSite(IUnknown* site) noexcept
{
using namespace std;
using namespace winrt;
wstring path;
HRESULT hr = NOERROR;
auto hwnd = ::GetForegroundWindow();
if (hwnd == nullptr)
{
return path;
}
TCHAR szName[MAX_PATH] = { 0 };
::GetClassName(hwnd, szName, MAX_PATH);
if (0 == StrCmp(szName, L"WorkerW") ||
0 == StrCmp(szName, L"Progman"))
{
//special folder: desktop
hr = ::SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, SHGFP_TYPE_CURRENT, szName);
if (FAILED(hr))
{
return path;
}
path = szName;
return path;
}
if (0 != StrCmp(szName, L"CabinetWClass"))
{
return path;
}
com_ptr<IShellWindows> shell;
try
{
shell = create_instance<IShellWindows>(CLSID_ShellWindows, CLSCTX_ALL);
}
catch (...)
{
//look like try_create_instance is not available no more
}
if (shell == nullptr)
{
return path;
}
com_ptr<IDispatch> disp;
wil::unique_variant variant;
variant.vt = VT_I4;
com_ptr<IWebBrowserApp> browser;
// look for correct explorer window
for (variant.intVal = 0;
shell->Item(variant, disp.put()) == S_OK;
variant.intVal++)
{
com_ptr<IWebBrowserApp> tmp;
if (FAILED(disp->QueryInterface(tmp.put())))
{
disp = nullptr; // get rid of DEBUG non-nullptr warning
continue;
}
HWND tmpHWND = NULL;
hr = tmp->get_HWND(reinterpret_cast<SHANDLE_PTR*>(&tmpHWND));
if (hwnd == tmpHWND)
{
browser = tmp;
disp = nullptr; // get rid of DEBUG non-nullptr warning
break; //found
}
disp = nullptr; // get rid of DEBUG non-nullptr warning
}
if (browser != nullptr)
{
wil::unique_bstr url;
hr = browser->get_LocationURL(&url);
if (FAILED(hr))
{
return path;
}
wstring sUrl(url.get(), SysStringLen(url.get()));
DWORD size = MAX_PATH;
hr = ::PathCreateFromUrl(sUrl.c_str(), szName, &size, NULL);
if (SUCCEEDED(hr))
{
path = szName;
}
}
return path;
site_ = site;
return S_OK;
}
IFACEMETHODIMP OpenTerminalHere::GetSite(REFIID riid, void** site) noexcept
{
RETURN_IF_FAILED(site_.query_to(riid, site));
return S_OK;
}
HRESULT OpenTerminalHere::GetLocationFromSite(IShellItem** location) const noexcept
{
wil::com_ptr_nothrow<IServiceProvider> serviceProvider;
RETURN_IF_FAILED(site_.query_to(serviceProvider.put()));
wil::com_ptr_nothrow<IFolderView> folderView;
RETURN_IF_FAILED(serviceProvider->QueryService(SID_SFolderView, IID_PPV_ARGS(folderView.put())));
RETURN_IF_FAILED(folderView->GetFolder(IID_PPV_ARGS(location)));
return S_OK;
}
HRESULT OpenTerminalHere::GetBestLocationFromSelectionOrSite(IShellItemArray* psiArray, IShellItem** location) const noexcept
{
wil::com_ptr_nothrow<IShellItem> psi;
if (psiArray)
{
DWORD count{};
RETURN_IF_FAILED(psiArray->GetCount(&count));
if (count) // Sometimes we get an array with a count of 0. Fall back to the site chain.
{
RETURN_IF_FAILED(psiArray->GetItemAt(0, psi.put()));
}
}
if (!psi)
{
RETURN_IF_FAILED(GetLocationFromSite(psi.put()));
}
RETURN_HR_IF(S_FALSE, !psi);
RETURN_IF_FAILED(psi.copy_to(location));
return S_OK;
}

View File

@@ -22,8 +22,6 @@ Author(s):
--*/
#pragma once
#include <conattrs.hpp>
using namespace Microsoft::WRL;
struct
@@ -34,7 +32,7 @@ struct
#else // DEV
__declspec(uuid("52065414-e077-47ec-a3ac-1cc5455e1b54"))
#endif
OpenTerminalHere : public RuntimeClass<RuntimeClassFlags<ClassicCom | InhibitFtmBase>, IExplorerCommand>
OpenTerminalHere : public RuntimeClass<RuntimeClassFlags<ClassicCom | InhibitFtmBase>, IExplorerCommand, IObjectWithSite>
{
#pragma region IExplorerCommand
STDMETHODIMP Invoke(IShellItemArray* psiItemArray,
@@ -52,9 +50,16 @@ struct
STDMETHODIMP GetCanonicalName(GUID* pguidCommandName);
STDMETHODIMP EnumSubCommands(IEnumExplorerCommand** ppEnum);
#pragma endregion
#pragma region IObjectWithSite
IFACEMETHODIMP SetSite(IUnknown* site) noexcept;
IFACEMETHODIMP GetSite(REFIID riid, void** site) noexcept;
#pragma endregion
private:
std::wstring _GetPathFromExplorer() const;
HRESULT GetLocationFromSite(IShellItem** location) const noexcept;
HRESULT GetBestLocationFromSelectionOrSite(IShellItemArray* psiArray, IShellItem** location) const noexcept;
wil::com_ptr_nothrow<IUnknown> site_;
};
CoCreatableClass(OpenTerminalHere);

View File

@@ -554,6 +554,15 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
_hPC.reset(); // tear down the pseudoconsole (this is like clicking X on a console window)
// CloseHandle() on pipes blocks until any current WriteFile()/ReadFile() has returned.
// CancelSynchronousIo prevents us from deadlocking ourselves.
// At this point in Close(), _inPipe won't be used anymore since the UI parts are torn down.
// _outPipe is probably still stuck in ReadFile() and might currently be written to.
if (_hOutputThread)
{
CancelSynchronousIo(_hOutputThread.get());
}
_inPipe.reset(); // break the pipes
_outPipe.reset();
@@ -615,10 +624,17 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
DWORD read{};
const auto readFail{ !ReadFile(_outPipe.get(), _buffer.data(), gsl::narrow_cast<DWORD>(_buffer.size()), &read, nullptr) };
// When we call CancelSynchronousIo() in Close() this is the branch that's taken and gets us out of here.
if (_isStateAtOrBeyond(ConnectionState::Closing))
{
return 0;
}
if (readFail) // reading failed (we must check this first, because read will also be 0.)
{
const auto lastError = GetLastError();
if (lastError != ERROR_BROKEN_PIPE && !_isStateAtOrBeyond(ConnectionState::Closing))
if (lastError != ERROR_BROKEN_PIPE)
{
// EXIT POINT
_indicateExitWithStatus(HRESULT_FROM_WIN32(lastError)); // print a message
@@ -631,12 +647,6 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
const auto result{ til::u8u16(std::string_view{ _buffer.data(), read }, _u16Str, _u8State) };
if (FAILED(result))
{
if (_isStateAtOrBeyond(ConnectionState::Closing))
{
// This termination was expected.
return 0;
}
// EXIT POINT
_indicateExitWithStatus(result); // print a message
_transitionToState(ConnectionState::Failed);

View File

@@ -13,7 +13,7 @@
namespace wil
{
// These belong in WIL upstream, so when we reingest the change that has them we'll get rid of ours.
using unique_static_pseudoconsole_handle = wil::unique_any<HPCON, decltype(&::ConptyClosePseudoConsole), ::ConptyClosePseudoConsole>;
using unique_static_pseudoconsole_handle = wil::unique_any<HPCON, decltype(&::ConptyClosePseudoConsole), ::ConptyClosePseudoConsoleNoWait>;
}
namespace winrt::Microsoft::Terminal::TerminalConnection::implementation

View File

@@ -95,7 +95,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
Control::IControlAppearance unfocusedAppearance,
TerminalConnection::ITerminalConnection connection) :
_connection{ connection },
_desiredFont{ DEFAULT_FONT_FACE, 0, DEFAULT_FONT_WEIGHT, { 0, DEFAULT_FONT_SIZE }, CP_UTF8 },
_desiredFont{ DEFAULT_FONT_FACE, 0, DEFAULT_FONT_WEIGHT, DEFAULT_FONT_SIZE, CP_UTF8 },
_actualFont{ DEFAULT_FONT_FACE, 0, DEFAULT_FONT_WEIGHT, { 0, DEFAULT_FONT_SIZE }, CP_UTF8, false }
{
_EnsureStaticInitialization();
@@ -859,15 +859,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - fontSize: The size of the font.
// Return Value:
// - Returns true if you need to call _refreshSizeUnderLock().
bool ControlCore::_setFontSizeUnderLock(int fontSize)
bool ControlCore::_setFontSizeUnderLock(float fontSize)
{
// Make sure we have a non-zero font size
const auto newSize = std::max(fontSize, 1);
const auto newSize = std::max(fontSize, 1.0f);
const auto fontFace = _settings->FontFace();
const auto fontWeight = _settings->FontWeight();
_actualFont = { fontFace, 0, fontWeight.Weight, { 0, newSize }, CP_UTF8, false };
_desiredFont = { fontFace, 0, fontWeight.Weight, newSize, CP_UTF8 };
_actualFont = { fontFace, 0, fontWeight.Weight, _desiredFont.GetEngineSize(), CP_UTF8, false };
_actualFontFaceName = { fontFace };
_desiredFont = { _actualFont };
const auto before = _actualFont.GetSize();
_updateFont();
@@ -893,11 +893,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - Adjust the font size of the terminal control.
// Arguments:
// - fontSizeDelta: The amount to increase or decrease the font size by.
void ControlCore::AdjustFontSize(int fontSizeDelta)
void ControlCore::AdjustFontSize(float fontSizeDelta)
{
const auto lock = _terminal->LockForWriting();
if (_setFontSizeUnderLock(_desiredFont.GetEngineSize().Y + fontSizeDelta))
if (_setFontSizeUnderLock(_desiredFont.GetFontSize() + fontSizeDelta))
{
_refreshSizeUnderLock();
}
@@ -1211,10 +1211,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return static_cast<uint16_t>(_actualFont.GetWeight());
}
til::size ControlCore::FontSizeInDips() const
winrt::Windows::Foundation::Size ControlCore::FontSizeInDips() const
{
const til::size fontSize{ _actualFont.GetSize() };
return fontSize.scale(til::math::rounding, 1.0f / ::base::saturated_cast<float>(_compositionScale));
const auto fontSize = _actualFont.GetSize();
const auto scale = 1.0f / static_cast<float>(_compositionScale);
return {
fontSize.width * scale,
fontSize.height * scale,
};
}
TerminalConnection::ConnectionState ControlCore::ConnectionState() const

View File

@@ -76,10 +76,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void SizeChanged(const double width, const double height);
void ScaleChanged(const double scale);
void AdjustFontSize(int fontSizeDelta);
void AdjustFontSize(float fontSizeDelta);
void ResetFontSize();
FontInfo GetFont() const;
til::size FontSizeInDips() const;
winrt::Windows::Foundation::Size FontSizeInDips() const;
winrt::Windows::Foundation::Size FontSize() const noexcept;
winrt::hstring FontFaceName() const noexcept;
@@ -282,7 +282,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
std::unique_ptr<til::throttled_func_trailing<>> _updatePatternLocations;
std::shared_ptr<ThrottledFuncTrailing<Control::ScrollPositionChangedArgs>> _updateScrollBar;
bool _setFontSizeUnderLock(int fontSize);
bool _setFontSizeUnderLock(float fontSize);
void _updateFont(const bool initialUpdate = false);
void _refreshSizeUnderLock();
void _updateSelectionUI();

View File

@@ -105,7 +105,7 @@ namespace Microsoft.Terminal.Control
void ClearHoveredCell();
void ResetFontSize();
void AdjustFontSize(Int32 fontSizeDelta);
void AdjustFontSize(Single fontSizeDelta);
void SizeChanged(Double width, Double height);
void ScaleChanged(Double scale);

View File

@@ -301,7 +301,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto touchdownPoint = *_singleClickTouchdownPos;
const auto dx = pixelPosition.X - touchdownPoint.X;
const auto dy = pixelPosition.Y - touchdownPoint.Y;
const auto w = fontSizeInDips.width;
const auto w = fontSizeInDips.Width;
const auto distanceSquared = dx * dx + dy * dy;
const auto maxDistanceSquared = w * w / 16; // (w / 4)^2
@@ -337,16 +337,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto fontSizeInDips{ _core->FontSizeInDips() };
// Get the difference between the point we've dragged to and the start of the touch.
const auto dy = static_cast<double>(newTouchPoint.Y - anchor.Y);
const auto dy = static_cast<float>(newTouchPoint.Y - anchor.Y);
// Start viewport scroll after we've moved more than a half row of text
if (std::abs(dy) > (fontSizeInDips.height / 2.0))
if (std::abs(dy) > (fontSizeInDips.Height / 2.0f))
{
// Multiply by -1, because moving the touch point down will
// create a positive delta, but we want the viewport to move up,
// so we'll need a negative scroll amount (and the inverse for
// panning down)
const auto numRows = dy / -fontSizeInDips.height;
const auto numRows = dy / -fontSizeInDips.Height;
const auto currentOffset = _core->ScrollOffset();
const auto newValue = numRows + currentOffset;
@@ -459,7 +459,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// scrolling event.
// Arguments:
// - mouseDelta: the mouse wheel delta that triggered this event.
void ControlInteractivity::_mouseTransparencyHandler(const double mouseDelta)
void ControlInteractivity::_mouseTransparencyHandler(const int32_t mouseDelta) const
{
// Transparency is on a scale of [0.0,1.0], so only increment by .01.
const auto effectiveDelta = mouseDelta < 0 ? -.01 : .01;
@@ -471,9 +471,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// event.
// Arguments:
// - mouseDelta: the mouse wheel delta that triggered this event.
void ControlInteractivity::_mouseZoomHandler(const double mouseDelta)
void ControlInteractivity::_mouseZoomHandler(const int32_t mouseDelta) const
{
const auto fontDelta = mouseDelta < 0 ? -1 : 1;
const auto fontDelta = mouseDelta < 0 ? -1.0f : 1.0f;
_core->AdjustFontSize(fontDelta);
}
@@ -483,7 +483,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - mouseDelta: the mouse wheel delta that triggered this event.
// - pixelPosition: the location of the mouse during this event
// - isLeftButtonPressed: true iff the left mouse button was pressed during this event.
void ControlInteractivity::_mouseScrollHandler(const double mouseDelta,
void ControlInteractivity::_mouseScrollHandler(const int32_t mouseDelta,
const Core::Point pixelPosition,
const bool isLeftButtonPressed)
{

View File

@@ -132,9 +132,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
unsigned int _numberOfClicks(Core::Point clickPos, Timestamp clickTime);
void _updateSystemParameterSettings() noexcept;
void _mouseTransparencyHandler(const double mouseDelta);
void _mouseZoomHandler(const double mouseDelta);
void _mouseScrollHandler(const double mouseDelta,
void _mouseTransparencyHandler(const int32_t mouseDelta) const;
void _mouseZoomHandler(const int32_t mouseDelta) const;
void _mouseScrollHandler(const int32_t mouseDelta,
const Core::Point terminalPosition,
const bool isLeftButtonPressed);

View File

@@ -36,7 +36,7 @@ namespace Microsoft.Terminal.Control
Boolean UseAtlasEngine { get; };
String FontFace { get; };
Int32 FontSize { get; };
Single FontSize { get; };
Windows.UI.Text.FontWeight FontWeight { get; };
String Padding { get; };
Windows.Foundation.Collections.IMap<String, UInt32> FontFeatures { get; };

View File

@@ -1517,7 +1517,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - Adjust the font size of the terminal control.
// Arguments:
// - fontSizeDelta: The amount to increase or decrease the font size by.
void TermControl::AdjustFontSize(int fontSizeDelta)
void TermControl::AdjustFontSize(float fontSizeDelta)
{
_core.AdjustFontSize(fontSizeDelta);
}
@@ -2082,8 +2082,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// The family is only used to determine if the font is truetype or
// not, but DX doesn't use that info at all.
// The Codepage is additionally not actually used by the DX engine at all.
FontInfo actualFont = { fontFace, 0, fontWeight.Weight, { 0, fontSize }, CP_UTF8, false };
FontInfoDesired desiredFont = { actualFont };
FontInfoDesired desiredFont{ fontFace, 0, fontWeight.Weight, fontSize, CP_UTF8 };
FontInfo actualFont{ fontFace, 0, fontWeight.Weight, desiredFont.GetEngineSize(), CP_UTF8, false };
// Create a DX engine and initialize it with our font and DPI. We'll
// then use it to measure how much space the requested rows and columns

View File

@@ -82,7 +82,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ScrollViewport(int viewTop);
void AdjustFontSize(int fontSizeDelta);
void AdjustFontSize(float fontSizeDelta);
void ResetFontSize();
til::point GetFontSize() const;

View File

@@ -71,7 +71,7 @@ namespace Microsoft.Terminal.Control
void SearchMatch(Boolean goForward);
void AdjustFontSize(Int32 fontSizeDelta);
void AdjustFontSize(Single fontSizeDelta);
void ResetFontSize();
void ToggleShaderEffects();

View File

@@ -379,7 +379,7 @@ void Terminal::ExpandSelectionToWord()
_selection->pivot = _selection->start;
_selection->end = buffer.GetWordEnd(_selection->end, _wordDelimiters);
// if we're targetting both endpoints, instead just target "end"
// if we're targeting both endpoints, instead just target "end"
if (WI_IsFlagSet(_selectionEndpoint, SelectionEndpoint::Start) && WI_IsFlagSet(_selectionEndpoint, SelectionEndpoint::End))
{
_selectionEndpoint = SelectionEndpoint::End;

View File

@@ -91,6 +91,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
InitializeComponent();
{
using namespace winrt::Windows::Globalization::NumberFormatting;
// > .NET rounds to 12 significant digits when displaying doubles, so we will [...]
// ...obviously not do that, because this is an UI element for humans. This prevents
// issues when displaying 32-bit floats, because WinUI is unaware about their existence.
SignificantDigitsNumberRounder rounder;
rounder.SignificantDigits(6);
// BODGY: Depends on WinUI internals.
_fontSizeBox().NumberFormatter().as<DecimalFormatter>().NumberRounder(rounder);
}
INITIALIZE_BINDABLE_ENUM_SETTING(CursorShape, CursorStyle, winrt::Microsoft::Terminal::Core::CursorStyle, L"Profile_CursorShape", L"Content");
INITIALIZE_BINDABLE_ENUM_SETTING(AdjustIndistinguishableColors, AdjustIndistinguishableColors, winrt::Microsoft::Terminal::Core::AdjustTextMode, L"Profile_AdjustIndistinguishableColors", L"Content");
INITIALIZE_BINDABLE_ENUM_SETTING_REVERSE_ORDER(BackgroundImageStretchMode, BackgroundImageStretchMode, winrt::Windows::UI::Xaml::Media::Stretch, L"Profile_BackgroundImageStretchMode", L"Content");

View File

@@ -34,7 +34,7 @@ namespace Microsoft.Terminal.Settings.Editor
IHostedInWindow WindowRoot; // necessary to send the right HWND into the file picker dialogs.
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(String, FontFace);
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Int32, FontSize);
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Single, FontSize);
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Windows.UI.Text.FontWeight, FontWeight);
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(String, ColorSchemeName);

View File

@@ -93,7 +93,8 @@
HasSettingValue="{x:Bind Appearance.HasFontSize, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.FontSizeOverrideSource, Mode=OneWay}"
Visibility="{x:Bind Appearance.IsDefault, Mode=OneWay}">
<muxc:NumberBox x:Uid="Profile_FontSizeBox"
<muxc:NumberBox x:Name="_fontSizeBox"
x:Uid="Profile_FontSizeBox"
AcceptsExpression="False"
LargeChange="10"
Maximum="128"

View File

@@ -24,6 +24,7 @@
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Globalization.h>
#include <winrt/Windows.Globalization.NumberFormatting.h>
#include <winrt/Windows.System.h>
#include <winrt/Windows.UI.h>
#include <winrt/Windows.UI.Core.h>

View File

@@ -117,7 +117,7 @@ private:
////////////////////////////////////////////////////////////////////////////////
#define ADJUST_FONT_SIZE_ARGS(X) \
X(int32_t, Delta, "delta", false, 0)
X(float, Delta, "delta", false, 0)
////////////////////////////////////////////////////////////////////////////////
#define SEND_INPUT_ARGS(X) \

View File

@@ -187,7 +187,7 @@ namespace Microsoft.Terminal.Settings.Model
[default_interface] runtimeclass AdjustFontSizeArgs : IActionArgs
{
Int32 Delta { get; };
Single Delta { get; };
};
[default_interface] runtimeclass SendInputArgs : IActionArgs

View File

@@ -1110,8 +1110,9 @@ void CascadiaSettings::WriteSettingsToDisk()
// write current settings to current settings file
Json::StreamWriterBuilder wbuilder;
wbuilder.settings_["indentation"] = " ";
wbuilder.settings_["enableYAMLCompatibility"] = true; // suppress spaces around colons
wbuilder.settings_["indentation"] = " ";
wbuilder.settings_["precision"] = 6; // prevent values like 1.1000000000000001
FILETIME lastWriteTime{};
const auto styledString{ Json::writeString(wbuilder, ToJson()) };

View File

@@ -16,7 +16,7 @@ namespace Microsoft.Terminal.Settings.Model
Microsoft.Terminal.Settings.Model.Profile SourceProfile { get; };
INHERITABLE_FONT_SETTING(String, FontFace);
INHERITABLE_FONT_SETTING(Int32, FontSize);
INHERITABLE_FONT_SETTING(Single, FontSize);
INHERITABLE_FONT_SETTING(Windows.UI.Text.FontWeight, FontWeight);
INHERITABLE_FONT_SETTING(Windows.Foundation.Collections.IMap<String COMMA UInt32>, FontFeatures);

View File

@@ -96,7 +96,7 @@ Author(s):
#define MTSM_FONT_SETTINGS(X) \
X(hstring, FontFace, "face", DEFAULT_FONT_FACE) \
X(int32_t, FontSize, "size", DEFAULT_FONT_SIZE) \
X(float, FontSize, "size", DEFAULT_FONT_SIZE) \
X(winrt::Windows::UI::Text::FontWeight, FontWeight, "weight", DEFAULT_FONT_WEIGHT) \
X(IFontAxesMap, FontAxes, "axes") \
X(IFontFeatureMap, FontFeatures, "features")

View File

@@ -121,7 +121,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::TerminalSettings, double, Opacity, UseAcrylic() ? 0.5 : 1.0);
INHERITABLE_SETTING(Model::TerminalSettings, hstring, Padding, DEFAULT_PADDING);
INHERITABLE_SETTING(Model::TerminalSettings, hstring, FontFace, DEFAULT_FONT_FACE);
INHERITABLE_SETTING(Model::TerminalSettings, int32_t, FontSize, DEFAULT_FONT_SIZE);
INHERITABLE_SETTING(Model::TerminalSettings, float, FontSize, DEFAULT_FONT_SIZE);
INHERITABLE_SETTING(Model::TerminalSettings, winrt::Windows::UI::Text::FontWeight, FontWeight);
INHERITABLE_SETTING(Model::TerminalSettings, IFontAxesMap, FontAxes);

View File

@@ -57,7 +57,7 @@
X(bool, UseAcrylic, false) \
X(winrt::hstring, Padding, DEFAULT_PADDING) \
X(winrt::hstring, FontFace, L"Consolas") \
X(int32_t, FontSize, DEFAULT_FONT_SIZE) \
X(float, FontSize, DEFAULT_FONT_SIZE) \
X(winrt::Windows::UI::Text::FontWeight, FontWeight) \
X(IFontFeatureMap, FontFeatures) \
X(IFontAxesMap, FontAxes) \

View File

@@ -41,4 +41,6 @@ CONPTY_EXPORT HRESULT WINAPI ConptyReparentPseudoConsole(HPCON hPC, HWND newPare
CONPTY_EXPORT VOID WINAPI ConptyClosePseudoConsole(HPCON hPC);
CONPTY_EXPORT VOID WINAPI ConptyClosePseudoConsoleNoWait(HPCON hPC);
CONPTY_EXPORT HRESULT WINAPI ConptyPackPseudoConsole(HANDLE hServerProcess, HANDLE hRef, HANDLE hSignal, HPCON* phPC);

View File

@@ -39,14 +39,31 @@ void ServiceLocator::SetOneCoreTeardownFunction(void (*pfn)()) noexcept
s_oneCoreTeardownFunction = pfn;
}
[[noreturn]] void ServiceLocator::RundownAndExit(const HRESULT hr)
void ServiceLocator::RundownAndExit(const HRESULT hr)
{
static thread_local bool preventRecursion = false;
static std::atomic<bool> locked;
// BODGY:
// pRender->TriggerTeardown() might cause another VtEngine pass, which then might fail to write to the IO pipe.
// If that happens it calls VtIo::CloseOutput(), which in turn calls ServiceLocator::RundownAndExit().
// This prevents the unintended recursion and resulting deadlock.
if (std::exchange(preventRecursion, true))
{
return;
}
// MSFT:40146639
// The premise of this function is that 1 thread enters and 0 threads leave alive.
// We need to prevent anyone from calling us until we actually ExitProcess(),
// so that we don't TriggerTeardown() twice. LockConsole() can't be used here,
// because doing so would prevent the render thread from progressing.
AcquireSRWLockExclusive(&s_shutdownLock);
if (locked.exchange(true, std::memory_order_relaxed))
{
// If we reach this point, another thread is already in the process of exiting.
// There's a lot of ways to suspend ourselves until we exit, one of which is "sleep forever".
Sleep(INFINITE);
}
// MSFT:15506250
// In VT I/O Mode, a client application might die before we've rendered

View File

@@ -32,7 +32,7 @@ namespace Microsoft::Console::Interactivity
public:
static void SetOneCoreTeardownFunction(void (*pfn)()) noexcept;
[[noreturn]] static void RundownAndExit(const HRESULT hr);
static void RundownAndExit(const HRESULT hr);
// N.B.: Location methods without corresponding creation methods
// automatically create the singleton object on demand.
@@ -86,7 +86,6 @@ namespace Microsoft::Console::Interactivity
static HWND LocatePseudoWindow(const HWND owner = nullptr /*HWND_DESKTOP = 0*/);
protected:
ServiceLocator(const ServiceLocator&) = delete;
ServiceLocator& operator=(const ServiceLocator&) = delete;
@@ -112,7 +111,5 @@ namespace Microsoft::Console::Interactivity
static Globals s_globals;
static bool s_pseudoWindowInitialized;
static wil::unique_hwnd s_pseudoWindow;
static inline SRWLOCK s_shutdownLock = SRWLOCK_INIT;
};
}

View File

@@ -561,6 +561,7 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo
{
const auto requestedFamily = fontInfoDesired.GetFamily();
auto requestedWeight = fontInfoDesired.GetWeight();
auto fontSize = fontInfoDesired.GetFontSize();
auto requestedSize = fontInfoDesired.GetEngineSize();
if (!requestedFaceName)
@@ -573,6 +574,7 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo
}
if (!requestedSize.Y)
{
fontSize = 12.0f;
requestedSize = { 0, 12 };
}
if (!requestedWeight)
@@ -614,13 +616,12 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo
// Point sizes are commonly treated at a 72 DPI scale
// (including by OpenType), whereas DirectWrite uses 96 DPI.
// Since we want the height in px we multiply by the display's DPI.
const auto fontSizeInDIP = requestedSize.Y / 72.0f * 96.0f;
const auto fontSizeInPx = requestedSize.Y / 72.0f * _api.dpi;
const auto fontSizeInDIP = fontSize / 72.0f * 96.0f;
const auto fontSizeInPx = fontSize / 72.0f * _api.dpi;
const auto designUnitsPerPx = fontSizeInPx / static_cast<float>(metrics.designUnitsPerEm);
const auto ascent = static_cast<float>(metrics.ascent) * designUnitsPerPx;
const auto descent = static_cast<float>(metrics.descent) * designUnitsPerPx;
const auto lineGap = static_cast<float>(metrics.lineGap) * designUnitsPerPx;
const auto underlinePosition = static_cast<float>(-metrics.underlinePosition) * designUnitsPerPx;
const auto underlineThickness = static_cast<float>(metrics.underlineThickness) * designUnitsPerPx;
const auto strikethroughPosition = static_cast<float>(-metrics.strikethroughPosition) * designUnitsPerPx;
@@ -628,9 +629,13 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo
const auto advanceWidth = static_cast<float>(glyphMetrics.advanceWidth) * designUnitsPerPx;
const auto halfGap = lineGap / 2.0f;
const auto baseline = std::roundf(ascent + halfGap);
const auto lineHeight = std::roundf(baseline + descent + halfGap);
// NOTE: Line-gaps shouldn't be taken into account for lineHeight calculations.
// Terminals don't really have "gaps" between lines and instead the expectation
// is that two full block characters above each other don't leave any gaps
// between the lines. "Terminus TTF" for instance sets a line-gap of 90 units
// even though its font bitmap only covers the ascend/descend height.
const auto baseline = std::roundf(ascent);
const auto lineHeight = std::roundf(baseline + descent);
const auto underlinePos = std::roundf(baseline + underlinePosition);
const auto underlineWidth = std::max(1.0f, std::roundf(underlineThickness));
const auto strikethroughPos = std::roundf(baseline + strikethroughPosition);
@@ -659,7 +664,7 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo
// Our cells can't overlap each other so we additionally clamp the bottom line to be inside the cell boundaries.
doubleUnderlinePosBottom = std::min(doubleUnderlinePosBottom, lineHeight - thinLineWidth);
const auto cellWidth = gsl::narrow<u16>(std::roundf(advanceWidth));
const auto cellWidth = gsl::narrow<u16>(std::lroundf(advanceWidth));
const auto cellHeight = gsl::narrow<u16>(lineHeight);
{
@@ -672,8 +677,7 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo
// The coordSizeUnscaled parameter to SetFromEngine is used for API functions like GetConsoleFontSize.
// Since clients expect that settings the font height to Y yields back a font height of Y,
// we're scaling the X relative/proportional to the actual cellWidth/cellHeight ratio.
// The code below uses a poor form of integer rounding.
requestedSize.X = (requestedSize.Y * cellWidth + cellHeight / 2) / cellHeight;
requestedSize.X = gsl::narrow_cast<til::CoordType>(std::lroundf(fontSize / cellHeight * cellWidth));
}
fontInfo.SetFromEngine(requestedFaceName, requestedFamily, requestedWeight, false, coordSize, requestedSize);

View File

@@ -249,6 +249,13 @@ try
_r.dirtyRect = _api.dirtyRect;
_r.scrollOffset = _api.scrollOffset;
// Clear the previous cursor. PaintCursor() is only called if the cursor is on.
if (const auto r = _api.invalidatedCursorArea; r.non_empty())
{
_setCellFlags(r, CellFlags::Cursor, CellFlags::None);
_r.dirtyRect |= til::rect{ r.left, r.top, r.right, r.bottom };
}
// This is an important block of code for our TileHashMap.
// We only process glyphs within the dirtyRect, but glyphs outside of the
// dirtyRect are still in use and shouldn't be discarded. This is critical
@@ -437,13 +444,6 @@ try
}
}
// Clear the previous cursor
if (const auto r = _api.invalidatedCursorArea; r.non_empty())
{
_setCellFlags(r, CellFlags::Cursor, CellFlags::None);
_r.dirtyRect |= til::rect{ r.left, r.top, r.right, r.bottom };
}
if (options.isOn)
{
const auto point = options.coordCursor;
@@ -639,8 +639,6 @@ void AtlasEngine::_createResources()
}
#endif // NDEBUG
const auto featureLevel = _r.device->GetFeatureLevel();
{
wil::com_ptr<IDXGIAdapter1> dxgiAdapter;
THROW_IF_FAILED(_r.device.query<IDXGIObject>()->GetParent(__uuidof(dxgiAdapter), dxgiAdapter.put_void()));
@@ -648,7 +646,23 @@ void AtlasEngine::_createResources()
DXGI_ADAPTER_DESC1 desc;
THROW_IF_FAILED(dxgiAdapter->GetDesc1(&desc));
_r.d2dMode = debugForceD2DMode || featureLevel < D3D_FEATURE_LEVEL_10_0 || WI_IsAnyFlagSet(desc.Flags, DXGI_ADAPTER_FLAG_REMOTE | DXGI_ADAPTER_FLAG_SOFTWARE);
_r.d2dMode = debugForceD2DMode || WI_IsAnyFlagSet(desc.Flags, DXGI_ADAPTER_FLAG_REMOTE | DXGI_ADAPTER_FLAG_SOFTWARE);
}
const auto featureLevel = _r.device->GetFeatureLevel();
if (featureLevel < D3D_FEATURE_LEVEL_10_0)
{
_r.d2dMode = true;
}
else if (featureLevel < D3D_FEATURE_LEVEL_11_0)
{
D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS options;
THROW_IF_FAILED(_r.device->CheckFeatureSupport(D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS, &options, sizeof(options)));
if (!options.ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x)
{
_r.d2dMode = true;
}
}
if (!_r.d2dMode)
@@ -681,11 +695,18 @@ void AtlasEngine::_createResources()
break;
}
static constexpr auto flags = D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR | D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_WARNINGS_ARE_ERRORS
static constexpr auto flags =
D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR
#ifdef NDEBUG
| D3DCOMPILE_OPTIMIZATION_LEVEL3;
| D3DCOMPILE_OPTIMIZATION_LEVEL3;
#else
| D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
// Only enable strictness and warnings in DEBUG mode
// as these settings makes it very difficult to develop
// shaders as windows terminal is not telling the user
// what's wrong, windows terminal just fails.
// Keep it in DEBUG mode to catch errors in shaders
// shipped with windows terminal
| D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_WARNINGS_ARE_ERRORS | D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#endif
wil::com_ptr<ID3DBlob> error;
@@ -935,6 +956,21 @@ void AtlasEngine::_recreateSizeDependentResources()
_api.glyphProps = Buffer<DWRITE_SHAPING_GLYPH_PROPERTIES>{ projectedGlyphSize };
_api.glyphAdvances = Buffer<f32>{ projectedGlyphSize };
_api.glyphOffsets = Buffer<DWRITE_GLYPH_OFFSET>{ projectedGlyphSize };
// Initialize cellGlyphMapping with valid data (whitespace), so that it can be
// safely used by the TileHashMap refresh logic via makeNewest() in StartPaint().
{
u16x2* coords{};
AtlasKey key{ { .cellCount = 1 }, 1, L" " };
AtlasValue value{ CellFlags::None, 1, &coords };
coords[0] = _r.tileAllocator.allocate(_r.glyphs);
const auto it = _r.glyphs.insert(std::move(key), std::move(value));
_r.glyphQueue.emplace_back(it);
std::fill(_r.cellGlyphMapping.begin(), _r.cellGlyphMapping.end(), it);
}
}
if (!_r.d2dMode)
@@ -1066,7 +1102,6 @@ void AtlasEngine::_recreateFontDependentResources()
auto& textFormat = _r.textFormats[italic][bold];
THROW_IF_FAILED(_sr.dwriteFactory->CreateTextFormat(_api.fontMetrics.fontName.c_str(), _api.fontMetrics.fontCollection.get(), fontWeight, fontStyle, DWRITE_FONT_STRETCH_NORMAL, _api.fontMetrics.fontSizeInDIP, L"", textFormat.put()));
THROW_IF_FAILED(textFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER));
THROW_IF_FAILED(textFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP));
// DWRITE_LINE_SPACING_METHOD_UNIFORM:
@@ -1075,11 +1110,16 @@ void AtlasEngine::_recreateFontDependentResources()
// We want that. Otherwise fallback fonts might be rendered with an incorrect baseline and get cut off vertically.
THROW_IF_FAILED(textFormat->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_UNIFORM, _r.cellSizeDIP.y, _api.fontMetrics.baselineInDIP));
if (const auto textFormat3 = textFormat.try_query<IDWriteTextFormat3>())
{
THROW_IF_FAILED(textFormat3->SetAutomaticFontAxes(DWRITE_AUTOMATIC_FONT_AXES_OPTICAL_SIZE));
// NOTE: SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER) breaks certain
// bitmap fonts which expect glyphs to be laid out left-aligned.
if (!_api.fontAxisValues.empty())
// NOTE: SetAutomaticFontAxes(DWRITE_AUTOMATIC_FONT_AXES_OPTICAL_SIZE) breaks certain
// fonts making them look fairly unslightly. With no option to easily disable this
// feature in Windows Terminal, it's better left disabled by default.
if (!_api.fontAxisValues.empty())
{
if (const auto textFormat3 = textFormat.try_query<IDWriteTextFormat3>())
{
// The wght axis defaults to the font weight.
_api.fontAxisValues[0].value = bold || standardAxes[0].value == -1.0f ? static_cast<float>(fontWeight) : standardAxes[0].value;
@@ -1176,6 +1216,15 @@ void AtlasEngine::_flushBufferLine()
// This would seriously blow us up otherwise.
Expects(_api.bufferLineColumn.size() == _api.bufferLine.size() + 1);
// GH#13962: With the lack of proper LineRendition support, just fill
// the remaining columns with whitespace to prevent any weird artifacts.
for (auto lastColumn = _api.bufferLineColumn.back(); lastColumn < _api.cellCount.x;)
{
++lastColumn;
_api.bufferLine.emplace_back(L' ');
_api.bufferLineColumn.emplace_back(lastColumn);
}
// NOTE:
// This entire function is one huge hack to see if it works.

View File

@@ -523,9 +523,9 @@ namespace Microsoft::Console::Render
struct CachedGlyphLayout
{
wil::com_ptr<IDWriteTextLayout> textLayout;
f32x2 halfSize;
f32x2 offset;
f32x2 scale;
f32x2 scaleCenter;
D2D1_DRAW_TEXT_OPTIONS options = D2D1_DRAW_TEXT_OPTIONS_NONE;
bool scalingRequired = false;

View File

@@ -472,10 +472,10 @@ void AtlasEngine::_drawGlyph(const TileHashMap::iterator& it) const
AtlasEngine::CachedGlyphLayout AtlasEngine::_getCachedGlyphLayout(const wchar_t* chars, u16 charsLength, u16 cellCount, IDWriteTextFormat* textFormat, bool coloredGlyph) const
{
const f32x2 layoutBox{ cellCount * _r.cellSizeDIP.x, _r.cellSizeDIP.y };
const f32x2 halfSize{ layoutBox.x * 0.5f, layoutBox.y * 0.5f };
bool scalingRequired = false;
f32x2 offset{ 0, 0 };
f32x2 scale{ 1, 1 };
f32x2 scaleCenter;
// See D2DFactory::DrawText
wil::com_ptr<IDWriteTextLayout> textLayout;
@@ -550,12 +550,18 @@ AtlasEngine::CachedGlyphLayout AtlasEngine::_getCachedGlyphLayout(const wchar_t*
static_cast<f32>(glyphMetrics.advanceHeight) / static_cast<f32>(metrics.designUnitsPerEm) * _r.fontMetrics.fontSizeInDIP,
};
// We always want box drawing glyphs to exactly match the size of a terminal cell.
// So for safe measure we'll always scale them to the exact size.
// But add 1px to the destination size, so that we don't end up with fractional pixels.
scalingRequired = true;
scale.x = layoutBox.x / boxSize.x;
scale.y = layoutBox.y / boxSize.y;
// We always want box drawing glyphs to be centered in their cell.
offset.x = (layoutBox.x - boxSize.x) * 0.5f;
offset.y = (layoutBox.y - boxSize.y) * 0.5f;
// We always want box drawing glyphs to exactly match the size of a terminal cell.
// But add 1px to the destination size, so that we don't end up with fractional pixels.
scale.x = (layoutBox.x + _r.dipPerPixel) / boxSize.x;
scale.y = (layoutBox.y + _r.dipPerPixel) / boxSize.y;
// Now that the glyph is in the center of the cell thanks
// to the offset, the scaleCenter is center of the cell.
scaleCenter.x = layoutBox.x * 0.5f;
scaleCenter.y = layoutBox.y * 0.5f;
}
}
else
@@ -563,16 +569,7 @@ AtlasEngine::CachedGlyphLayout AtlasEngine::_getCachedGlyphLayout(const wchar_t*
DWRITE_OVERHANG_METRICS overhang;
THROW_IF_FAILED(textLayout->GetOverhangMetrics(&overhang));
const DWRITE_OVERHANG_METRICS clampedOverhang{
std::max(0.0f, overhang.left),
std::max(0.0f, overhang.top),
std::max(0.0f, overhang.right),
std::max(0.0f, overhang.bottom),
};
f32x2 actualSize{
layoutBox.x + overhang.left + overhang.right,
layoutBox.y + overhang.top + overhang.bottom,
};
auto actualSizeX = layoutBox.x + overhang.left + overhang.right;
// Long glyphs should be drawn with their proper design size, even if that makes them a bit blurry,
// because otherwise we fail to support "pseudo" block characters like the "===" ligature in Cascadia Code.
@@ -585,8 +582,7 @@ AtlasEngine::CachedGlyphLayout AtlasEngine::_getCachedGlyphLayout(const wchar_t*
const auto advanceScale = _r.fontMetrics.advanceScale;
scalingRequired = true;
scale = { advanceScale, advanceScale };
actualSize.x *= advanceScale;
actualSize.y *= advanceScale;
actualSizeX *= advanceScale;
}
// We need to offset glyphs that are simply outside of our layout box (layoutBox.x/.y)
@@ -627,34 +623,27 @@ AtlasEngine::CachedGlyphLayout AtlasEngine::_getCachedGlyphLayout(const wchar_t*
// --> offsetY = 0
// --> scale = layoutBox.y / (layoutBox.y + left + right)
// = 0.69f
offset.x = clampedOverhang.left - clampedOverhang.right;
offset.y = clampedOverhang.top - clampedOverhang.bottom;
offset.x = std::max(0.0f, overhang.left) - std::max(0.0f, overhang.right);
scaleCenter.x = offset.x;
scaleCenter.y = _r.fontMetrics.baselineInDIP;
if ((actualSize.x - layoutBox.x) > _r.dipPerPixel)
if ((actualSizeX - layoutBox.x) > _r.dipPerPixel)
{
scalingRequired = true;
offset.x = (overhang.left - overhang.right) * 0.5f;
scale.x = layoutBox.x / actualSize.x;
scale.x = layoutBox.x / actualSizeX;
scale.y = scale.x;
scaleCenter.x = layoutBox.x * 0.5f;
}
if ((actualSize.y - layoutBox.y) > _r.dipPerPixel)
if (overhang.top > _r.dipPerPixel || overhang.bottom > _r.dipPerPixel)
{
const auto descend = _r.cellSizeDIP.y - _r.fontMetrics.baselineInDIP;
const auto scaleTop = _r.fontMetrics.baselineInDIP / (_r.fontMetrics.baselineInDIP + overhang.top);
const auto scaleBottom = descend / (descend + overhang.bottom);
scalingRequired = true;
offset.y = (overhang.top - overhang.bottom) * 0.5f;
scale.x = std::min(scale.x, layoutBox.y / actualSize.y);
scale.x = std::min(scale.x, std::min(scaleTop, scaleBottom));
scale.y = scale.x;
}
// As explained below, we use D2D1_DRAW_TEXT_OPTIONS_NO_SNAP to prevent a weird issue with baseline snapping.
// But we do want it technically, so this re-implements baseline snapping... I think?
// It calculates the new `baseline` height after transformation by `scale.y` relative to the center point `halfSize.y`.
//
// This works even if `scale.y == 1`, because then `baseline == baselineInDIP + offset.y` and `baselineInDIP`
// is always measured in full pixels. So rounding it will be equivalent to just rounding `offset.y` itself.
const auto baseline = halfSize.y + (_r.fontMetrics.baselineInDIP + offset.y - halfSize.y) * scale.y;
// This rounds to the nearest multiple of _r.dipPerPixel.
const auto baselineFixed = roundf(baseline * _r.pixelPerDIP) * _r.dipPerPixel;
offset.y += (baselineFixed - baseline) / scale.y;
}
auto options = D2D1_DRAW_TEXT_OPTIONS_NONE;
@@ -672,11 +661,19 @@ AtlasEngine::CachedGlyphLayout AtlasEngine::_getCachedGlyphLayout(const wchar_t*
// where every single "=" might be blatantly misaligned vertically (same for any box drawings).
WI_SetFlagIf(options, D2D1_DRAW_TEXT_OPTIONS_NO_SNAP, scalingRequired);
// ClearType basically has a 3x higher horizontal resolution. To make our glyphs render the same everywhere,
// it's probably for the best to ensure we initially rasterize them on a whole pixel boundary.
// (https://en.wikipedia.org/wiki/ClearType#How_ClearType_works)
offset.x = roundf(offset.x * _r.pixelPerDIP) * _r.dipPerPixel;
// As explained below, we use D2D1_DRAW_TEXT_OPTIONS_NO_SNAP to prevent a weird issue with baseline snapping.
// But we do want it technically, so this re-implements baseline snapping... I think?
offset.y = roundf(offset.y * _r.pixelPerDIP) * _r.dipPerPixel;
return CachedGlyphLayout{
.textLayout = textLayout,
.halfSize = halfSize,
.offset = offset,
.scale = scale,
.scaleCenter = scaleCenter,
.options = options,
.scalingRequired = scalingRequired,
};
@@ -790,8 +787,8 @@ void AtlasEngine::CachedGlyphLayout::applyScaling(ID2D1RenderTarget* d2dRenderTa
0,
0,
scale.y,
(origin.x + halfSize.x) * (1.0f - scale.x),
(origin.y + halfSize.y) * (1.0f - scale.y),
(origin.x + scaleCenter.x) * (1.0f - scale.x),
(origin.y + scaleCenter.y) * (1.0f - scale.y),
};
d2dRenderTarget->SetTransform(&transform);
}

View File

@@ -8,23 +8,24 @@
FontInfoDesired::FontInfoDesired(const std::wstring_view& faceName,
const unsigned char family,
const unsigned int weight,
const til::size coordSizeDesired,
const float fontSize,
const unsigned int codePage) noexcept :
FontInfoBase(faceName, family, weight, false, codePage),
_coordSizeDesired(coordSizeDesired)
_coordSizeDesired{ 0, lroundf(fontSize) },
_fontSize{ fontSize }
{
}
FontInfoDesired::FontInfoDesired(const FontInfo& fiFont) noexcept :
FontInfoBase(fiFont),
_coordSizeDesired(fiFont.GetUnscaledSize())
_coordSizeDesired{ fiFont.GetUnscaledSize() },
_fontSize{ static_cast<float>(_coordSizeDesired.height) }
{
}
bool FontInfoDesired::operator==(const FontInfoDesired& other) noexcept
float FontInfoDesired::GetFontSize() const noexcept
{
return FontInfoBase::operator==(other) &&
_coordSizeDesired == other._coordSizeDesired;
return _fontSize;
}
til::size FontInfoDesired::GetEngineSize() const noexcept

View File

@@ -75,29 +75,30 @@ Renderer::~Renderer()
}
const auto hr = _PaintFrameForEngine(pEngine);
if (E_PENDING == hr)
if (SUCCEEDED(hr))
{
if (--tries == 0)
{
// Stop trying.
_pThread->DisablePainting();
if (_pfnRendererEnteredErrorState)
{
_pfnRendererEnteredErrorState();
}
// If there's no callback, we still don't want to FAIL_FAST: the renderer going black
// isn't near as bad as the entire application aborting. We're a component. We shouldn't
// abort applications that host us.
return S_FALSE;
}
// Add a bit of backoff.
// Sleep 150ms, 300ms, 450ms before failing out and disabling the renderer.
Sleep(renderBackoffBaseTimeMilliseconds * (maxRetriesForRenderEngine - tries));
continue;
break;
}
LOG_IF_FAILED(hr);
break;
LOG_HR_IF(hr, hr != E_PENDING);
if (--tries == 0)
{
// Stop trying.
_pThread->DisablePainting();
if (_pfnRendererEnteredErrorState)
{
_pfnRendererEnteredErrorState();
}
// If there's no callback, we still don't want to FAIL_FAST: the renderer going black
// isn't near as bad as the entire application aborting. We're a component. We shouldn't
// abort applications that host us.
return S_FALSE;
}
// Add a bit of backoff.
// Sleep 150ms, 300ms, 450ms before failing out and disabling the renderer.
Sleep(renderBackoffBaseTimeMilliseconds * (maxRetriesForRenderEngine - tries));
}
}

View File

@@ -27,15 +27,17 @@ public:
FontInfoDesired(const std::wstring_view& faceName,
const unsigned char family,
const unsigned int weight,
const til::size coordSizeDesired,
const float fontSize,
const unsigned int uiCodePage) noexcept;
FontInfoDesired(const FontInfo& fiFont) noexcept;
bool operator==(const FontInfoDesired& other) noexcept;
bool operator==(const FontInfoDesired& other) = delete;
float GetFontSize() const noexcept;
til::size GetEngineSize() const noexcept;
bool IsDefaultRasterFont() const noexcept;
private:
til::size _coordSizeDesired;
float _fontSize;
};

View File

@@ -20,8 +20,8 @@ namespace
}
// Generated by Generate-CodepointWidthsFromUCD.ps1 -Pack:True -Full:False -NoOverrides:False
// on 6/13/2022 8:57:08 PM (UTC) from Unicode 14.0.0.
// 321259 (0x4E6EB) codepoints covered.
// on 9/14/2022 7:12:26 PM (UTC) from Unicode 15.0.0.
// 321281 (0x4E701) codepoints covered.
// 240 (0xF0) codepoints overridden.
// Override path: .\src\types\unicode_width_overrides.xml
static constexpr std::array<UnicodeRange, 300> s_wideAndAmbiguousTable{
@@ -263,7 +263,9 @@ namespace
UnicodeRange{ 0x1aff5, 0x1affb, CodepointWidth::Wide },
UnicodeRange{ 0x1affd, 0x1affe, CodepointWidth::Wide },
UnicodeRange{ 0x1b000, 0x1b122, CodepointWidth::Wide },
UnicodeRange{ 0x1b132, 0x1b132, CodepointWidth::Wide },
UnicodeRange{ 0x1b150, 0x1b152, CodepointWidth::Wide },
UnicodeRange{ 0x1b155, 0x1b155, CodepointWidth::Wide },
UnicodeRange{ 0x1b164, 0x1b167, CodepointWidth::Wide },
UnicodeRange{ 0x1b170, 0x1b2fb, CodepointWidth::Wide },
UnicodeRange{ 0x1f004, 0x1f004, CodepointWidth::Wide },
@@ -303,7 +305,7 @@ namespace
UnicodeRange{ 0x1f6cc, 0x1f6cc, CodepointWidth::Wide },
UnicodeRange{ 0x1f6d0, 0x1f6d2, CodepointWidth::Wide },
UnicodeRange{ 0x1f6d5, 0x1f6d7, CodepointWidth::Wide },
UnicodeRange{ 0x1f6dd, 0x1f6df, CodepointWidth::Wide },
UnicodeRange{ 0x1f6dc, 0x1f6df, CodepointWidth::Wide },
UnicodeRange{ 0x1f6eb, 0x1f6ec, CodepointWidth::Wide },
UnicodeRange{ 0x1f6f4, 0x1f6fc, CodepointWidth::Wide },
UnicodeRange{ 0x1f7e0, 0x1f7eb, CodepointWidth::Wide },
@@ -311,15 +313,13 @@ namespace
UnicodeRange{ 0x1f90c, 0x1f93a, CodepointWidth::Wide },
UnicodeRange{ 0x1f93c, 0x1f945, CodepointWidth::Wide },
UnicodeRange{ 0x1f947, 0x1f9ff, CodepointWidth::Wide },
UnicodeRange{ 0x1fa70, 0x1fa74, CodepointWidth::Wide },
UnicodeRange{ 0x1fa78, 0x1fa7c, CodepointWidth::Wide },
UnicodeRange{ 0x1fa80, 0x1fa86, CodepointWidth::Wide },
UnicodeRange{ 0x1fa90, 0x1faac, CodepointWidth::Wide },
UnicodeRange{ 0x1fab0, 0x1faba, CodepointWidth::Wide },
UnicodeRange{ 0x1fac0, 0x1fac5, CodepointWidth::Wide },
UnicodeRange{ 0x1fad0, 0x1fad9, CodepointWidth::Wide },
UnicodeRange{ 0x1fae0, 0x1fae7, CodepointWidth::Wide },
UnicodeRange{ 0x1faf0, 0x1faf6, CodepointWidth::Wide },
UnicodeRange{ 0x1fa70, 0x1fa7c, CodepointWidth::Wide },
UnicodeRange{ 0x1fa80, 0x1fa88, CodepointWidth::Wide },
UnicodeRange{ 0x1fa90, 0x1fabd, CodepointWidth::Wide },
UnicodeRange{ 0x1fabf, 0x1fac5, CodepointWidth::Wide },
UnicodeRange{ 0x1face, 0x1fadb, CodepointWidth::Wide },
UnicodeRange{ 0x1fae0, 0x1fae8, CodepointWidth::Wide },
UnicodeRange{ 0x1faf0, 0x1faf8, CodepointWidth::Wide },
UnicodeRange{ 0x20000, 0x2fffd, CodepointWidth::Wide },
UnicodeRange{ 0x30000, 0x3fffd, CodepointWidth::Wide },
UnicodeRange{ 0xe0100, 0xe01ef, CodepointWidth::Ambiguous },

View File

@@ -4,6 +4,7 @@ EXPORTS
ConptyCreatePseudoConsoleAsUser
ConptyResizePseudoConsole
ConptyClosePseudoConsole
ConptyClosePseudoConsoleNoWait
ConptyClearPseudoConsole
ConptyShowHidePseudoConsole
ConptyReparentPseudoConsole

View File

@@ -84,10 +84,10 @@ void ConPtyTests::CreateConPtyNoPipes()
VERIFY_FAILED(_CreatePseudoConsole(defaultSize, nullptr, nullptr, 0, &pcon));
VERIFY_SUCCEEDED(_CreatePseudoConsole(defaultSize, nullptr, goodOut, 0, &pcon));
_ClosePseudoConsoleMembers(&pcon);
_ClosePseudoConsoleMembers(&pcon, TRUE);
VERIFY_SUCCEEDED(_CreatePseudoConsole(defaultSize, goodIn, nullptr, 0, &pcon));
_ClosePseudoConsoleMembers(&pcon);
_ClosePseudoConsoleMembers(&pcon, TRUE);
}
void ConPtyTests::CreateConPtyBadSize()
@@ -131,7 +131,7 @@ void ConPtyTests::GoodCreate()
&pcon));
auto closePty = wil::scope_exit([&] {
_ClosePseudoConsoleMembers(&pcon);
_ClosePseudoConsoleMembers(&pcon, TRUE);
});
}
@@ -160,7 +160,7 @@ void ConPtyTests::GoodCreateMultiple()
0,
&pcon1));
auto closePty1 = wil::scope_exit([&] {
_ClosePseudoConsoleMembers(&pcon1);
_ClosePseudoConsoleMembers(&pcon1, TRUE);
});
VERIFY_SUCCEEDED(
@@ -170,7 +170,7 @@ void ConPtyTests::GoodCreateMultiple()
0,
&pcon2));
auto closePty2 = wil::scope_exit([&] {
_ClosePseudoConsoleMembers(&pcon2);
_ClosePseudoConsoleMembers(&pcon2, TRUE);
});
}
@@ -197,7 +197,7 @@ void ConPtyTests::SurvivesOnBreakInput()
0,
&pty));
auto closePty1 = wil::scope_exit([&] {
_ClosePseudoConsoleMembers(&pty);
_ClosePseudoConsoleMembers(&pty, TRUE);
});
DWORD dwExit;
@@ -242,7 +242,7 @@ void ConPtyTests::SurvivesOnBreakOutput()
0,
&pty));
auto closePty1 = wil::scope_exit([&] {
_ClosePseudoConsoleMembers(&pty);
_ClosePseudoConsoleMembers(&pty, TRUE);
});
DWORD dwExit;
@@ -287,7 +287,7 @@ void ConPtyTests::DiesOnBreakBoth()
0,
&pty));
auto closePty1 = wil::scope_exit([&] {
_ClosePseudoConsoleMembers(&pty);
_ClosePseudoConsoleMembers(&pty, TRUE);
});
DWORD dwExit;
@@ -358,7 +358,7 @@ void ConPtyTests::DiesOnClose()
0,
&pty));
auto closePty1 = wil::scope_exit([&] {
_ClosePseudoConsoleMembers(&pty);
_ClosePseudoConsoleMembers(&pty, TRUE);
});
DWORD dwExit;
@@ -382,7 +382,7 @@ void ConPtyTests::DiesOnClose()
Log::Comment(NoThrowString().Format(L"Sleep a bit to let the process attach"));
Sleep(100);
_ClosePseudoConsoleMembers(&pty);
_ClosePseudoConsoleMembers(&pty, TRUE);
GetExitCodeProcess(hConPtyProcess.get(), &dwExit);
VERIFY_ARE_NOT_EQUAL(dwExit, (DWORD)STILL_ACTIVE);

View File

@@ -347,9 +347,10 @@ HRESULT _ReparentPseudoConsole(_In_ const PseudoConsole* const pPty, _In_ const
// HPCON via the API).
// Arguments:
// - pPty: A pointer to a PseudoConsole struct.
// - wait: If true, waits for conhost/OpenConsole to exit first.
// Return Value:
// - <none>
void _ClosePseudoConsoleMembers(_In_ PseudoConsole* pPty)
void _ClosePseudoConsoleMembers(_In_ PseudoConsole* pPty, BOOL wait)
{
if (pPty != nullptr)
{
@@ -365,19 +366,11 @@ void _ClosePseudoConsoleMembers(_In_ PseudoConsole* pPty)
// has yet to send before we hard kill it.
if (_HandleIsValid(pPty->hConPtyProcess))
{
// If the conhost is already dead, then that's fine. Presumably
// it's finished flushing it's output already.
DWORD dwExit = 0;
// If GetExitCodeProcess failed, it's likely conhost is already dead
// If so, skip waiting regardless of whatever error
// GetExitCodeProcess returned.
// We'll just go straight to killing conhost.
if (GetExitCodeProcess(pPty->hConPtyProcess, &dwExit) && dwExit == STILL_ACTIVE)
if (wait)
{
WaitForSingleObject(pPty->hConPtyProcess, INFINITE);
}
TerminateProcess(pPty->hConPtyProcess, 0);
CloseHandle(pPty->hConPtyProcess);
pPty->hConPtyProcess = nullptr;
}
@@ -398,13 +391,14 @@ void _ClosePseudoConsoleMembers(_In_ PseudoConsole* pPty)
// PseudoConsoles that were created with CreatePseudoConsole.
// Arguments:
// - pPty: A pointer to a PseudoConsole struct.
// - wait: If true, waits for conhost/OpenConsole to exit first.
// Return Value:
// - <none>
VOID _ClosePseudoConsole(_In_ PseudoConsole* pPty)
static void _ClosePseudoConsole(_In_ PseudoConsole* pPty, BOOL wait) noexcept
{
if (pPty != nullptr)
{
_ClosePseudoConsoleMembers(pPty);
_ClosePseudoConsoleMembers(pPty, wait);
HeapFree(GetProcessHeap(), 0, pPty);
}
}
@@ -465,7 +459,7 @@ extern "C" HRESULT ConptyCreatePseudoConsoleAsUser(_In_ HANDLE hToken,
auto pPty = (PseudoConsole*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PseudoConsole));
RETURN_IF_NULL_ALLOC(pPty);
auto cleanupPty = wil::scope_exit([&]() noexcept {
_ClosePseudoConsole(pPty);
_ClosePseudoConsole(pPty, TRUE);
});
wil::unique_handle duplicatedInput;
@@ -544,13 +538,22 @@ extern "C" HRESULT WINAPI ConptyReparentPseudoConsole(_In_ HPCON hPC, HWND newPa
// console window they were running in was closed.
// This can fail if the conhost hosting the pseudoconsole failed to be
// terminated, or if the pseudoconsole was already terminated.
// Waits for conhost/OpenConsole to exit first.
extern "C" VOID WINAPI ConptyClosePseudoConsole(_In_ HPCON hPC)
{
const auto pPty = (PseudoConsole*)hPC;
if (pPty != nullptr)
{
_ClosePseudoConsole(pPty);
}
_ClosePseudoConsole((PseudoConsole*)hPC, TRUE);
}
// Function Description:
// Closes the conpty and all associated state.
// Client applications attached to the conpty will also behave as though the
// console window they were running in was closed.
// This can fail if the conhost hosting the pseudoconsole failed to be
// terminated, or if the pseudoconsole was already terminated.
// Doesn't wait for conhost/OpenConsole to exit.
extern "C" VOID WINAPI ConptyClosePseudoConsoleNoWait(_In_ HPCON hPC)
{
_ClosePseudoConsole((PseudoConsole*)hPC, FALSE);
}
// NOTE: This one is not defined in the Windows headers but is

View File

@@ -41,8 +41,7 @@ HRESULT _ResizePseudoConsole(_In_ const PseudoConsole* const pPty, _In_ const CO
HRESULT _ClearPseudoConsole(_In_ const PseudoConsole* const pPty);
HRESULT _ShowHidePseudoConsole(_In_ const PseudoConsole* const pPty, const bool show);
HRESULT _ReparentPseudoConsole(_In_ const PseudoConsole* const pPty, _In_ const HWND newParent);
void _ClosePseudoConsoleMembers(_In_ PseudoConsole* pPty);
VOID _ClosePseudoConsole(_In_ PseudoConsole* pPty);
void _ClosePseudoConsoleMembers(_In_ PseudoConsole* pPty, BOOL wait);
HRESULT ConptyCreatePseudoConsoleAsUser(_In_ HANDLE hToken,
_In_ COORD size,