diff --git a/include/rm.h b/include/rm.h index 2c68e6515..18b33050b 100644 --- a/include/rm.h +++ b/include/rm.h @@ -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)) diff --git a/src/options.c b/src/options.c index d378d2200..bfd42b189 100644 --- a/src/options.c +++ b/src/options.c @@ -5114,6 +5114,7 @@ boolean setinitial, setfromfile; assign_graphics(ROGUESET); } else if (!rogueflag) assign_graphics(PRIMARY); + preference_update("symset"); need_redraw = TRUE; return TRUE; diff --git a/sys/winnt/nttty.c b/sys/winnt/nttty.c index 64bea743c..3ac469ea8 100644 --- a/sys/winnt/nttty.c +++ b/sys/winnt/nttty.c @@ -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 */