/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: rtwin.cpp,v 1.1.2.4 2004/07/09 13:08:06 pankajgupta Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

/////////////////////////////////////////////////////////////////////////////
//
// RTWIN.CPP
//
// Function:
//  BOOL OnPaint(TextWindow* pTextWindow, void* pDC, void* pPaintstruct,
//	    ULONG32 ulTimeOfCurrentDraw,
//	    BOOL bSomeonesBeginOrEndTimeWasCrossedOverSinceLastDraw,
//	    INT16 xScrollWindowAmt, INT16 yScrollWindowAmt);
// Purpose:
//  This function does the actual rendering of text by doing a TextOut() of
//  all text in all TextContainers in the TextContainerList of pTextWindow
//  whose text is visible in the rectangle to be updated
//  This Function is called from WM_PAINT and hdc and ps are from
//  BeginPaint(); ps holds the invalid rectangle to be updated: 
// Returns:
//  Returns FALSE on error, TRUE otherwise. 
//
//
// Function:
//  void PaintBackground(TextWindow*, Display*, Colormap, Pixmap)
// Purpose:
//  This function paints the background of the window at the given rectangle
//  of pPaintstruct.
//  This Function is called from WM_PAINT, and pPaintstruct is (in Windows)
//  from  BeginPaint(); pPaintstruct holds the invalid rectangle to be
//  painted.
// Returns:
//  Returns FALSE on error, TRUE otherwise. 
//
//

#include "hxtypes.h"
#include "hxassert.h"

#include "hxstack.h"
#include "hxslist.h"

#include "rt_types.h"
#include "fontdefs.h"
#include "fontinfo.h"
#include "txtattrb.h"
#include "txtcntnr.h"
#include "textline.h"
#include "atocolor.h"
#include "atotime.h" //for live time comparison: IsTimeAMoreRecentThanTimeB()

#include "chxxtype.h"                                                           
#include "txtwindw.h" //for class TextWindow.                                   
#include "hxcom.h"                                                              
#include "hxvsurf.h"                                                           
#include "dict.h"

#include "drawptxt.h"
#include "hxcom.h"
#include "unixcmap.h"
#include "../../rtwin.h"

#if (!defined(_BEOS))

#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE		
static char HX_THIS_FILE[] = __FILE__;
#endif

// convert xRGB triplet to display specific format
void convertCOLORTYPEtoXVisualFormat(COLORTYPE RGBVal, XColor& xcolor)
{
    // assume starting with a new XColor
    memset(&xcolor, 0, sizeof(XColor));

    // separate r,g and b
    UINT16 t;
    t = (RGBVal & 0x00ff0000) >> 16;
    xcolor.red   = t << 8;
    
    t = (RGBVal & 0x0000ff00) >> 8;
    xcolor.green = t << 8;
    
    t = (RGBVal & 0x000000ff);
    xcolor.blue  = t << 8;
    
    //color.pixel = n;
    xcolor.flags = DoRed | DoGreen | DoBlue;
}

static XFontStruct* GetFontStruct(Display*, TextWindow*, TextContainer*, float);
static void PaintBackground(TextWindow*, Display*, Colormap, Pixmap);

/////////////////////////////////////////////////////////////////////////////
// Function:
//  BOOL
//  OnPaint
//  (
//	TextWindow* pTextWindow,
//	void* pvEvent,
//	void* pvPixmap,
//	HXBitmapInfoHeader* pBitmapInfoHeader,
//	ULONG32 ulTimeOfCurrentDraw
//  )
//
// Purpose:
//  This function does the actual rendering of text by doing a TextOut() of
//  all text in all TextContainers in the TextContainerList of pTextWindow
//  whose text is visible in the rectangle to be updated
//  This Function is called from WM_PAINT and hdc and ps are from
//  BeginPaint(); ps holds the invalid rectangle to be updated: 
//
// Returns:
//  Returns FALSE on error, TRUE otherwise. 
//
BOOL OnPaint(TextWindow* pTextWindow, void* pvEvent, void* pvPixmap,
        HXBitmapInfoHeader *pBitmapInfoHeader,
	ULONG32 ulTimeOfCurrentDraw,
	BOOL /* bSomeonesBeginOrEndTimeWasCrossedOverSinceLastDraw */,
	INT16 xScrollWindowAmt, INT16 yScrollWindowAmt)
{
    HX_ASSERT_VALID_PTR(pTextWindow);
    HX_ASSERT_VALID_PTR(pvEvent);
    HX_ASSERT_VALID_PTR(pvPixmap);
    HX_ASSERT_VALID_PTR(pBitmapInfoHeader);

    if (pTextWindow == NULL || pvEvent == NULL || pvPixmap == NULL)
    {
	return FALSE;
    }

    LONG32 lWindowWidth = pTextWindow->getWindowWidth();
    LONG32 lWindowHeight = pTextWindow->getWindowHeight();

    HXxEvent *pEvent = (HXxEvent*)pvEvent;
    
    // retrieve the X11 object we'll need to do the drawing
    Pixmap pixmap = *((Pixmap*)pvPixmap);
    HXxWindow *pWindow = (HXxWindow*)pEvent->param2;
    Display* pDisplay = (Display*)(pWindow->display);
    Colormap cmap = HXGetXColormap(pDisplay, (Window)pWindow->window);

    // fill in the background
    /***** TODO: make this conditional on friendly scrolling *****/
    PaintBackground(pTextWindow, pDisplay, cmap, pixmap);

    if (ulTimeOfCurrentDraw == TIME_INVALID)
    {
	return TRUE;
    }

    if (pTextWindow->textCntnrListSize() < 1)
    {
	return TRUE;
    }
    
    // We must adjust for where the virtual window location is due to
    // looping:
    LONG32 currentXOffsetDueToLooping = 
	pTextWindow->getCurrentXOffsetDueToLooping();
    LONG32 currentYOffsetDueToLooping = 
	pTextWindow->getCurrentYOffsetDueToLooping();

    BOOL bWindowHeightIsNotTheOriginalValue = 
	    lWindowHeight != pTextWindow->getHeight();
    BOOL bWindowWidthIsNotTheOriginalValue =
	    lWindowWidth != pTextWindow->getWidth();

    //We must adjust for where the virtual window location is due to
    // "teleprompter" adjustment so that the last line of text is visible
    // at the bottom edge of the window:
    LONG32 currentYOffsetForTeleprompter = 0L;
    if(TYPE_TELEPROMPTER == pTextWindow->getType())
    {
	currentYOffsetForTeleprompter =
		pTextWindow->getCurrentYOffsetForTeleprompter();
    }

    // This is freed at end of proc, so code should avoid
    // using return below here
    XLockDisplay(pDisplay);
    GC gc = XCreateGC(pDisplay, pixmap, 0, NULL);
    XUnlockDisplay(pDisplay);
    // TODO: check return value of XCreateGC

    XColor xcolor;
    BOOL bFreeColors = FALSE;

    float fXScaleFactor = (float)lWindowWidth / (float)pTextWindow->getWidth();
    float fYScaleFactor = (float)lWindowHeight / (float)pTextWindow->getHeight();

    // Iterate over each TextContainer in the window
    LISTPOSITION pos = pTextWindow->GetStartPosition();
    while (pos)
    {
	TextContainer* pContainer = NULL;
	XFontStruct* fs = NULL;
	COLORTYPE textColor;

	//  Note: GetNext(pos) effectively does GetAt(pos++)
	pContainer = (TextContainer*)pTextWindow->GetNext(pos);

	if (TEXT_SHOULD_BE_DRAWN(pContainer))
	{
	    //Check to make sure pContainer's text is located
	    // in the window before we go to the trouble of
	    // drawing it
	    LONG32 lUpperLeftXWithOffsets =
		    currentXOffsetDueToLooping +
		    pContainer->getXUpperLeftCorner();
	    LONG32 lLowerRightXWithOffsets =
		    currentXOffsetDueToLooping +
		    pContainer->getXLowerRightCorner();
	    LONG32 lUpperLeftYWithOffsets =
		    currentYOffsetDueToLooping +
		    currentYOffsetForTeleprompter +
		    pContainer->getYUpperLeftCorner();
	    LONG32 lLowerRightYWithOffsets =
		    currentYOffsetDueToLooping +
		    currentYOffsetForTeleprompter +
		    pContainer->getYLowerRightCorner();

	    LONG32 xScaled = lUpperLeftXWithOffsets;

	    LONG32 yScaled = lUpperLeftYWithOffsets;

	    LONG32 lScaledUpperLeftXWithOffsets =
		    lUpperLeftXWithOffsets;
	    LONG32 lScaledLowerRightXWithOffsets =
		    lLowerRightXWithOffsets;
	    LONG32 lScaledUpperLeftYWithOffsets =
		    lUpperLeftYWithOffsets;
	    LONG32 lScaledLowerRightYWithOffsets =
		    lLowerRightYWithOffsets;

	    if (bWindowWidthIsNotTheOriginalValue)
	    {
		xScaled  = 
			LONG32((float)xScaled * fXScaleFactor);
		lScaledUpperLeftXWithOffsets =
			LONG32((float)lScaledUpperLeftXWithOffsets *
			fXScaleFactor);
		lScaledLowerRightXWithOffsets =
			LONG32((float)lScaledLowerRightXWithOffsets *
			fXScaleFactor);
	    }
	    if (bWindowHeightIsNotTheOriginalValue)
	    {
		yScaled  = 
			LONG32((float)yScaled * fYScaleFactor);
		lScaledUpperLeftYWithOffsets =
			LONG32((float)lScaledUpperLeftYWithOffsets *
			fYScaleFactor);
		lScaledLowerRightYWithOffsets =
			LONG32((float)lScaledLowerRightYWithOffsets *
			fYScaleFactor);
	    }

	    ///Make sure pContainer's text is located in the
	    ///window before we go to the trouble of doing a TextOut:
	    if(	   (lScaledUpperLeftXWithOffsets > pTextWindow->getWindowWidth()) 
		    ||  (lScaledLowerRightXWithOffsets < 0)
		    ||  (lScaledUpperLeftYWithOffsets > pTextWindow->getWindowHeight()) 
		    ||  (lScaledLowerRightYWithOffsets < 0) )
	    {
		continue; //text doesn't overlap the window.
	    }

	    //If updateRect is in stretched coordinates, then the above
	    // check won't find everyone who is outside the window, so
	    // let's make sure we do here:
	    if (bWindowHeightIsNotTheOriginalValue)
	    {
		if(yScaled > pTextWindow->getWindowHeight())
		    continue; //It's outside the window
	    }
	    if (bWindowWidthIsNotTheOriginalValue)
	    {
		if(xScaled > pTextWindow->getWindowWidth())
		    continue; //It's outside the window
	    }


	    // check the time
	    //First, if we're live, we need to compare times keeping
	    // ULONG32-wrap-around in mind:
	    BOOL bBeginTimeIsMoreRecentThanCurTime =
		    IsTimeAMoreRecentThanTimeB(
		    pContainer->getBeginTime(),
		    ulTimeOfCurrentDraw,
		    pTextWindow->isLiveSource());
	    BOOL bCurTimeIsMoreRecentThanEndTime =
		    IsTimeAMoreRecentThanTimeB(
		    ulTimeOfCurrentDraw,
		    pContainer->getEndTime(),
		    pTextWindow->isLiveSource());
	    //Sanity check:
	    if(TIME_INVALID == pContainer->getEndTime())
	    {
		HX_ASSERT(!bCurTimeIsMoreRecentThanEndTime);
	    }
	    if(bBeginTimeIsMoreRecentThanCurTime  ||
		    bCurTimeIsMoreRecentThanEndTime)
	    {
		continue; // while (pos)
	    }

	    fs = GetFontStruct(pDisplay, pTextWindow,
		     pContainer, fYScaleFactor);
	    if (fs == NULL)
	    {
		// This means we couldn't find any font to render with.
		// TODO: put some debugging messages here
		continue; // while(pos)
	    }
	    XLockDisplay(pDisplay);
	    XSetFont(pDisplay, gc, fs->fid);
	    XUnlockDisplay(pDisplay);

	    textColor = pContainer->getTextColor();

	    if (pTextWindow->getType() == TYPE_TICKERTAPE)
	    {
	        textColor = pContainer->getTickertapeTextColor();
	    }

	    convertCOLORTYPEtoXVisualFormat(textColor, xcolor);
	    bFreeColors = TRUE;

	    // get color
	    HXFindBestXColor(pDisplay, cmap, &xcolor);

	    //if bgcolor is not transparent, and/or if isUnderlined
	    // is TRUE, draw a rect (or line for underline) manually
	    // behind the entire text rather than letting textout do
	    // it for each letter (which is slower and sometimes leaves
	    // gaps if the letter spacing is slightly different than
	    // the os would natively space it:
	    COLORTYPE textBgColor = pContainer->getTextBackgroundColor();
	    if((BAD_RGB_COLOR != textBgColor  &&
		    TRANSPARENT_COLOR != textBgColor)  ||
		    pContainer->isUnderlined()  ||
		    pContainer->isStruckOut())
	    {
		LONG32 yLowerRightScaled =
			lScaledLowerRightYWithOffsets;

		//XXXEH- TODO: make sure UNIX has the same problem:
		//Fix for bug 6527; when double sized, we want to
		// double the padding on the right edge.  This
		// "padding" (which used to be +1 always) is
		// necessary when drawing a rect in Windows (& UNIX?) because
		// PaintRectangle draws up to, but not including,
		// the right edge:
		LONG32 additionalWidth = 1;
		if(lLowerRightYWithOffsets != 0) //avoid div by 0.
		{
		    additionalWidth =
			yLowerRightScaled /
			lLowerRightYWithOffsets;
		}
		if(additionalWidth < 1)
		{
		    additionalWidth=1;
		}

		UINT32 bgColor = 0;
		if(BAD_RGB_COLOR != textBgColor  &&
			TRANSPARENT_COLOR != textBgColor)
		{
		    XColor bgColor;
		    convertCOLORTYPEtoXVisualFormat(textBgColor,
						    bgColor);

		    // get color
		    HXFindBestXColor(pDisplay, cmap, &bgColor);

		    XLockDisplay(pDisplay);
		    XSetForeground(pDisplay, gc, bgColor.pixel);
		    XSetBackground(pDisplay, gc, 0);

		    XFillRectangle(pDisplay, pixmap, gc,
			    xScaled,
			    yScaled,
			    //In UNIX, width and height are used
			    // unlike in Windows where absolute lower
			    // right corner position is used, so we
			    // have to subtract the upper left corner:
			    (lScaledLowerRightXWithOffsets-xScaled) +
				    additionalWidth,
			    (yLowerRightScaled-yScaled)+1);
		    XUnlockDisplay(pDisplay);

		    UINT32 colors[1];
		    colors[0] = bgColor.pixel;
		}
		//Do underlining manually now (due to
		// possible gaps if alternate font has to be
		// used and forced spacing is not quite as os
		// calculates it):
		if(pContainer->isUnderlined())
		{
		    INT32 thickness =
			    (yLowerRightScaled - yScaled) / 12;
		    if(thickness < 1)
		    {
			thickness = 1;
		    }
		    if(pContainer->isBold())
		    {
			thickness += (thickness/2);
		    }

		    XLockDisplay(pDisplay);
		    XSetForeground(pDisplay, gc, xcolor.pixel);
		    XSetBackground(pDisplay, gc, 0); 		    //XXXEH-Is this needed?:

		    
		    XFillRectangle(pDisplay, pixmap, gc,
			    xScaled,
			    yLowerRightScaled - thickness + 1,
			    lScaledLowerRightXWithOffsets-xScaled + 
				   (ULONG32) fXScaleFactor,
			    thickness);
		    XUnlockDisplay(pDisplay);
		}
		//Do strike-through manually now (due to
		// possible gaps if alternate font has to be
		// used and forced spacing is not quite as os
		// calculates it):
		if(pContainer->isStruckOut())
		{
		    INT32 thickness =
			    (yLowerRightScaled - yScaled) / 18;
		    if(thickness < 1)
		    {
			thickness = 1;
		    }

		    XLockDisplay(pDisplay);
		    XSetForeground(pDisplay, gc, xcolor.pixel);
		    XSetBackground(pDisplay, gc, 0);		    //XXXEH-Is this needed?:


		    XFillRectangle(pDisplay, pixmap, gc,
			    xScaled,
			    yScaled + ((yLowerRightScaled - yScaled) / 2),
			    lScaledLowerRightXWithOffsets-xScaled + 
				   (ULONG32) fXScaleFactor,
			    thickness);
		    XUnlockDisplay(pDisplay);
		}
	    }

	    char* textbuffer=pContainer->getBuffer();
	    char buf[3]; /* Flawfinder: ignore */  //[0] holds char to be drawn (or [0] and
				// [1] do if DBCS), and [1] (or [2] if 
				// 2-byte char) holds '\0';
	    buf[1] = buf[2] = '\0';
	    INT16 bufLen = 0;

	    if (textbuffer && pContainer->getTextLengthPlus1() < 1)
	    {
		continue; // while (pos)
            }
	
	    ULONG32 ulCharset = pContainer->getFontCharset();
	    LONG32 cx = xScaled;
	    LONG32 cy = yScaled + fs->ascent - 2;

	    XLockDisplay(pDisplay);
	    XSetForeground(pDisplay, gc, xcolor.pixel);
	    XUnlockDisplay(pDisplay);

	    // draw each character @ offset in x from the first:
	    do
	    {
		char ch = *textbuffer;
		UINT16 usCh = ch;
		buf[0] = ch;
		bufLen = 1;
		if (IS_MULTI_BYTE_CHARSET(ulCharset))
		{
		    if (IS_UNICODE_CHARSET(ulCharset) ||
			(UCHAR)ch >= DBCS_MIN_LEAD_BYTE_VAL)
		    {
		       // ch is lead byte of a 2-byte char.
			textbuffer++;
			ch = *textbuffer;
			bufLen = 2; //is a 2-byte char.
			buf[1] = ch; //==trail byte.
			usCh <<= 8; //move lead byte into high byte.
			usCh |= ch; //put trail byte into low byte.
		    }
		}
		//XXXEH- test this DBCS stuff^^!!!

		ULONG32 ulLookupCharWidth = GET_CHAR_WIDTH(pContainer, usCh);
		if(bWindowWidthIsNotTheOriginalValue)
		{
		    ulLookupCharWidth =
			    LONG32(((float)ulLookupCharWidth *
			    (float)lWindowWidth) /
			    (float)pTextWindow->getWidth());
		}

		//First, horizontally center the character inside
		// its bounding box in case its native width is
		// noticably different from its cross-platform-
		// table-assigned width:
		LONG32 lXPlatWidth = (LONG32)ulLookupCharWidth;
		LONG32 lNativeWidth = (bufLen>1?
			//use fixed x-plat width for double byte chars:
			lXPlatWidth:
			(LONG32)XTextWidth(  fs, buf, 1) );

		LONG32 lCrossPlatVsNativeWidthOffset = 0L;
		if(lNativeWidth  &&  //Should never be 0, but...
			lXPlatWidth!=lNativeWidth) 
		{
		    //Note: we should not adjust lNativeWidth by any
		    // scale factor even if bWindowWidthIsNotTheOriginalValue
		    // because that's already been accounted for in the font
		    // point size.

		    //Note: this can be negative!:
		    lCrossPlatVsNativeWidthOffset =
			    (lXPlatWidth-lNativeWidth)/2;
		}

		XDrawString(pDisplay, pixmap, gc,
			cx + lCrossPlatVsNativeWidthOffset,
			cy,
			buf, bufLen);

		cx += ulLookupCharWidth;

	    } while(*(++textbuffer));

	    pContainer->textHasChangedSinceLastDraw(FALSE);

	    //Since OnTimeSync()/OnTimeSynch() may not yet have been
	    //called, we need to time-bounds check:

	} // if (pContainer->textHasChangedSinceLastDraw())
    } // while(pos)

    if (bFreeColors)
    {
	UINT32 colors[1];
	colors[0] = xcolor.pixel;
    }

    XLockDisplay(pDisplay);
    XFreeGC(pDisplay, gc);
    XUnlockDisplay(pDisplay);

    // now blt this information to the screen

    IHXVideoSurface* pVideoSurface = (IHXVideoSurface*)pEvent->param1;
    
    HX_ASSERT(NULL != pVideoSurface);
    if(NULL == pVideoSurface)
    {
	return FALSE; //no draw.
    }

    pVideoSurface->AddRef();

    //avoid race condition when re-allocating offscreen bitmap:
    // TODO: if (!pTextWindow->m_bHandlingWindowResizing)
	LONG32 w, h;

	w = pTextWindow->getWindowWidth();
 	h = pTextWindow->getWindowHeight();

	XLockDisplay(pDisplay);
	XImage *pImage = XGetImage(pDisplay, pixmap,
		0, 0, w, h,
		AllPlanes,
		ZPixmap);
	XUnlockDisplay(pDisplay);

	CHXxRect destRect, srcRect;
	destRect.SetRect(0, 0, w, h);
	srcRect.SetRect(0, 0, w, h);
        pEvent->result = pVideoSurface->Blt(
		    (UCHAR *)pImage->data,
		    pBitmapInfoHeader,
		    destRect, 
		    srcRect);

        XDestroyImage(pImage);
	
    HX_RELEASE(pVideoSurface);
	
    return TRUE;
}


/////////////////////////////////////////////////////////////////////////////
// Function:
//  BOOL
//  PaintBackground
//  (
//  	TextWindow* pTextWindow,
//      Display*    pDisplay,
//      Colormap    cmap,
//      Pixmap      pixmap
//  )
//
// Purpose:
//  This function paints the background of the window
//

static void
PaintBackground(TextWindow* pTextWindow, Display* pDisplay, Colormap cmap, Pixmap pixmap)
{
    XColor bgColor;
    convertCOLORTYPEtoXVisualFormat(pTextWindow->getBackgroundColor(),
				    bgColor);

    // get color
    HXFindBestXColor(pDisplay, cmap, &bgColor);

    XGCValues gcv;
    gcv.background = 0;
    gcv.foreground = bgColor.pixel;
    XLockDisplay(pDisplay);
    GC gc = XCreateGC(pDisplay, pixmap, GCForeground | GCBackground, &gcv);

    XFillRectangle(pDisplay, pixmap, gc,
		   0, 0,
		   // Note: this used to use getWidth and getHeight
		   pTextWindow->getWindowWidth(),
		   pTextWindow->getWindowHeight());
    XUnlockDisplay(pDisplay);

    UINT32 colors[1];
    colors[0] = bgColor.pixel;

    // TODO: save and reuse this GC?
    XLockDisplay(pDisplay);
    XFreeGC(pDisplay, gc);
    XUnlockDisplay(pDisplay);

    return;
}

// TODO KPW: this code really should use the resource database
// to allow users to specify a particular fontSpec for 
// each face

static struct { const char *face; const char *family; } fontMap[] = {
    { "[default]", "times" }, // default if no map found, must be first
    { "times", "times" },
    { "courier", "courier" },
    { "courier new", "courier" },
    { "arial", "helvetica" },
    { "arial black", "helvetica" },
    { "arial narrow", "helvetica" },
    { "arial rounded MT bold", "helvetica" },
    { "helvetica", "helvetica" },
    { NULL, NULL }
};
/*  XXXEH: all recognized font names in RT should be added
    to the above struct once the appropriate "backup" font face has been
    determined. */

static const char*
MapFaceToFamily(const char *face)
{
   register int i;

   for (i = 0; NULL != face   &&  fontMap[i].face != NULL; i++)
   {
       if (strcasecmp(face, fontMap[i].face) == 0)
       {
	   return(fontMap[i].family);
       }
   }

   // return the default
   return(fontMap[0].family);
}

// Note: this may return NULL

static XFontStruct*
GetFontStruct(Display *pDisplay, TextWindow* pTextWindow,
	      TextContainer* pContainer,
	      float fYScaleFactor)
{
    if (!pTextWindow)
    {
	return NULL;
    }

    Dict* pDict = pTextWindow->m_font_dict;
    char fontSpec[1024]; /* Flawfinder: ignore */
    const char* pFontFamily = NULL;
    const char* pAlternateFontFamily = NULL;
    XFontStruct* fs = NULL;

    pFontFamily = pContainer->getFontFaceString(
	    pTextWindow->getMajorContentVersion(),
	    pTextWindow->getMinorContentVersion());

    pAlternateFontFamily = MapFaceToFamily(
	    pContainer->getFontFaceString(
	    pTextWindow->getMajorContentVersion(),
	    pTextWindow->getMinorContentVersion()));

    ULONG32 ulCharset = pContainer->getFontCharset();

    //XXXEH: this is a half-*ssed attempt to support CJK fonts.  This will
    // do at least as good a job as prior code (that rendered all CJK chars
    // using Latin-1 characters, i.e, it was unreadable):
    if (IS_MULTI_BYTE_CHARSET(ulCharset))
    {
	sprintf(fontSpec, "-*-%s-%s-%s-*--%ld-*-75-75-*-*-*-%s", /* Flawfinder: ignore */
		//ignore face name in this build; just load any available
		// font in this charset
		"fixed",
		pContainer->isBold() ? "bold" : "medium",
		pContainer->isItalicized() ? "i" : "r",
		(INT32)(pContainer->getFontPointSize() * fYScaleFactor),
		(ulCharset & HX_JAPANESE_CHARSET?"jisx0208.1990-0":
			(ulCharset & HX_KOREAN_CHARSET?
// /XXXEH- for plain text, we need to use this only if user specified
// "ksc5601" or "hangeul" charset, but not "johab"; need to find out what to
// use for "johab":
				"KSC5601.1987-0": 
				(HX_CHINESE_CHARSET?
				//XXXEH: CNSxxx is Taiwan charset but most
				// likely doesn't map exactly to big5; need
				// more research to find big5 charset:
					(ulCharset==CHARSET__big5?
// /XXXEH- Doh!!: I think these are backwards; we need to fix this, however,
// only for plain text and (maybe) for new RealText version:
					 "GB2312.1980-0":"CNS11643.1986-1"):
					"*" /*:load whatever if not CJK.*/
				)
			)
		) );
    }
    else
    {
	sprintf(fontSpec, "-*-%s-%s-%s-*--%ld-*-75-75-*-*-*-*", /* Flawfinder: ignore */
		pFontFamily,
		pContainer->isBold() ? "bold" : "medium",
		pContainer->isItalicized() ? "i" : "r",
		(INT32)(pContainer->getFontPointSize() * fYScaleFactor));
    }

    Dict_entry* ent = pDict->find(fontSpec);
    if (ent)
    {
	fs = (XFontStruct*)ent->obj;
    }
    else
    {
	XLockDisplay(pDisplay);
	fs = XLoadQueryFont(pDisplay, fontSpec);
	XUnlockDisplay(pDisplay);

	if (fs)
	{
	    pDict->enter(fontSpec, fs);
	}
	else if(!IS_MULTI_BYTE_CHARSET(ulCharset))
	{
	    // /Helps minimize effect of PR 33172:
	    // We want to put the originally-requested font spec
	    // into the dictionary with the XFontStruct of the *alternate*
	    // one so that, next time we try to load the same font spec,
	    // we end up getting the alternate one out of the dict.
	    // (The true fix for PR 33172 is to delete the entries in the
	    // dict prior to deleting the dict);
	    char szAlternateFontSpec[1024]; /* Flawfinder: ignore */
	    sprintf(szAlternateFontSpec, "-*-%s-%s-%s-*--%ld-*-75-75-*-*-*-*", /* Flawfinder: ignore */
		    pAlternateFontFamily,
		    pContainer->isBold() ? "bold" : "medium",
		    pContainer->isItalicized() ? "i" : "r",
		    (INT32)(pContainer->getFontPointSize() * fYScaleFactor));
	    XLockDisplay(pDisplay);
	    fs = XLoadQueryFont(pDisplay, szAlternateFontSpec);
	    XUnlockDisplay(pDisplay);
	    if (fs)
	    {
		pDict->enter(fontSpec, fs);
	    }
	}
    }

    // if it's not in the dict and we couldn't load it substitute either
    // "times". "helvetica", or "courier", depending on the font face's
    // RT-defined "spacing" similarities to one of those font faces.
    // XXXEH: note: if a CJK (DBCS) charset is specified and the load query
    // font failed for the given point size, that doesn't mean it would
    // for sure fail at other point sizes, so we need to special case those
    // and maintain the charsets when for-looping through the point size
    // queries:
    int ulFontPointSize = pContainer->getFontPointSize() -1;
    INT32 lScaledHeight = (INT32)((float)ulFontPointSize * fYScaleFactor);
    while (!fs && lScaledHeight >= 8 )
    {
	ULONG32 ulFontFaceIndex = pContainer->getFontFace(); 
         sprintf(fontSpec, "-*-%s-%s-%s-*--%ld-*-75-75-*-*-*-*", /* Flawfinder: ignore */
		(ulFontFaceIndex&USES_COURIER_SPACING?"courier":
		    (ulFontFaceIndex&USES_ARIAL_SPACING?"helvetica":
			"times")),
		"medium", "r",  lScaledHeight);

	 Dict_entry* ent = pDict->find(fontSpec);
	 if (ent)
	 {
	     fs = (XFontStruct*)ent->obj;
	 }
	 else
	 {
	     XLockDisplay(pDisplay);
	     fs = XLoadQueryFont(pDisplay, fontSpec);
	     XUnlockDisplay(pDisplay);
	     if (fs)
	       pDict->enter(fontSpec, fs);
	 }

	 lScaledHeight--;
    }

    return fs;
}
#endif /* _BEOS */
