WindowsXP-SP1/shell/osshell/control/console/misc.c
2020-09-30 16:53:49 +02:00

745 lines
21 KiB
C

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
misc.c
Abstract:
This file implements the NT console server font routines.
Author:
Therese Stowell (thereses) 22-Jan-1991
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#ifdef DEBUG_PRINT
ULONG gDebugFlag = 0 ;
// ULONG gDebugFlag = _DBGOUTPUT | _DBGCHARS | _DBGFONTS | _DBGFONTS2 ;
#endif
PFONT_INFO FontInfo = NULL;
ULONG FontInfoLength;
ULONG NumberOfFonts;
BOOL gbEnumerateFaces = FALSE;
#define FE_ABANDONFONT 0
#define FE_SKIPFONT 1
#define FE_FONTOK 2
/*
* Initial default fonts and face names
*/
PFACENODE gpFaceNames = NULL;
/*
* TTPoints -- Initial font pixel heights for TT fonts
*/
SHORT TTPoints[] = {
5, 6, 7, 8, 10, 12, 14, 16, 18, 20, 24, 28, 36, 72
};
#if defined(FE_SB)
/*
* TTPointsDbcs -- Initial font pixel heights for TT fonts of DBCS.
* So, This list except odd point size because font width is (SBCS:DBCS != 1:2).
*/
SHORT TTPointsDbcs[] = {
6, 8, 10, 12, 14, 16, 18, 20, 24, 28, 36, 72
};
#endif
typedef struct _FONTENUMDATA {
HDC hDC;
BOOL bFindFaces;
ULONG ulFE;
PSHORT pTTPoints;
UINT nTTPoints;
} FONTENUMDATA, *PFONTENUMDATA;
PFACENODE
AddFaceNode(PFACENODE *ppStart, LPTSTR ptsz) {
PFACENODE pNew;
PFACENODE *ppTmp;
int cb;
/*
* Is it already here?
*/
for (ppTmp = ppStart; *ppTmp; ppTmp = &((*ppTmp)->pNext)) {
if (_tcscmp(((*ppTmp)->atch), ptsz) == 0) {
// already there !
return *ppTmp;
}
}
cb = (_tcslen(ptsz) + 1) * sizeof(TCHAR);
pNew = (PFACENODE)HeapAlloc(RtlProcessHeap(),0,sizeof(FACENODE) + cb);
if (pNew == NULL) {
return NULL;
}
pNew->pNext = NULL;
pNew->dwFlag = 0;
_tcscpy(pNew->atch, ptsz);
*ppTmp = pNew;
return pNew;
}
VOID
DestroyFaceNodes( VOID ) {
PFACENODE pNext;
PFACENODE pTmp;
pTmp = gpFaceNames;
while (pTmp != NULL) {
pNext = pTmp->pNext;
HeapFree(RtlProcessHeap(), 0, pTmp);
pTmp = pNext;
}
gpFaceNames = NULL;
}
int
AddFont(
ENUMLOGFONT *pelf,
NEWTEXTMETRIC *pntm,
int nFontType,
HDC hDC,
PFACENODE pFN
)
/*++
Add the font desribed by the LOGFONT structure to the font table if
it's not already there.
--*/
{
HFONT hFont;
TEXTMETRIC tm;
LONG nFont;
COORD SizeToShow;
COORD SizeActual;
COORD SizeWant;
BYTE tmFamily;
SIZE Size;
LPTSTR ptszFace = pelf->elfLogFont.lfFaceName;
/* get font info */
SizeWant.X = (SHORT)pelf->elfLogFont.lfWidth;
SizeWant.Y = (SHORT)pelf->elfLogFont.lfHeight;
CreateBoldFont:
pelf->elfLogFont.lfQuality = DEFAULT_QUALITY;
hFont = CreateFontIndirect(&pelf->elfLogFont);
if (!hFont) {
DBGFONTS((" REJECT font (can't create)\n"));
return FE_SKIPFONT; // same font in other sizes may still be suitable
}
DBGFONTS2((" hFont = %lx\n", hFont));
//
// BUGBUG
// For reasons unbeknownst to me, removing this code causes GDI
// to yack, claiming that the font is owned by another process.
//
SelectObject(hDC, hFont);
GetTextMetrics(hDC, &tm);
GetTextExtentPoint32(hDC, TEXT("0"), 1, &Size);
SizeActual.X = (SHORT)Size.cx;
SizeActual.Y = (SHORT)(tm.tmHeight + tm.tmExternalLeading);
DBGFONTS2((" actual size %d,%d\n", SizeActual.X, SizeActual.Y));
tmFamily = tm.tmPitchAndFamily;
if (TM_IS_TT_FONT(tmFamily) && (SizeWant.Y >= 0)) {
SizeToShow = SizeWant;
if (SizeWant.X == 0) {
// Asking for zero width height gets a default aspect-ratio width.
// It's better to show that width rather than 0.
SizeToShow.X = SizeActual.X;
}
} else {
SizeToShow = SizeActual;
}
//
// The size shouldn't be zero. This is to help catch Windows Bug #332453.
//
ASSERT(SizeActual.X != 0 && SizeActual.Y != 0 && "If you hit this please e-mail jasonsch");
DBGFONTS2((" SizeToShow = (%d,%d), SizeActual = (%d,%d)\n",
SizeToShow.X, SizeToShow.Y, SizeActual.X, SizeActual.Y));
//
// BUGBUG
// There's a GDI bug - this assert fails occasionally.
//ASSERT (tm.tmMaxCharWidth == pntm->tmMaxCharWidth);
/*
* NOW, determine whether this font entry has already been cached
* LATER : it may be possible to do this before creating the font, if
* we can trust the dimensions & other info from pntm.
* Sort by size:
* 1) By pixelheight (negative Y values)
* 2) By height (as shown)
* 3) By width (as shown)
*/
for (nFont = 0; nFont < (LONG)NumberOfFonts; ++nFont) {
COORD SizeShown;
if (FontInfo[nFont].hFont == NULL) {
DBGFONTS(("! Font %x has a NULL hFont\n", nFont));
continue;
}
if (FontInfo[nFont].SizeWant.X > 0) {
SizeShown.X = FontInfo[nFont].SizeWant.X;
} else {
SizeShown.X = FontInfo[nFont].Size.X;
}
if (FontInfo[nFont].SizeWant.Y > 0) {
// This is a font specified by cell height.
SizeShown.Y = FontInfo[nFont].SizeWant.Y;
} else {
SizeShown.Y = FontInfo[nFont].Size.Y;
if (FontInfo[nFont].SizeWant.Y < 0) {
// This is a TT font specified by character height.
if (SizeWant.Y < 0 && SizeWant.Y > FontInfo[nFont].SizeWant.Y) {
// Requested pixelheight is smaller than this one.
DBGFONTS(("INSERT %d pt at %x, before %d pt\n",
-SizeWant.Y, nFont, -FontInfo[nFont].SizeWant.Y));
break;
}
}
}
// DBGFONTS((" SizeShown(%x) = (%d,%d)\n",nFont,SizeShown.X,SizeShown.Y));
if (SIZE_EQUAL(SizeShown, SizeToShow) &&
FontInfo[nFont].Family == tmFamily &&
FontInfo[nFont].Weight == tm.tmWeight &&
_tcscmp(FontInfo[nFont].FaceName, ptszFace) == 0) {
/*
* Already have this font
*/
DBGFONTS2((" Already have the font\n"));
DeleteObject(hFont);
return FE_FONTOK;
}
if ((SizeToShow.Y < SizeShown.Y) ||
(SizeToShow.Y == SizeShown.Y && SizeToShow.X < SizeShown.X)) {
/*
* This new font is smaller than nFont
*/
DBGFONTS(("INSERT at %x, SizeToShow = (%d,%d)\n", nFont,
SizeToShow.X,SizeToShow.Y));
break;
}
}
/*
* If we have to grow our font table, do it
*/
if (NumberOfFonts == FontInfoLength) {
PFONT_INFO Temp;
FontInfoLength += FONT_INCREMENT;
Temp = (PFONT_INFO)HeapReAlloc(RtlProcessHeap(), 0, FontInfo,
sizeof(FONT_INFO) * FontInfoLength);
if (Temp == NULL) {
FontInfoLength -= FONT_INCREMENT;
return FE_ABANDONFONT; // no point enumerating more - no memory!
}
FontInfo = Temp;
}
/*
* The font we are adding should be inserted into the list,
* if it is smaller than the last one.
*/
if (nFont < (LONG)NumberOfFonts) {
RtlMoveMemory(&FontInfo[nFont+1],
&FontInfo[nFont],
sizeof(FONT_INFO) * (NumberOfFonts - nFont));
}
/*
* Store the font info
*/
FontInfo[nFont].hFont = hFont;
FontInfo[nFont].Family = tmFamily;
FontInfo[nFont].Size = SizeActual;
if (TM_IS_TT_FONT(tmFamily)) {
FontInfo[nFont].SizeWant = SizeWant;
} else {
FontInfo[nFont].SizeWant.X = 0;
FontInfo[nFont].SizeWant.Y = 0;
}
FontInfo[nFont].Weight = tm.tmWeight;
FontInfo[nFont].FaceName = pFN->atch;
#if defined(FE_SB)
FontInfo[nFont].tmCharSet = tm.tmCharSet;
#endif
++NumberOfFonts;
/*
* If this is a true type font, create a bold version too.
*/
if (nFontType == TRUETYPE_FONTTYPE && !IS_BOLD(FontInfo[nFont].Weight)) {
pelf->elfLogFont.lfWeight = FW_BOLD;
goto CreateBoldFont;
}
return FE_FONTOK; // and continue enumeration
}
VOID
InitializeFonts( VOID )
{
EnumerateFonts(EF_DEFFACE); // Just the Default font
}
VOID
DestroyFonts( VOID )
{
ULONG FontIndex;
if (FontInfo != NULL) {
for (FontIndex = 0; FontIndex < NumberOfFonts; FontIndex++) {
DeleteObject(FontInfo[FontIndex].hFont);
}
HeapFree(RtlProcessHeap(), 0, FontInfo);
FontInfo = NULL;
NumberOfFonts = 0;
}
DestroyFaceNodes();
}
/*
* Returns bit combination
* FE_ABANDONFONT - do not continue enumerating this font
* FE_SKIPFONT - skip this font but keep enumerating
* FE_FONTOK - font was created and added to cache or already there
*/
int
FontEnum(
ENUMLOGFONT *pelf,
NEWTEXTMETRIC *pntm,
int nFontType,
PFONTENUMDATA pfed
)
/*++
Is called exactly once by GDI for each font in the system. This
routine is used to store the FONT_INFO structure.
--*/
{
UINT i;
LPTSTR ptszFace = pelf->elfLogFont.lfFaceName;
PFACENODE pFN;
DBGFONTS((" FontEnum \"%ls\" (%d,%d) weight 0x%lx(%d) %x -- %s\n",
ptszFace,
pelf->elfLogFont.lfWidth, pelf->elfLogFont.lfHeight,
pelf->elfLogFont.lfWeight, pelf->elfLogFont.lfWeight,
pelf->elfLogFont.lfCharSet,
pfed->bFindFaces ? "Finding Faces" : "Creating Fonts"));
//
// reject variable width and italic fonts, also tt fonts with neg ac
//
if
(
!(pelf->elfLogFont.lfPitchAndFamily & FIXED_PITCH) ||
(pelf->elfLogFont.lfItalic) ||
!(pntm->ntmFlags & NTM_NONNEGATIVE_AC)
)
{
if (! IsAvailableTTFont(ptszFace)) {
DBGFONTS((" REJECT face (dbcs, variable pitch, italic, or neg a&c)\n"));
return pfed->bFindFaces ? FE_SKIPFONT : FE_ABANDONFONT;
}
}
/*
* reject TT fonts for whoom family is not modern, that is do not use
* FF_DONTCARE // may be surprised unpleasantly
* FF_DECORATIVE // likely to be symbol fonts
* FF_SCRIPT // cursive, inappropriate for console
* FF_SWISS OR FF_ROMAN // variable pitch
*/
if ((nFontType == TRUETYPE_FONTTYPE) &&
((pelf->elfLogFont.lfPitchAndFamily & 0xf0) != FF_MODERN)) {
DBGFONTS((" REJECT face (TT but not FF_MODERN)\n"));
return pfed->bFindFaces ? FE_SKIPFONT : FE_ABANDONFONT;
}
/*
* reject non-TT fonts that aren't OEM
*/
if ((nFontType != TRUETYPE_FONTTYPE) &&
#if defined(FE_SB)
(!gfFESystem || !IS_ANY_DBCS_CHARSET(pelf->elfLogFont.lfCharSet)) &&
#endif
(pelf->elfLogFont.lfCharSet != OEM_CHARSET)) {
DBGFONTS((" REJECT face (not TT nor OEM)\n"));
return pfed->bFindFaces ? FE_SKIPFONT : FE_ABANDONFONT;
}
/*
* reject non-TT fonts that are virtical font
*/
if ((nFontType != TRUETYPE_FONTTYPE) &&
(ptszFace[0] == TEXT('@'))) {
DBGFONTS((" REJECT face (not TT and TATEGAKI)\n"));
return pfed->bFindFaces ? FE_SKIPFONT : FE_ABANDONFONT;
}
/*
* reject non-TT fonts that aren't Terminal
*/
if (gfFESystem && (nFontType != TRUETYPE_FONTTYPE) &&
(_tcscmp(ptszFace, TEXT("Terminal")) != 0)) {
DBGFONTS((" REJECT face (not TT nor Terminal)\n"));
return pfed->bFindFaces ? FE_SKIPFONT : FE_ABANDONFONT;
}
/*
* reject Far East TT fonts that aren't Far East charset.
*/
if (IsAvailableTTFont(ptszFace) &&
!IS_ANY_DBCS_CHARSET(pelf->elfLogFont.lfCharSet) &&
!IsAvailableTTFontCP(ptszFace,0)
) {
DBGFONTS((" REJECT face (Far East TT and not Far East charset)\n"));
return FE_SKIPFONT; // should be enumerate next charset.
}
/*
* Add or find the facename
*/
pFN = AddFaceNode(&gpFaceNames, ptszFace);
if (pFN == NULL) {
return FE_ABANDONFONT;
}
if (pfed->bFindFaces) {
DWORD dwFontType;
if (nFontType == TRUETYPE_FONTTYPE) {
DBGFONTS(("NEW TT FACE %ls\n", ptszFace));
dwFontType = EF_TTFONT;
} else if (nFontType == RASTER_FONTTYPE) {
DBGFONTS(("NEW OEM FACE %ls\n",ptszFace));
dwFontType = EF_OEMFONT;
}
pFN->dwFlag |= dwFontType | EF_NEW;
#if defined(FE_SB)
if (IS_ANY_DBCS_CHARSET(pelf->elfLogFont.lfCharSet))
pFN->dwFlag |= EF_DBCSFONT;
#endif
return FE_SKIPFONT;
}
if (IS_BOLD(pelf->elfLogFont.lfWeight)) {
DBGFONTS2((" A bold font (weight %d)\n", pelf->elfLogFont.lfWeight));
// return FE_SKIPFONT;
}
/*
* Add the font to the table. If this is a true type font, add the
* sizes from the array. Otherwise, just add the size we got.
*/
if (nFontType & TRUETYPE_FONTTYPE) {
for (i = 0; i < pfed->nTTPoints; i++) {
pelf->elfLogFont.lfHeight = pfed->pTTPoints[i];
pelf->elfLogFont.lfWidth = 0;
pelf->elfLogFont.lfWeight = 400;
pfed->ulFE |= AddFont(pelf, pntm, nFontType, pfed->hDC, pFN);
if (pfed->ulFE & FE_ABANDONFONT) {
return FE_ABANDONFONT;
}
}
} else {
pfed->ulFE |= AddFont(pelf, pntm, nFontType, pfed->hDC, pFN);
if (pfed->ulFE & FE_ABANDONFONT) {
return FE_ABANDONFONT;
}
}
return FE_FONTOK; // and continue enumeration
}
BOOL
DoFontEnum(
HDC hDC,
LPTSTR ptszFace,
PSHORT pTTPoints,
UINT nTTPoints)
{
BOOL bDeleteDC = FALSE;
FONTENUMDATA fed;
LOGFONT LogFont;
DBGFONTS(("DoFontEnum \"%ls\"\n", ptszFace));
if (hDC == NULL) {
hDC = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
bDeleteDC = TRUE;
}
fed.hDC = hDC;
fed.bFindFaces = (ptszFace == NULL);
fed.ulFE = 0;
fed.pTTPoints = pTTPoints;
fed.nTTPoints = nTTPoints;
RtlZeroMemory(&LogFont, sizeof(LOGFONT));
LogFont.lfCharSet = DEFAULT_CHARSET;
if (ptszFace)
_tcscpy(LogFont.lfFaceName, ptszFace);
/*
* EnumFontFamiliesEx function enumerates one font in every face in every character set.
*/
EnumFontFamiliesEx(hDC, &LogFont, (FONTENUMPROC)FontEnum, (LPARAM)&fed, 0);
if (bDeleteDC) {
DeleteDC(hDC);
}
return (fed.ulFE & FE_FONTOK) != 0;
}
VOID
RemoveFace(LPTSTR ptszFace)
{
DWORD i;
int nToRemove = 0;
DBGFONTS(("RemoveFace %ls\n", ptszFace));
//
// Delete & Remove fonts with Face Name == ptszFace
//
for (i = 0; i < NumberOfFonts; i++) {
if (_tcscmp(FontInfo[i].FaceName, ptszFace) == 0) {
BOOL bDeleted = DeleteObject(FontInfo[i].hFont);
DBGPRINT(("RemoveFace: hFont %lx was %sdeleted\n",
FontInfo[i].hFont, bDeleted ? "" : "NOT "));
FontInfo[i].hFont = NULL;
nToRemove++;
} else if (nToRemove > 0) {
/*
* Shuffle from FontInfo[i] down nToRemove slots.
*/
RtlMoveMemory(&FontInfo[i - nToRemove],
&FontInfo[i],
sizeof(FONT_INFO)*(NumberOfFonts - i));
NumberOfFonts -= nToRemove;
i -= nToRemove;
nToRemove = 0;
}
}
NumberOfFonts -= nToRemove;
}
TCHAR DefaultFaceName[LF_FACESIZE];
COORD DefaultFontSize;
BYTE DefaultFontFamily;
ULONG DefaultFontIndex = 0;
ULONG CurrentFontIndex = 0;
NTSTATUS
EnumerateFonts(
DWORD Flags)
{
TEXTMETRIC tm;
HDC hDC;
PFACENODE pFN;
ULONG ulOldEnumFilter;
BOOL bEnumOEMFace = TRUE;
DWORD FontIndex;
DWORD dwFontType = 0;
DBGFONTS(("EnumerateFonts %lx\n", Flags));
dwFontType = (EF_TTFONT|EF_OEMFONT|EF_DEFFACE) & Flags;
if (FontInfo == NULL) {
//
// allocate memory for the font array
//
NumberOfFonts = 0;
FontInfo = (PFONT_INFO)HeapAlloc(RtlProcessHeap(),0,sizeof(FONT_INFO) * INITIAL_FONTS);
if (FontInfo == NULL)
return STATUS_NO_MEMORY;
FontInfoLength = INITIAL_FONTS;
}
hDC = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
// Before enumeration, turn off font enumeration filters.
ulOldEnumFilter = SetFontEnumeration(0);
SetFontEnumeration(ulOldEnumFilter & ~FE_FILTER_TRUETYPE);
if (Flags & EF_DEFFACE) {
SelectObject(hDC, GetStockObject(OEM_FIXED_FONT));
GetTextMetrics(hDC, &tm);
GetTextFace(hDC, LF_FACESIZE, DefaultFaceName);
DefaultFontSize.X = (SHORT)(tm.tmMaxCharWidth);
DefaultFontSize.Y = (SHORT)(tm.tmHeight+tm.tmExternalLeading);
DefaultFontFamily = tm.tmPitchAndFamily;
#if !defined(FE_SB)
DBGFONTS(("Default (OEM) Font %ls (%d,%d)\n", DefaultFaceName,
DefaultFontSize.X, DefaultFontSize.Y));
#else
if (IS_ANY_DBCS_CHARSET(tm.tmCharSet))
DefaultFontSize.X /= 2;
DBGFONTS(("Default (OEM) Font %ls (%d,%d) CharSet 0x%02X\n", DefaultFaceName,
DefaultFontSize.X, DefaultFontSize.Y,
tm.tmCharSet));
#endif
// Make sure we are going to enumerate the OEM face.
pFN = AddFaceNode(&gpFaceNames, DefaultFaceName);
if (pFN)
pFN->dwFlag |= EF_DEFFACE | EF_OEMFONT;
}
if (gbEnumerateFaces) {
/*
* Set the EF_OLD bit and clear the EF_NEW bit
* for all previously available faces
*/
for (pFN = gpFaceNames; pFN; pFN = pFN->pNext) {
pFN->dwFlag |= EF_OLD;
pFN->dwFlag &= ~EF_NEW;
}
//
// Use DoFontEnum to get the names of all the suitable Faces
// All facenames found will be put in gpFaceNames with
// the EF_NEW bit set.
//
DoFontEnum(hDC, NULL, TTPoints, 1);
gbEnumerateFaces = FALSE;
}
// Use DoFontEnum to get all fonts from the system. Our FontEnum
// proc puts just the ones we want into an array
//
for (pFN = gpFaceNames; pFN; pFN = pFN->pNext) {
DBGFONTS(("\"%ls\" is %s%s%s%s%s%s\n", pFN->atch,
pFN->dwFlag & EF_NEW ? "NEW " : " ",
pFN->dwFlag & EF_OLD ? "OLD " : " ",
pFN->dwFlag & EF_ENUMERATED ? "ENUMERATED " : " ",
pFN->dwFlag & EF_OEMFONT ? "OEMFONT " : " ",
pFN->dwFlag & EF_TTFONT ? "TTFONT " : " ",
pFN->dwFlag & EF_DEFFACE ? "DEFFACE " : " "));
if ((pFN->dwFlag & (EF_OLD|EF_NEW)) == EF_OLD) {
// The face is no longer available
RemoveFace(pFN->atch);
pFN->dwFlag &= ~EF_ENUMERATED;
continue;
}
if ((pFN->dwFlag & dwFontType) == 0) {
// not the kind of face we want
continue;
}
if (pFN->dwFlag & EF_ENUMERATED) {
// we already enumerated this face
continue;
}
if (pFN->dwFlag & EF_TTFONT) {
#if defined(FE_SB)
if (gfFESystem && !IsAvailableTTFontCP(pFN->atch,0))
DoFontEnum(hDC, pFN->atch, TTPointsDbcs, NELEM(TTPointsDbcs));
else
#endif
DoFontEnum(hDC, pFN->atch, TTPoints, NELEM(TTPoints));
} else {
DoFontEnum(hDC, pFN->atch, NULL, 0);
// If we find that the face just enumerated is the same as OEM,
// reset flag so we don't try to enumerate it again.
if (!_tcsncmp(pFN->atch, DefaultFaceName, LF_FACESIZE)) {
bEnumOEMFace = FALSE;
}
}
pFN->dwFlag |= EF_ENUMERATED;
}
// After enumerating fonts, restore the font enumeration filter.
SetFontEnumeration(ulOldEnumFilter);
DeleteDC(hDC);
#if defined(FE_SB)
if (gfFESystem )
{
for (FontIndex = 0; FontIndex < NumberOfFonts; FontIndex++) {
if (FontInfo[FontIndex].Size.X == DefaultFontSize.X &&
FontInfo[FontIndex].Size.Y == DefaultFontSize.Y &&
IS_ANY_DBCS_CHARSET(FontInfo[FontIndex].tmCharSet) &&
FontInfo[FontIndex].Family == DefaultFontFamily) {
break;
}
}
}
else
{
#endif
for (FontIndex = 0; FontIndex < NumberOfFonts; FontIndex++) {
if (FontInfo[FontIndex].Size.X == DefaultFontSize.X &&
FontInfo[FontIndex].Size.Y == DefaultFontSize.Y &&
FontInfo[FontIndex].Family == DefaultFontFamily) {
break;
}
}
#if defined(FE_SB)
}
#endif
ASSERT(FontIndex < NumberOfFonts);
if (FontIndex < NumberOfFonts) {
DefaultFontIndex = FontIndex;
} else {
DefaultFontIndex = 0;
}
DBGFONTS(("EnumerateFonts : DefaultFontIndex = %ld\n", DefaultFontIndex));
return STATUS_SUCCESS;
}