installed pty
[VSoRC/.git] / node_modules / node-pty / deps / winpty / src / agent / ConsoleFont.cc
1 // Copyright (c) 2015 Ryan Prichard
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
4 // of this software and associated documentation files (the "Software"), to
5 // deal in the Software without restriction, including without limitation the
6 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 // sell copies of the Software, and to permit persons to whom the Software is
8 // furnished to do so, subject to the following conditions:
9 //
10 // The above copyright notice and this permission notice shall be included in
11 // all copies or substantial portions of the Software.
12 //
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 // IN THE SOFTWARE.
20
21 #include "ConsoleFont.h"
22
23 #include <windows.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <wchar.h>
27
28 #include <algorithm>
29 #include <string>
30 #include <tuple>
31 #include <utility>
32 #include <vector>
33
34 #include "../shared/DebugClient.h"
35 #include "../shared/OsModule.h"
36 #include "../shared/StringUtil.h"
37 #include "../shared/WindowsVersion.h"
38 #include "../shared/WinptyAssert.h"
39 #include "../shared/winpty_snprintf.h"
40
41 namespace {
42
43 #define COUNT_OF(x) (sizeof(x) / sizeof((x)[0]))
44
45 // See https://en.wikipedia.org/wiki/List_of_CJK_fonts
46 const wchar_t kLucidaConsole[] = L"Lucida Console";
47 const wchar_t kMSGothic[] = { 0xff2d, 0xff33, 0x0020, 0x30b4, 0x30b7, 0x30c3, 0x30af, 0 }; // 932, Japanese
48 const wchar_t kNSimSun[] = { 0x65b0, 0x5b8b, 0x4f53, 0 }; // 936, Chinese Simplified
49 const wchar_t kGulimChe[] = { 0xad74, 0xb9bc, 0xccb4, 0 }; // 949, Korean
50 const wchar_t kMingLight[] = { 0x7d30, 0x660e, 0x9ad4, 0 }; // 950, Chinese Traditional
51
52 struct FontSize {
53     short size;
54     int width;
55 };
56
57 struct Font {
58     const wchar_t *faceName;
59     unsigned int family;
60     short size;
61 };
62
63 // Ideographs in East Asian languages take two columns rather than one.
64 // In the console screen buffer, a "full-width" character will occupy two
65 // cells of the buffer, the first with attribute 0x100 and the second with
66 // attribute 0x200.
67 //
68 // Windows does not correctly identify code points as double-width in all
69 // configurations.  It depends heavily on the code page, the font facename,
70 // and (somehow) even the font size.  In the 437 code page (MS-DOS), for
71 // example, no codepoints are interpreted as double-width.  When the console
72 // is in an East Asian code page (932, 936, 949, or 950), then sometimes
73 // selecting a "Western" facename like "Lucida Console" or "Consolas" doesn't
74 // register, or if the font *can* be chosen, then the console doesn't handle
75 // double-width correctly.  I tested the double-width handling by writing
76 // several code points with WriteConsole and checking whether one or two cells
77 // were filled.
78 //
79 // In the Japanese code page (932), Microsoft's default font is MS Gothic.
80 // MS Gothic double-width handling seems to be broken with console versions
81 // prior to Windows 10 (including Windows 10's legacy mode), and it's
82 // especially broken in Windows 8 and 8.1.
83 //
84 // Test by running: misc/Utf16Echo A2 A3 2014 3044 30FC 4000
85 //
86 // The first three codepoints are always rendered as half-width with the
87 // Windows Japanese fonts.  (Of these, the first two must be half-width,
88 // but U+2014 could be either.)  The last three are rendered as full-width,
89 // and they are East_Asian_Width=Wide.
90 //
91 // Windows 7 fails by modeling all codepoints as full-width with font
92 // sizes 22 and above.
93 //
94 // Windows 8 gets U+00A2, U+00A3, U+2014, U+30FC, and U+4000 wrong, but
95 // using a point size not listed in the console properties dialog
96 // (e.g. "9") is less wrong:
97 //
98 //             |        code point               |
99 //  font       | 00A2 00A3 2014 3044 30FC 4000   | cell size
100 // ------------+---------------------------------+----------
101 //  8          |  F    F    F    F    H    H     |   4x8
102 //  9          |  F    F    F    F    F    F     |   5x9
103 //  16         |  F    F    F    F    H    H     |   8x16
104 // raster 6x13 |  H    H    H    F    F    H(*)  |   6x13
105 //
106 // (*) The Raster Font renders U+4000 as a white box (i.e. an unsupported
107 // character).
108 //
109
110 // See:
111 //  - misc/Font-Report-June2016 directory for per-size details
112 //  - misc/font-notes.txt
113 //  - misc/Utf16Echo.cc, misc/FontSurvey.cc, misc/SetFont.cc, misc/GetFont.cc
114
115 const FontSize kLucidaFontSizes[] = {
116     { 5, 3 },
117     { 6, 4 },
118     { 8, 5 },
119     { 10, 6 },
120     { 12, 7 },
121     { 14, 8 },
122     { 16, 10 },
123     { 18, 11 },
124     { 20, 12 },
125     { 36, 22 },
126     { 48, 29 },
127     { 60, 36 },
128     { 72, 43 },
129 };
130
131 // Japanese.  Used on Vista and Windows 7.
132 const FontSize k932GothicVista[] = {
133     { 6, 3 },
134     { 8, 4 },
135     { 10, 5 },
136     { 12, 6 },
137     { 13, 7 },
138     { 15, 8 },
139     { 17, 9 },
140     { 19, 10 },
141     { 21, 11 },
142     // All larger fonts are more broken w.r.t. full-size East Asian characters.
143 };
144
145 // Japanese.  Used on Windows 8, 8.1, and the legacy 10 console.
146 const FontSize k932GothicWin8[] = {
147     // All of these characters are broken w.r.t. full-size East Asian
148     // characters, but they're equally broken.
149     { 5, 3 },
150     { 7, 4 },
151     { 9, 5 },
152     { 11, 6 },
153     { 13, 7 },
154     { 15, 8 },
155     { 17, 9 },
156     { 20, 10 },
157     { 22, 11 },
158     { 24, 12 },
159     // include extra-large fonts for small terminals
160     { 36, 18 },
161     { 48, 24 },
162     { 60, 30 },
163     { 72, 36 },
164 };
165
166 // Japanese.  Used on the new Windows 10 console.
167 const FontSize k932GothicWin10[] = {
168     { 6, 3 },
169     { 8, 4 },
170     { 10, 5 },
171     { 12, 6 },
172     { 14, 7 },
173     { 16, 8 },
174     { 18, 9 },
175     { 20, 10 },
176     { 22, 11 },
177     { 24, 12 },
178     // include extra-large fonts for small terminals
179     { 36, 18 },
180     { 48, 24 },
181     { 60, 30 },
182     { 72, 36 },
183 };
184
185 // Chinese Simplified.
186 const FontSize k936SimSun[] = {
187     { 6, 3 },
188     { 8, 4 },
189     { 10, 5 },
190     { 12, 6 },
191     { 14, 7 },
192     { 16, 8 },
193     { 18, 9 },
194     { 20, 10 },
195     { 22, 11 },
196     { 24, 12 },
197     // include extra-large fonts for small terminals
198     { 36, 18 },
199     { 48, 24 },
200     { 60, 30 },
201     { 72, 36 },
202 };
203
204 // Korean.
205 const FontSize k949GulimChe[] = {
206     { 6, 3 },
207     { 8, 4 },
208     { 10, 5 },
209     { 12, 6 },
210     { 14, 7 },
211     { 16, 8 },
212     { 18, 9 },
213     { 20, 10 },
214     { 22, 11 },
215     { 24, 12 },
216     // include extra-large fonts for small terminals
217     { 36, 18 },
218     { 48, 24 },
219     { 60, 30 },
220     { 72, 36 },
221 };
222
223 // Chinese Traditional.
224 const FontSize k950MingLight[] = {
225     { 6, 3 },
226     { 8, 4 },
227     { 10, 5 },
228     { 12, 6 },
229     { 14, 7 },
230     { 16, 8 },
231     { 18, 9 },
232     { 20, 10 },
233     { 22, 11 },
234     { 24, 12 },
235     // include extra-large fonts for small terminals
236     { 36, 18 },
237     { 48, 24 },
238     { 60, 30 },
239     { 72, 36 },
240 };
241
242 // Some of these types and functions are missing from the MinGW headers.
243 // Others are undocumented.
244
245 struct AGENT_CONSOLE_FONT_INFO {
246     DWORD nFont;
247     COORD dwFontSize;
248 };
249
250 struct AGENT_CONSOLE_FONT_INFOEX {
251     ULONG cbSize;
252     DWORD nFont;
253     COORD dwFontSize;
254     UINT FontFamily;
255     UINT FontWeight;
256     WCHAR FaceName[LF_FACESIZE];
257 };
258
259 // undocumented XP API
260 typedef BOOL WINAPI SetConsoleFont_t(
261             HANDLE hOutput,
262             DWORD dwFontIndex);
263
264 // undocumented XP API
265 typedef DWORD WINAPI GetNumberOfConsoleFonts_t();
266
267 // XP and up
268 typedef BOOL WINAPI GetCurrentConsoleFont_t(
269             HANDLE hOutput,
270             BOOL bMaximumWindow,
271             AGENT_CONSOLE_FONT_INFO *lpConsoleCurrentFont);
272
273 // XP and up
274 typedef COORD WINAPI GetConsoleFontSize_t(
275             HANDLE hConsoleOutput,
276             DWORD nFont);
277
278 // Vista and up
279 typedef BOOL WINAPI GetCurrentConsoleFontEx_t(
280             HANDLE hConsoleOutput,
281             BOOL bMaximumWindow,
282             AGENT_CONSOLE_FONT_INFOEX *lpConsoleCurrentFontEx);
283
284 // Vista and up
285 typedef BOOL WINAPI SetCurrentConsoleFontEx_t(
286             HANDLE hConsoleOutput,
287             BOOL bMaximumWindow,
288             AGENT_CONSOLE_FONT_INFOEX *lpConsoleCurrentFontEx);
289
290 #define GET_MODULE_PROC(mod, funcName) \
291     m_##funcName = reinterpret_cast<funcName##_t*>((mod).proc(#funcName)); \
292
293 #define DEFINE_ACCESSOR(funcName) \
294     funcName##_t &funcName() const { \
295         ASSERT(valid()); \
296         return *m_##funcName; \
297     }
298
299 class XPFontAPI {
300 public:
301     XPFontAPI() : m_kernel32(L"kernel32.dll") {
302         GET_MODULE_PROC(m_kernel32, GetCurrentConsoleFont);
303         GET_MODULE_PROC(m_kernel32, GetConsoleFontSize);
304     }
305
306     bool valid() const {
307         return m_GetCurrentConsoleFont != NULL &&
308             m_GetConsoleFontSize != NULL;
309     }
310
311     DEFINE_ACCESSOR(GetCurrentConsoleFont)
312     DEFINE_ACCESSOR(GetConsoleFontSize)
313
314 private:
315     OsModule m_kernel32;
316     GetCurrentConsoleFont_t *m_GetCurrentConsoleFont;
317     GetConsoleFontSize_t *m_GetConsoleFontSize;
318 };
319
320 class UndocumentedXPFontAPI : public XPFontAPI {
321 public:
322     UndocumentedXPFontAPI() : m_kernel32(L"kernel32.dll") {
323         GET_MODULE_PROC(m_kernel32, SetConsoleFont);
324         GET_MODULE_PROC(m_kernel32, GetNumberOfConsoleFonts);
325     }
326
327     bool valid() const {
328         return this->XPFontAPI::valid() &&
329             m_SetConsoleFont != NULL &&
330             m_GetNumberOfConsoleFonts != NULL;
331     }
332
333     DEFINE_ACCESSOR(SetConsoleFont)
334     DEFINE_ACCESSOR(GetNumberOfConsoleFonts)
335
336 private:
337     OsModule m_kernel32;
338     SetConsoleFont_t *m_SetConsoleFont;
339     GetNumberOfConsoleFonts_t *m_GetNumberOfConsoleFonts;
340 };
341
342 class VistaFontAPI : public XPFontAPI {
343 public:
344     VistaFontAPI() : m_kernel32(L"kernel32.dll") {
345         GET_MODULE_PROC(m_kernel32, GetCurrentConsoleFontEx);
346         GET_MODULE_PROC(m_kernel32, SetCurrentConsoleFontEx);
347     }
348
349     bool valid() const {
350         return this->XPFontAPI::valid() &&
351             m_GetCurrentConsoleFontEx != NULL &&
352             m_SetCurrentConsoleFontEx != NULL;
353     }
354
355     DEFINE_ACCESSOR(GetCurrentConsoleFontEx)
356     DEFINE_ACCESSOR(SetCurrentConsoleFontEx)
357
358 private:
359     OsModule m_kernel32;
360     GetCurrentConsoleFontEx_t *m_GetCurrentConsoleFontEx;
361     SetCurrentConsoleFontEx_t *m_SetCurrentConsoleFontEx;
362 };
363
364 static std::vector<std::pair<DWORD, COORD> > readFontTable(
365         XPFontAPI &api, HANDLE conout, DWORD maxCount) {
366     std::vector<std::pair<DWORD, COORD> > ret;
367     for (DWORD i = 0; i < maxCount; ++i) {
368         COORD size = api.GetConsoleFontSize()(conout, i);
369         if (size.X == 0 && size.Y == 0) {
370             break;
371         }
372         ret.push_back(std::make_pair(i, size));
373     }
374     return ret;
375 }
376
377 static void dumpFontTable(HANDLE conout, const char *prefix) {
378     const int kMaxCount = 1000;
379     if (!isTracingEnabled()) {
380         return;
381     }
382     XPFontAPI api;
383     if (!api.valid()) {
384         trace("dumpFontTable: cannot dump font table -- missing APIs");
385         return;
386     }
387     std::vector<std::pair<DWORD, COORD> > table =
388         readFontTable(api, conout, kMaxCount);
389     std::string line;
390     char tmp[128];
391     size_t first = 0;
392     while (first < table.size()) {
393         size_t last = std::min(table.size() - 1, first + 10 - 1);
394         winpty_snprintf(tmp, "%sfonts %02u-%02u:",
395             prefix, static_cast<unsigned>(first), static_cast<unsigned>(last));
396         line = tmp;
397         for (size_t i = first; i <= last; ++i) {
398             if (i % 10 == 5) {
399                 line += "  - ";
400             }
401             winpty_snprintf(tmp, " %2dx%-2d",
402                 table[i].second.X, table[i].second.Y);
403             line += tmp;
404         }
405         trace("%s", line.c_str());
406         first = last + 1;
407     }
408     if (table.size() == kMaxCount) {
409         trace("%sfonts: ... stopped reading at %d fonts ...",
410             prefix, kMaxCount);
411     }
412 }
413
414 static std::string stringToCodePoints(const std::wstring &str) {
415     std::string ret = "(";
416     for (size_t i = 0; i < str.size(); ++i) {
417         char tmp[32];
418         winpty_snprintf(tmp, "%X", str[i]);
419         if (ret.size() > 1) {
420             ret.push_back(' ');
421         }
422         ret += tmp;
423     }
424     ret.push_back(')');
425     return ret;
426 }
427
428 static void dumpFontInfoEx(
429         const AGENT_CONSOLE_FONT_INFOEX &infoex,
430         const char *prefix) {
431     if (!isTracingEnabled()) {
432         return;
433     }
434     std::wstring faceName(infoex.FaceName,
435         winpty_wcsnlen(infoex.FaceName, COUNT_OF(infoex.FaceName)));
436     trace("%snFont=%u dwFontSize=(%d,%d) "
437         "FontFamily=0x%x FontWeight=%u FaceName=%s %s",
438         prefix,
439         static_cast<unsigned>(infoex.nFont),
440         infoex.dwFontSize.X, infoex.dwFontSize.Y,
441         infoex.FontFamily, infoex.FontWeight, utf8FromWide(faceName).c_str(),
442         stringToCodePoints(faceName).c_str());
443 }
444
445 static void dumpVistaFont(VistaFontAPI &api, HANDLE conout, const char *prefix) {
446     if (!isTracingEnabled()) {
447         return;
448     }
449     AGENT_CONSOLE_FONT_INFOEX infoex = {0};
450     infoex.cbSize = sizeof(infoex);
451     if (!api.GetCurrentConsoleFontEx()(conout, FALSE, &infoex)) {
452         trace("GetCurrentConsoleFontEx call failed");
453         return;
454     }
455     dumpFontInfoEx(infoex, prefix);
456 }
457
458 static void dumpXPFont(XPFontAPI &api, HANDLE conout, const char *prefix) {
459     if (!isTracingEnabled()) {
460         return;
461     }
462     AGENT_CONSOLE_FONT_INFO info = {0};
463     if (!api.GetCurrentConsoleFont()(conout, FALSE, &info)) {
464         trace("GetCurrentConsoleFont call failed");
465         return;
466     }
467     trace("%snFont=%u dwFontSize=(%d,%d)",
468         prefix,
469         static_cast<unsigned>(info.nFont),
470         info.dwFontSize.X, info.dwFontSize.Y);
471 }
472
473 static bool setFontVista(
474         VistaFontAPI &api,
475         HANDLE conout,
476         const Font &font) {
477     AGENT_CONSOLE_FONT_INFOEX infoex = {};
478     infoex.cbSize = sizeof(AGENT_CONSOLE_FONT_INFOEX);
479     infoex.dwFontSize.Y = font.size;
480     infoex.FontFamily = font.family;
481     infoex.FontWeight = 400;
482     winpty_wcsncpy_nul(infoex.FaceName, font.faceName);
483     dumpFontInfoEx(infoex, "setFontVista: setting font to: ");
484     if (!api.SetCurrentConsoleFontEx()(conout, FALSE, &infoex)) {
485         trace("setFontVista: SetCurrentConsoleFontEx call failed");
486         return false;
487     }
488     memset(&infoex, 0, sizeof(infoex));
489     infoex.cbSize = sizeof(infoex);
490     if (!api.GetCurrentConsoleFontEx()(conout, FALSE, &infoex)) {
491         trace("setFontVista: GetCurrentConsoleFontEx call failed");
492         return false;
493     }
494     if (wcsncmp(infoex.FaceName, font.faceName,
495             COUNT_OF(infoex.FaceName)) != 0) {
496         trace("setFontVista: face name was not set");
497         dumpFontInfoEx(infoex, "setFontVista: post-call font: ");
498         return false;
499     }
500     // We'd like to verify that the new font size is correct, but we can't
501     // predict what it will be, even though we just set it to `pxSize` through
502     // an apprently symmetric interface.  For the Chinese and Korean fonts, the
503     // new `infoex.dwFontSize.Y` value can be slightly larger than the height
504     // we specified.
505     return true;
506 }
507
508 static Font selectSmallFont(int codePage, int columns, bool isNewW10) {
509     // Iterate over a set of font sizes according to the code page, and select
510     // one.
511
512     const wchar_t *faceName = nullptr;
513     unsigned int fontFamily = 0;
514     const FontSize *table = nullptr;
515     size_t tableSize = 0;
516
517     switch (codePage) {
518         case 932: // Japanese
519             faceName = kMSGothic;
520             fontFamily = 0x36;
521             if (isNewW10) {
522                 table = k932GothicWin10;
523                 tableSize = COUNT_OF(k932GothicWin10);
524             } else if (isAtLeastWindows8()) {
525                 table = k932GothicWin8;
526                 tableSize = COUNT_OF(k932GothicWin8);
527             } else {
528                 table = k932GothicVista;
529                 tableSize = COUNT_OF(k932GothicVista);
530             }
531             break;
532         case 936: // Chinese Simplified
533             faceName = kNSimSun;
534             fontFamily = 0x36;
535             table = k936SimSun;
536             tableSize = COUNT_OF(k936SimSun);
537             break;
538         case 949: // Korean
539             faceName = kGulimChe;
540             fontFamily = 0x36;
541             table = k949GulimChe;
542             tableSize = COUNT_OF(k949GulimChe);
543             break;
544         case 950: // Chinese Traditional
545             faceName = kMingLight;
546             fontFamily = 0x36;
547             table = k950MingLight;
548             tableSize = COUNT_OF(k950MingLight);
549             break;
550         default:
551             faceName = kLucidaConsole;
552             fontFamily = 0x36;
553             table = kLucidaFontSizes;
554             tableSize = COUNT_OF(kLucidaFontSizes);
555             break;
556     }
557
558     size_t bestIndex = static_cast<size_t>(-1);
559     std::tuple<int, int> bestScore = std::make_tuple(-1, -1);
560
561     // We might want to pick the smallest possible font, because we don't know
562     // how large the monitor is (and the monitor size can change).  We might
563     // want to pick a larger font to accommodate console programs that resize
564     // the console on their own, like DOS edit.com, which tends to resize the
565     // console to 80 columns.
566
567     for (size_t i = 0; i < tableSize; ++i) {
568         const int width = table[i].width * columns;
569
570         // In general, we'd like to pick a font size where cutting the number
571         // of columns in half doesn't immediately violate the minimum width
572         // constraint.  (e.g. To run DOS edit.com, a user might resize their
573         // terminal to ~100 columns so it's big enough to show the 80 columns
574         // post-resize.)  To achieve this, give priority to fonts that allow
575         // this halving.  We don't want to encourage *very* large fonts,
576         // though, so disable the effect as the number of columns scales from
577         // 80 to 40.
578         const int halfColumns = std::min(columns, std::max(40, columns / 2));
579         const int halfWidth = table[i].width * halfColumns;
580
581         std::tuple<int, int> thisScore = std::make_tuple(-1, -1);
582         if (width >= 160 && halfWidth >= 160) {
583             // Both sizes are good.  Prefer the smaller fonts.
584             thisScore = std::make_tuple(2, -width);
585         } else if (width >= 160) {
586             // Prefer the smaller fonts.
587             thisScore = std::make_tuple(1, -width);
588         } else {
589             // Otherwise, prefer the largest font in our table.
590             thisScore = std::make_tuple(0, width);
591         }
592         if (thisScore > bestScore) {
593             bestIndex = i;
594             bestScore = thisScore;
595         }
596     }
597
598     ASSERT(bestIndex != static_cast<size_t>(-1));
599     return Font { faceName, fontFamily, table[bestIndex].size };
600 }
601
602 static void setSmallFontVista(VistaFontAPI &api, HANDLE conout,
603                               int columns, bool isNewW10) {
604     int codePage = GetConsoleOutputCP();
605     const auto font = selectSmallFont(codePage, columns, isNewW10);
606     if (setFontVista(api, conout, font)) {
607         trace("setSmallFontVista: success");
608         return;
609     }
610     if (codePage == 932 || codePage == 936 ||
611             codePage == 949 || codePage == 950) {
612         trace("setSmallFontVista: falling back to default codepage font instead");
613         const auto fontFB = selectSmallFont(0, columns, isNewW10);
614         if (setFontVista(api, conout, fontFB)) {
615             trace("setSmallFontVista: fallback was successful");
616             return;
617         }
618     }
619     trace("setSmallFontVista: failure");
620 }
621
622 struct FontSizeComparator {
623     bool operator()(const std::pair<DWORD, COORD> &obj1,
624                     const std::pair<DWORD, COORD> &obj2) const {
625         int score1 = obj1.second.X + obj1.second.Y;
626         int score2 = obj2.second.X + obj2.second.Y;
627         return score1 < score2;
628     }
629 };
630
631 static void setSmallFontXP(UndocumentedXPFontAPI &api, HANDLE conout) {
632     // Read the console font table and sort it from smallest to largest.
633     const DWORD fontCount = api.GetNumberOfConsoleFonts()();
634     trace("setSmallFontXP: number of console fonts: %u",
635         static_cast<unsigned>(fontCount));
636     std::vector<std::pair<DWORD, COORD> > table =
637         readFontTable(api, conout, fontCount);
638     std::sort(table.begin(), table.end(), FontSizeComparator());
639     for (size_t i = 0; i < table.size(); ++i) {
640         // Skip especially narrow fonts to permit narrower terminals.
641         if (table[i].second.X < 4) {
642             continue;
643         }
644         trace("setSmallFontXP: setting font to %u",
645             static_cast<unsigned>(table[i].first));
646         if (!api.SetConsoleFont()(conout, table[i].first)) {
647             trace("setSmallFontXP: SetConsoleFont call failed");
648             continue;
649         }
650         AGENT_CONSOLE_FONT_INFO info;
651         if (!api.GetCurrentConsoleFont()(conout, FALSE, &info)) {
652             trace("setSmallFontXP: GetCurrentConsoleFont call failed");
653             return;
654         }
655         if (info.nFont != table[i].first) {
656             trace("setSmallFontXP: font was not set");
657             dumpXPFont(api, conout, "setSmallFontXP: post-call font: ");
658             continue;
659         }
660         trace("setSmallFontXP: success");
661         return;
662     }
663     trace("setSmallFontXP: failure");
664 }
665
666 } // anonymous namespace
667
668 // A Windows console window can never be larger than the desktop window.  To
669 // maximize the possible size of the console in rows*cols, try to configure
670 // the console with a small font.  Unfortunately, we cannot make the font *too*
671 // small, because there is also a minimum window size in pixels.
672 void setSmallFont(HANDLE conout, int columns, bool isNewW10) {
673     trace("setSmallFont: attempting to set a small font for %d columns "
674         "(CP=%u OutputCP=%u)",
675         columns,
676         static_cast<unsigned>(GetConsoleCP()),
677         static_cast<unsigned>(GetConsoleOutputCP()));
678     VistaFontAPI vista;
679     if (vista.valid()) {
680         dumpVistaFont(vista, conout, "previous font: ");
681         dumpFontTable(conout, "previous font table: ");
682         setSmallFontVista(vista, conout, columns, isNewW10);
683         dumpVistaFont(vista, conout, "new font: ");
684         dumpFontTable(conout, "new font table: ");
685         return;
686     }
687     UndocumentedXPFontAPI xp;
688     if (xp.valid()) {
689         dumpXPFont(xp, conout, "previous font: ");
690         dumpFontTable(conout, "previous font table: ");
691         setSmallFontXP(xp, conout);
692         dumpXPFont(xp, conout, "new font: ");
693         dumpFontTable(conout, "new font table: ");
694         return;
695     }
696     trace("setSmallFont: neither Vista nor XP APIs detected -- giving up");
697     dumpFontTable(conout, "font table: ");
698 }