Closed electroly closed 3 years ago
Although it's always best if the user can change the font settings to what they prefer, I'm a great advocate of making things work by default and preventing programmers from having to add workarounds (in particular platform-dependent workarounds). So I think it is a good idea to handle this in Turbo Vision.
I would like to point out a few things about your solution:
CONOUT$
is the most portable way to access the output buffer in Turbo Vision. Turbo Vision will access the console even if stdio is being redirected (https://github.com/magiblot/tvision/blob/b31528265bde6d4aa818bdef9afefbff3f84b830/source/platform/stdioctl.cpp, line 79), but in that case GetStdHandle(STD_OUTPUT_HANDLE)
may not return a console handle. Of course, it's best if the client application needs not know about this.Below is a possible way of handling this from within Turbo Vision. This has to be added right after the call to setlocale
in Win32ConsoleStrategy::initConsole
(source/platform/win32con.cpp
). Let me know if it works for you.
if (!supportsVT)
{
// Disable bitmap font in legacy console because multibyte characters
// are not displayed correctly.
CONSOLE_FONT_INFOEX fontInfo {};
fontInfo.cbSize = sizeof(fontInfo);
auto isBitmap = [](UINT family)
{
// https://docs.microsoft.com/en-us/windows/console/console-font-infoex
// "FontFamily: see the description of the tmPitchAndFamily member
// of the TEXTMETRIC structure."
// https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-textmetricw
// "A monospace bitmap font has all of these low-order bits clear".
return !(family & (TMPF_FIXED_PITCH | TMPF_VECTOR | TMPF_TRUETYPE | TMPF_DEVICE));
};
if ( GetCurrentConsoleFontEx(StdioCtl::out(), FALSE, &fontInfo)
&& isBitmap(fontInfo.FontFamily) )
{
// Compute the new font height based on the bitmap font size.
auto &oldSize = fontInfo.dwFontSize;
short fontY = 2*min(oldSize.X, oldSize.Y);
for (auto *name : {L"Consolas", L"Lucida Console"})
{
fontInfo.nFont = 0;
fontInfo.FontFamily = FF_DONTCARE;
fontInfo.FontWeight = FW_NORMAL;
fontInfo.dwFontSize = {0, fontY}; // Width estimated automatically, it seems.
wcscpy(fontInfo.FaceName, name);
// SetCurrentConsoleFontEx succeeds even if the font is not available.
// We need to check whether the font has actually been set.
SetCurrentConsoleFontEx(StdioCtl::out(), FALSE, &fontInfo);
GetCurrentConsoleFontEx(StdioCtl::out(), FALSE, &fontInfo);
if (wcscmp(fontInfo.FaceName, name) == 0)
break;
}
}
}
Looks great; I like your fix. I tested on both Windows 7 and Windows 10 and everything works great.
Good catch on the console handle and the raster fonts in the modern console.
I agree your way of handling the font selection is better than bringing in GDI. I initially wanted to fallback to any available monospace font if neither Consolas nor Lucida Console were available, but after determining that both fonts have shipped with Windows since Vista, I took it out. We may not need the Lucida Console fallback either, since Consolas will be there. This fallback would only be necessary on Windows XP, which ships Lucida Console but not Consolas. However, I believe tvision does not support XP. Maybe we should take out the fallback and only use Consolas to simplify the code.
Correct about the high-DPI; I'm using 175% scaling in Display Settings on a 4K monitor. One thing I noticed in my testing is that on Windows 10, the legacy console doesn't seem to apply the system scaling, but the modern console does. I initially used a size of 28 which was needed to make the legacy console readable on my screen, but then when I tried the same code on the modern console, the text was gigantic. Same issue in the other direction for the legacy console on a low-DPI test system; I had to back it down to 24 to fit on the screen at all with Windows 7's minimum resolution and default scaling. Basing the TrueType font size on the bitmap size instead of hardcoding is a clever solution that seems good enough and preserves the user's choice.
We may not need the Lucida Console fallback either, since Consolas will be there. This fallback would only be necessary on Windows XP, which ships Lucida Console but not Consolas. However, I believe tvision does not support XP.
I have a Windows Vista VM image for testing purposes where only Lucida Console and the raster font are available, I don't know why. So there is no problem in keeping the Lucida Console fallback.
Regarding Windows XP, I was not sure whether it would still work. It builds fine using the XP toolset, but 1) UTF-8 support for RTL file-system operations is not available and 2) it does not actually run on XP due to dynamic linking errors (not caused by this change). So no, XP is not supported.
Sounds good to me. I did not actually test with Vista to see if Consolas is there; I only looked at the claims made by the font documentation. Better to keep the fallback.
Not sure if this is appropriate for tvision to be doing; this might be something the client application should handle. I'm happy to ship this fix in the TMBASIC codebase but wanted to offer it to you in case tvision could use it.
Here is the code, please feel free to steal any or all of it if you're interested: https://github.com/electroly/tmbasic/blob/master/src/util/initConsole.cpp
I use
GetCurrentConsoleFontEx
to determine whether the console host is using a raster font, and if so, I useSetCurrentConsoleFontEx
to change the font to either Consolas or Lucida Console. I check first to make sure the font is available on the system and bail if neither is present. Both fonts ship with the versions of Windows that tvision supports, but I wanted to err on the side of caution.Doing this at startup allows TMBASIC to display correctly on a fresh Windows 7 install without the user needing to change their console settings. I haven't tested Vista but I imagine it will work the same there.
I had to link in
-lgdi32
so I can useEnumFontFamiliesEx
to check whether Consolas or Lucida Console are available.