]> granicus.if.org Git - nethack/commitdiff
Win32TTY: Fix using a console font with wide glyphs
authorBart House <bart@barthouse.com>
Sat, 21 Oct 2017 20:41:29 +0000 (13:41 -0700)
committerPasi Kallinen <paxed@alt.org>
Wed, 25 Oct 2017 07:59:11 +0000 (10:59 +0300)
Added support to detect when the current console font has glyphs
that are too wide and will cause rendering errors in the console.
If detected, we warn the user and change the code page to 437
and the font to Consolas. At exit, if we had changed the font
and code page then we will restore to the original font and code page.

include/rm.h
src/options.c
sys/winnt/nttty.c

index 2c68e65156956c48456f2a47a560c388eb0c1690..18b33050b8ca74e76b28f7335640633b279dbd9c 100644 (file)
@@ -300,6 +300,8 @@ extern const struct symdef defsyms[MAXPCHARS]; /* defaults */
 extern const struct symdef def_warnsyms[WARNCOUNT];
 extern int currentgraphics; /* from drawing.c */
 extern nhsym showsyms[];
+extern nhsym l_syms[];
+extern nhsym r_syms[];
 
 extern struct symsetentry symset[NUM_GRAPHICS]; /* from drawing.c */
 #define SYMHANDLING(ht) (symset[currentgraphics].handling == (ht))
index d378d22007338d19df8a2dffa87b2f319715fbb3..bfd42b18980bf56cd0442910f9acebf866cd411e 100644 (file)
@@ -5114,6 +5114,7 @@ boolean setinitial, setfromfile;
                 assign_graphics(ROGUESET);
         } else if (!rogueflag)
             assign_graphics(PRIMARY);
+        preference_update("symset");
         need_redraw = TRUE;
         return TRUE;
 
index 64bea743c64ae0738b4531195df8782a2fd53ab5..3ac469ea84a403ae766ec901f3bc9b60c4fa3d8e 100644 (file)
@@ -44,6 +44,10 @@ int FDECL(process_keystroke,
           (INPUT_RECORD *, boolean *, BOOLEAN_P numberpad, int portdebug));
 static void NDECL(init_ttycolor);
 static void NDECL(really_move_cursor);
+static void NDECL(check_and_set_font);
+static boolean NDECL(check_font_widths);
+static void NDECL(set_known_good_console_font);
+static void NDECL(restore_original_console_font);
 
 /* Win32 Console handles for input and output */
 HANDLE hConIn;
@@ -54,6 +58,11 @@ CONSOLE_SCREEN_BUFFER_INFO csbi, origcsbi;
 COORD ntcoord;
 INPUT_RECORD ir;
 
+/* Support for changing console font if existing glyph widths are too wide */
+boolean console_font_changed;
+CONSOLE_FONT_INFOEX original_console_font_info;
+UINT original_console_code_page;
+
 extern boolean getreturn_enabled; /* from sys/share/pcsys.c */
 extern int redirect_stdout;
 
@@ -148,6 +157,10 @@ KEYHANDLERNAME pKeyHandlerName;
 void
 gettty()
 {
+    console_font_changed = FALSE;
+
+    check_and_set_font();
+
 #ifndef TEXTCOLOR
     int k;
 #endif
@@ -171,6 +184,8 @@ const char *s;
     end_screen();
     if (s)
         raw_print(s);
+
+    restore_original_console_font();
 }
 
 /* called by init_nhwindows() and resume_nhwindows() */
@@ -514,45 +529,47 @@ char ch;
  * Overrides wintty.c function of the same name
  * for win32. It is used for glyphs only, not text.
  */
+
+/* CP437 to Unicode mapping according to the Unicode Consortium */
+static const WCHAR cp437[] = {
+    0x0020, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
+    0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C,
+    0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8,
+    0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC,
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302,
+    0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
+    0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
+    0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
+    0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
+    0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,
+    0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+    0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
+    0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
+    0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
+    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,
+    0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
+    0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
+    0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
+    0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
+    0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0
+};
+
 void
 g_putch(in_ch)
 int in_ch;
 {
-    /* CP437 to Unicode mapping according to the Unicode Consortium */
-    static const WCHAR cp437[] = {
-        0x0020, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
-        0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C,
-        0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8,
-        0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC,
-        0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
-        0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
-        0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
-        0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
-        0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
-        0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
-        0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
-        0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
-        0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
-        0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
-        0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
-        0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302,
-        0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
-        0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
-        0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
-        0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
-        0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,
-        0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
-        0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
-        0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
-        0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
-        0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
-        0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,
-        0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
-        0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
-        0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
-        0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
-        0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0
-    };
     boolean inverse = FALSE;
     unsigned char ch = (unsigned char) in_ch;
 
@@ -853,6 +870,8 @@ const char *pref;
         toggle_mouse_support();
 #endif
     }
+    if (stricmp(pref, "symset") == 0)
+        check_and_set_font();
     return;
 }
 
@@ -1466,4 +1485,179 @@ GetConsoleHwnd(void)
     return hwndFound;
 }
 #endif /*CHANGE_COLOR*/
+
+static int CALLBACK EnumFontCallback(
+    const LOGFONTW * lf, const TEXTMETRICW * tm, DWORD fontType, LPARAM lParam)
+{
+    LOGFONTW * lf_ptr = (LOGFONTW *) lParam;
+    *lf_ptr = *lf;
+    return 0;
+}
+
+/* check_and_set_font ensures that the current font will render the symbols
+ * that are currently being used correctly.  If they will not be rendered
+ * correctly, then it will change the font to a known good font.
+ */
+void
+check_and_set_font()
+{
+    if (!check_font_widths()) {
+        raw_print("WARNING: glyphs too wide in console font."
+                  "  Changing code page to 437 and font to Consolas\n");
+        set_known_good_console_font();
+    }
+}
+
+/* check_font_widths returns TRUE if all glyphs in current console font
+ * fit within the width of a single console cell.
+ */
+boolean
+check_font_widths()
+{
+    CONSOLE_FONT_INFOEX console_font_info;
+    console_font_info.cbSize = sizeof(console_font_info);
+    BOOL success = GetCurrentConsoleFontEx(hConOut, FALSE,
+                                            &console_font_info);
+
+    /* get console window and DC
+     * NOTE: the DC from the console window does not have the correct
+     *       font selected at this point.
+     */
+    HWND hWnd = GetConsoleWindow();
+    HDC hDC = GetDC(hWnd);
+
+    LOGFONTW logical_font;
+    logical_font.lfCharSet = DEFAULT_CHARSET;
+    wcscpy(logical_font.lfFaceName, console_font_info.FaceName);
+    logical_font.lfPitchAndFamily = 0;
+
+    /* getting matching console font */
+    LOGFONTW matching_log_font = { 0 };
+    EnumFontFamiliesExW(hDC, &logical_font, EnumFontCallback,
+                                        (LPARAM) &matching_log_font, 0);
+
+    if (matching_log_font.lfHeight == 0) {
+        raw_print("Unable to enumerate system fonts\n");
+        return FALSE;
+    }
+
+    /* create font matching console font */
+    LOGFONTW console_font_log_font = matching_log_font;
+    console_font_log_font.lfWeight = console_font_info.FontWeight;
+    console_font_log_font.lfHeight = console_font_info.dwFontSize.Y;
+    console_font_log_font.lfWidth = 0;
+    HFONT console_font = CreateFontIndirectW(&console_font_log_font);
+
+    if (console_font == NULL) {
+        raw_print("Unable to create console font\n");
+        return FALSE;
+    }
+
+    /* select font */
+    HGDIOBJ saved_font = SelectObject(hDC, console_font);
+
+    /* determine whether it is a true type font */
+    TEXTMETRICA tm;
+    success = GetTextMetricsA(hDC, &tm);
+
+    if (!success) {
+        raw_print("Unable to get console font text metrics\n");
+        goto clean_up;
+    }
+
+    boolean isTrueType = (tm.tmPitchAndFamily & TMPF_TRUETYPE) != 0;
+
+    /* determine which glyphs are used */
+    boolean used[256];
+    memset(used, 0, sizeof(used));
+    for (int i = 0; i < SYM_MAX; i++) {
+        used[l_syms[i]] = TRUE;
+        used[r_syms[i]] = TRUE;
+    }
+
+    int wcUsedCount = 0;
+    wchar_t wcUsed[256];
+    for (int i = 0; i < sizeof(used); i++)
+        if (used[i])
+            wcUsed[wcUsedCount++] = cp437[i];
+
+    /* measure the set of used glyphs to ensure they fit */
+    boolean all_glyphs_fit = TRUE;
+
+    for (int i = 0; i < wcUsedCount; i++) {
+        int width;
+        if (isTrueType) {
+            ABC abc;
+            success = GetCharABCWidthsW(hDC, wcUsed[i], wcUsed[i], &abc);
+            width = abc.abcA + abc.abcB + abc.abcC;
+        } else {
+            success = GetCharWidthW(hDC, wcUsed[i], wcUsed[i], &width);
+        }
+
+        if (success && width > console_font_info.dwFontSize.X) {
+            all_glyphs_fit = FALSE;
+            break;
+        }
+    }
+
+clean_up:
+
+    SelectObject(hDC, saved_font);
+    DeleteObject(console_font);
+
+    return all_glyphs_fit;
+}
+
+/* set_known_good_console_font sets the code page and font used by the console
+ * to settings know to work well with NetHack.  It also saves the original
+ * settings so that they can be restored prior to NetHack exit.
+ */
+void
+set_known_good_console_font()
+{
+    CONSOLE_FONT_INFOEX console_font_info;
+    console_font_info.cbSize = sizeof(console_font_info);
+    BOOL success = GetCurrentConsoleFontEx(hConOut, FALSE,
+                                            &console_font_info);
+
+    console_font_changed = TRUE;
+    original_console_font_info = console_font_info;
+    original_console_code_page = GetConsoleOutputCP();
+
+    wcscpy_s(console_font_info.FaceName,
+        sizeof(console_font_info.FaceName)
+            / sizeof(console_font_info.FaceName[0]),
+        L"Consolas");
+
+    success = SetConsoleOutputCP(437);
+    if (!success)
+        raw_print("Unable to set console code page to 437\n");
+
+    success = SetCurrentConsoleFontEx(hConOut, FALSE, &console_font_info);
+    if (!success)
+        raw_print("Unable to set console font to Consolas\n");
+}
+
+/* restore_original_console_font will restore the console font and code page
+ * settings to what they were when NetHack was launched.
+ */
+void
+restore_original_console_font()
+{
+    if (console_font_changed) {
+        BOOL success;
+        raw_print("Restoring original font and code page\n");
+        success = SetConsoleOutputCP(original_console_code_page);
+        if (!success)
+            raw_print("Unable to restore original code page\n");
+
+        success = SetCurrentConsoleFontEx(hConOut, FALSE,
+                                            &original_console_font_info);
+        if (!success)
+            raw_print("Unable to restore original font\n");
+
+        console_font_changed = FALSE;
+    }
+}
+
 #endif /* WIN32 */