Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow FontInfo{,Base,Desired} to store a font name > 32 wch #3107

Merged
merged 6 commits into from
Oct 15, 2019

Conversation

DHowett
Copy link
Member

@DHowett DHowett commented Oct 7, 2019

Summary of the Pull Request

We now truncate the font name as it goes out to GDI APIs, in console API
servicing, and in the propsheet.

I attempted to defer truncating the font to as far up the stack as possible, so as to make FontInfo usable for the broadest set of cases.

PR Checklist

Detailed Description of the Pull Request / Additional comments

There are a couple things I have questions about, actually. I know that Settings gets memset (memsat?) by the registry deserializer, and perhaps that's another place for us to tackle. Right now, this pull request enables fonts whose names are >= 32 characters in Windows Terminal only, but the underpinnings are there for conhost as well. We'd need to explicitly break at the API, or perhaps return a failure or log something to telemetry.

  • Should we log truncation at the API boundary to telemetry?
  • Should the "launch face name" get the same treatment as the face name in Settings?
  • Should we fix Settings here, or later?
  • TrueTypeFontList is built out of things in winconp, the private console header. That fills me with dread that those structures are used for interop -- so I didn't bubble these fixes all the way up through the "default font" list, even though nominally the font list is also unbounded strings.
  • Is unsigned int right for codepage? For width?

I left some comments about truncating font faces around, but they could be tantamount to noise.

We now truncate the font name as it goes out to GDI APIs, in console API
servicing, and in the propsheet.

Fixes #602.
@DHowett
Copy link
Member Author

DHowett commented Oct 7, 2019

I also tested with my font set to __DefaultTTFont__.

@DHowett
Copy link
Member Author

DHowett commented Oct 7, 2019

argh, I used the wrong browser profile to submit this PR

@miniksa
Copy link
Member

miniksa commented Oct 8, 2019

I think #2674 might be resolved or close to resolved with this too. Can you check that?

@DHowett-MSFT
Copy link
Contributor

Closer, but not there yet. It's suppressed in two places, one of which still has some static wstrings (font, padding, starting directory, delimiters)

@miniksa
Copy link
Member

miniksa commented Oct 8, 2019

  • Should we log truncation at the API boundary to telemetry?

I think so, yes.

  • Should the "launch face name" get the same treatment as the face name in Settings?

I think so, yes.

  • Should we fix Settings here, or later?

Here if it's possible. I would love for conhost to get the benefits of this too.

  • TrueTypeFontList is built out of things in winconp, the private console header. That fills me with dread that those structures are used for interop -- so I didn't bubble these fixes all the way up through the "default font" list, even though nominally the font list is also unbounded strings.

It's not for interop. It was just shared between the conhost and propsheet code which used to live in different depots and were both written in just plain C. Now that both of them can use C++ and live in the same depot, you're welcome to bubble it the whole way through TrueTypeFontList (and half rewrite that bugger if you like to use modern collections and stuff). Or set it as a TODO for someone else to do.

  • Is unsigned int right for codepage? For width?

Int might be a bit too big for codepage. I think Codepage is actually an unsigned short. But I'm fine with unsigned int if that fits better in things.

I also think font weight is 100-900 so unsigned short might be more appropriately sized there too. (Width translated to weight via asking you what "width" was all about in Teams.)

src/host/renderFontDefaults.cpp Outdated Show resolved Hide resolved
src/interactivity/win32/menu.cpp Outdated Show resolved Hide resolved
src/renderer/base/FontInfoBase.cpp Outdated Show resolved Hide resolved
src/renderer/base/FontInfoBase.cpp Outdated Show resolved Hide resolved
@@ -396,7 +396,8 @@ GdiEngine::~GdiEngine()
// NOTE: not using what GDI gave us because some fonts don't quite roundtrip (e.g. MS Gothic and VL Gothic)
lf.lfPitchAndFamily = (FIXED_PITCH | FF_MODERN);

wcscpy_s(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), FontDesired.GetFaceName());
// NOTE: GDI cannot support font faces > 32 characters in length. THIS TRUNCATES THE FONT.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have any idea if GDI will accept the first 31-32 characters of a font whose name is too long and still resolve the correctish one?

@@ -396,7 +396,8 @@ GdiEngine::~GdiEngine()
// NOTE: not using what GDI gave us because some fonts don't quite roundtrip (e.g. MS Gothic and VL Gothic)
lf.lfPitchAndFamily = (FIXED_PITCH | FF_MODERN);

wcscpy_s(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), FontDesired.GetFaceName());
// NOTE: GDI cannot support font faces > 32 characters in length. THIS TRUNCATES THE FONT.
wcscpy_s(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), FontDesired.GetFaceName().data());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StringCchCopy maybe? It has an HRESULT failure code...

src/renderer/gdi/state.cpp Outdated Show resolved Hide resolved
src/renderer/inc/FontInfoBase.hpp Show resolved Hide resolved
src/renderer/inc/IFontDefaultList.hpp Outdated Show resolved Hide resolved
src/host/renderFontDefaults.cpp Outdated Show resolved Hide resolved
@ghost ghost added the Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something label Oct 8, 2019
Copy link
Contributor

@DHowett-MSFT DHowett-MSFT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Should we log truncation at the API boundary to telemetry?

I think so, yes.

K.

  • Should the "launch face name" get the same treatment as the face name in Settings?

I think so, yes.

K.

  • Should we fix Settings here, or later?

Here if it's possible. I would love for conhost to get the benefits of this too.

This one is a far bigger workitem, so I filed #3123 as a followup. I looked into doing this to TrueTypeFontList, and it cascades up through all the dialog procedure code, so I'm scared off of doing it for this pR.

  • TrueTypeFontList is built out of things in winconp, the private console header. That fills me with dread that those structures are used for interop -- so I didn't bubble these fixes all the way up through the "default font" list, even though nominally the font list is also unbounded strings.

It's not for interop. It was just shared between the conhost and propsheet code which used to live in different depots and were both written in just plain C. Now that both of them can use C++ and live in the same depot, you're welcome to bubble it the whole way through TrueTypeFontList (and half rewrite that bugger if you like to use modern collections and stuff). Or set it as a TODO for someone else to do.

Roger, but as above: propsheet needs more modernization than conhost today to get this bootstrapped.

  • Is unsigned int right for codepage? For width?

Int might be a bit too big for codepage. I think Codepage is actually an unsigned short. But I'm fine with unsigned int if that fits better in things.

I also think font weight is 100-900 so unsigned short might be more appropriately sized there too. (Width translated to weight via asking you what "width" was all about in Teams.)

I was going to say "bah i'll stick with int", but then I decided to use short and use gsl's throwing narrower so that we can root out anybody who doesn't comply.

It looks like SetCurrentConsoleFont lets you pass an int for weight, but the docs say it ranges from 0-1000 so maybe this is fine? That one's getting a non-throwing narrow.

src/renderer/base/FontInfoBase.cpp Outdated Show resolved Hide resolved
@DHowett-MSFT
Copy link
Contributor

Actually, on the telemetry front: we'll get a log and fail to service the API call when the font name is too long. Right now, there's no way to get a font name that's too long... so I'm going to tag this with #3123 as well.

@DHowett-MSFT
Copy link
Contributor

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@DHowett-MSFT
Copy link
Contributor

Okay, clearly I regressed this.

@@ -269,7 +269,8 @@ void ApiRoutines::GetNumberOfConsoleMouseButtonsImpl(ULONG& buttons) noexcept
consoleFontInfoEx.FontFamily = fontInfo.GetFamily();
consoleFontInfoEx.FontWeight = fontInfo.GetWeight();

RETURN_IF_FAILED(StringCchCopyW(consoleFontInfoEx.FaceName, ARRAYSIZE(consoleFontInfoEx.FaceName), fontInfo.GetFaceName()));
// GH#3123: We will fail to service the API if the font name is longer than 32 characters.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ehhh can we just truncate it to safely fit? I feel that's better than failing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we want a method that's like "GetNameGdiCompatible()" that fills an LF_FACESIZE size array.

src/host/settings.cpp Show resolved Hide resolved
@@ -404,13 +403,13 @@ void Settings::SetFilterOnPaste(const bool fFilterOnPaste)
_fFilterOnPaste = fFilterOnPaste;
}

const WCHAR* const Settings::GetLaunchFaceName() const
const std::wstring& Settings::GetLaunchFaceName() const
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What? Why? Why not just a std::wstring_view?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/shrug no real reason, i went with your first suggestion instead of your second

@@ -374,7 +374,7 @@ GdiEngine::~GdiEngine()
// Because the API is affected by the raster/TT status of the actively selected font, we can't have
// GDI choosing a TT font for us when we ask for Raster. We have to settle for forcing the current system
// Terminal font to load even if it doesn't have the glyphs necessary such that the APIs continue to work fine.
if (0 == wcscmp(FontDesired.GetFaceName(), L"Terminal"))
if (FontDesired.GetFaceName() == L"Terminal")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it didn't have a whole lot to do with you here, but should the magic string L"Terminal" be a constexpr at the top instead of here?

Copy link
Member

@miniksa miniksa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, good enough. Thanks.

@DHowett-MSFT
Copy link
Contributor

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@DHowett-MSFT
Copy link
Contributor

I'm extremely confused/alarmed.

The selection tests were failing because the unique_ptr holding the Selection singleton at static file scope was being overwritten with 0x00000000FFFFFFFF. It then later failed the null check in Selection::Instance(), so that value was returned as the Selection instance.

Time travel tracing indicates that the constructor for CursorBlinker CONSOLE_INFORMATION::_blinker is writing to the same storage as the unique_ptr when it initializes the timer timeout to INFINITE.

Why do they share storage?

@DHowett-MSFT
Copy link
Contributor

It turns out that there's an ODR violation! CONSOLE_INFORMATION changes size based on whether UNIT_TESTING is defined (VtIo got a new UNIT_TESTING-only member in #2525), and that gets rolled up into Host.unittest.lib. InteractivityBase.lib has a compile-time dependency on the layout of CONSOLE_INFORMATION, but InteractivityBase isn't built with a UNIT_TESTING variant. CI gets constructed in InteractivityBase, and as the linker can resolve the ODR violation in whatever way it sees fit it ends up choosing the shorter layout when packing in file-level statics.

This bug only exists in our tests. The fix in 83fcb6c moves the trampled global out of the way... but just fixes the symptom. :|
/cc @miniksa

@DHowett-MSFT
Copy link
Contributor

Followup filed in #3170.

@DHowett-MSFT DHowett-MSFT merged commit b664761 into master Oct 15, 2019
@DHowett-MSFT DHowett-MSFT deleted the dev/duhowett/font32 branch October 15, 2019 04:23
@ghost
Copy link

ghost commented Oct 23, 2019

🎉Windows Terminal Preview v0.6.2951.0 has been released which incorporates this pull request.:tada:

Handy links:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Specifying a font with name longer than 32 characters causes the app to crash
4 participants