//	Zinc Interface Library - O_DSP.CPP
//	COPYRIGHT (C) 1990-1995.  All Rights Reserved.
//	Zinc Software Incorporated.  Pleasant Grove, Utah  USA
/* This file is part of OpenZinc

OpenZinc is free software: You can redistribute it and/or modify it
 under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the license or
 (at your option) any later version.

OpenZinc is distributed in the hope that it will be useful,
but without ANY WARRANTY; without even the implied warranty of
MARCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lessor Public license for more details

You should have received a copy of the GNU Lessor Public License
along with OpenZinc. If not, see <http://www.gnu.org/licenses/>.
*/


// Work around for IBM C Set++ compiler bug.
#if defined(__IBMCPP__)
#	if __IBMCPP__ <= 210
#		define GpiQueryColorData	GpiQueryColordata
#	endif
#endif

#define INCL_DOSMISC
#define INCL_GPI
#define INCL_WINSYS
#define INCL_WINERRORS
#define INCL_WINPOINTERS
#define INCL_WINWINDOWMGR
#define INCL_WINFRAMEMGR
#include <malloc.h>
#include <stdio.h>
#include "ui_dsp.hpp"

static int _virtualCount = 0;
static UI_REGION _virtualRegion;

HPS UI_OS2_DISPLAY::hps = 0;
FONTMETRICS UI_OS2_DISPLAY::fontTable[ZIL_MAXFONTS];

static ZIL_ICHAR _operatingSystem[] = { 'O','S','/','2',0 };
static ZIL_ICHAR _windowingSystem[] = { 'P','M',0 };
static ZIL_ICHAR _codeSet[] = { 'I','B','M','_',0 };

// Initialize the bitmap information structures.
static BITMAPINFO2 *_bitmapInfo = ZIL_NULLP(BITMAPINFO2);
static BITMAPINFOHEADER2 *_bitmapHeaderInfo = ZIL_NULLP(BITMAPINFOHEADER2);
static BITMAPINFO2 *_monoBitmapInfo = ZIL_NULLP(BITMAPINFO2);
static BITMAPINFOHEADER2 *_monoBitmapHeaderInfo = ZIL_NULLP(BITMAPINFOHEADER2);

// ----- UI_OS2_DISPLAY -----------------------------------------------------

UI_OS2_DISPLAY::UI_OS2_DISPLAY(void) :
	UI_DISPLAY(FALSE, _operatingSystem, _windowingSystem), maxColors(16)
{
	hab = WinInitialize(0);
	columns = WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN);
	lines = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);
	preSpace = 2, postSpace = 0;

	HPS desktopPS = WinGetPS(HWND_DESKTOP);

	// Work around for OS/2 2.0 bug.
	LONG OS2VersionMajor;
	LONG OS2VersionMinor;
	DosQuerySysInfo(QSV_VERSION_MAJOR, QSV_VERSION_MAJOR, &OS2VersionMajor, sizeof(LONG));
	DosQuerySysInfo(QSV_VERSION_MINOR, QSV_VERSION_MINOR, &OS2VersionMinor, sizeof(LONG));
	int codePage = 850;
	if (OS2VersionMajor == 20 && OS2VersionMinor == 00)
	{
		cellWidth = 7;
		cellHeight = 24;
	}
	else
	{
		// Determine the cell height and width.
		FONTMETRICS defaultFont;
		GpiQueryFontMetrics(desktopPS, sizeof(FONTMETRICS), &defaultFont);

		cellWidth = defaultFont.lAveCharWidth + 1;
		cellHeight = defaultFont.lMaxBaselineExt
			+ defaultFont.lExternalLeading			// Space between text rows
			+ preSpace + postSpace						// Above / below border
			+ 6; 												// Border Space
		codePage = defaultFont.usCodePage;
	}

	strcpy(codeSet, _codeSet);
	itoa(codePage, &codeSet[strlen(codeSet)], 10, 0);

	// Initialize the font table.
	for (int i = 0; i < ZIL_MAXFONTS; i++)
		memset(&fontTable[i], 0, sizeof(FONTMETRICS));

	// Set the small font (others use OS/2 system settings).
#if !defined(ZIL_UNICODE)
	LONG maxFonts = 0;
	maxFonts = GpiQueryFonts(desktopPS, QF_PUBLIC, (PSZ)"Helv", &maxFonts, sizeof(FONTMETRICS), ZIL_NULLP(FONTMETRICS));

	FONTMETRICS *font = new FONTMETRICS[maxFonts];
	GpiQueryFonts(desktopPS, QF_PUBLIC, (PSZ)"Helv", &maxFonts, sizeof(FONTMETRICS), font);

	for (i = 0; i < maxFonts; i++)
	{
		if (font[i].sNominalPointSize == 80 && !fontTable[FNT_SMALL_FONT].lMatch)
		{
			fontTable[FNT_SMALL_FONT] = font[i];
			break;
		}
	}

	delete font;
#endif
	WinReleasePS(desktopPS);
	installed = TRUE;
}

UI_OS2_DISPLAY::~UI_OS2_DISPLAY(void)
{
	if (hab)
		WinTerminate(hab);
	if (_bitmapInfo)
	{
		delete _bitmapInfo;
		delete _bitmapHeaderInfo;
	}
	if (_monoBitmapInfo)
	{
		delete _monoBitmapInfo;
		delete _monoBitmapHeaderInfo;
	}
}

void UI_OS2_DISPLAY::Bitmap(ZIL_SCREENID screenID, int left, int top,
	int bitmapWidth, int bitmapHeight, const ZIL_UINT8 *bitmapArray,
	const UI_PALETTE *palette, const UI_REGION *clip, ZIL_BITMAP_HANDLE *_colorBitmap,
	ZIL_BITMAP_HANDLE *_monoBitmap)
{
	// Make sure there is a valid bitmap.
	ZIL_BITMAP_HANDLE colorBitmap = 0, monoBitmap = 0;
	if (_colorBitmap)
		colorBitmap = *_colorBitmap;
	if (_monoBitmap)
		monoBitmap = *_monoBitmap;
	if (!bitmapArray && !colorBitmap)
		return;

	// Virtualize the display. (This call set up UI_OS2_DISPLAY::hps.)
	VirtualGet(screenID, left, top, left + bitmapWidth - 1, top + bitmapHeight - 1);
	HRGN hRegion = 0;
	if (clip)
	{
		RECTL os2Region;
		os2Region.xLeft = clip->left - _virtualRegion.left;
		os2Region.yBottom = _virtualRegion.bottom - clip->bottom;
		os2Region.xRight = clip->right - _virtualRegion.left + 1;
		os2Region.yTop = _virtualRegion.bottom - clip->top + 1;
		hRegion = GpiCreateRegion(hps, 1, &os2Region);
		if (hRegion)
			GpiSetClipRegion(hps, hRegion, ZIL_NULLP(HRGN));
	}

	// Determine the bitmap position.
	POINTL point[4];
	point[0].x = left - _virtualRegion.left;
	point[0].y = _virtualRegion.bottom - top - bitmapHeight + 1;
	point[1].x = point[0].x + bitmapWidth - 1;
	point[1].y = point[0].y + bitmapHeight - 1;
	point[2].x = point[2].y = 0;
	point[3].x = bitmapWidth;
	point[3].y = bitmapHeight;

	// Convert the bitmap array then draw the bitmap.
	if (!colorBitmap)
		BitmapArrayToHandle(screenID, bitmapWidth, bitmapHeight, bitmapArray,
			palette, &colorBitmap, &monoBitmap);
	if (monoBitmap)
	{
		// OS2 3.0(Warp) fix.
		POINTL savePoint[4];
		savePoint[0] = point[0];
		savePoint[1] = point[1];
		savePoint[2] = point[2];
		savePoint[3] = point[3];

		GpiWCBitBlt(hps, monoBitmap, 4, point, ROP_SRCAND, BBO_IGNORE);

		// OS2 3.0(Warp) fix.
		point[0] = savePoint[0];
		point[1] = savePoint[1];
		point[2] = savePoint[2];
		point[3] = savePoint[3];

		GpiWCBitBlt(hps, colorBitmap, 4, point, ROP_SRCINVERT, BBO_IGNORE);
	}
	else if (colorBitmap)
		GpiWCBitBlt(hps, colorBitmap, 4, point, ROP_SRCCOPY, BBO_IGNORE);
	if (monoBitmap)
	{
		if (!_monoBitmap)
			GpiDeleteBitmap(monoBitmap);
		else
			*_monoBitmap = monoBitmap;
	}
	if (colorBitmap)
	{
		if (!_colorBitmap)
			GpiDeleteBitmap(colorBitmap);
		else
			*_colorBitmap = colorBitmap;
	}

	// Un-virtualize the display.
	if (hRegion)
	{
		GpiSetClipRegion(hps, ZIL_NULLH(HRGN), ZIL_NULLP(HRGN));
		GpiDestroyRegion(hps, hRegion);
	}
	VirtualPut(screenID);
}

void UI_OS2_DISPLAY::BitmapArrayToHandle(ZIL_SCREENID screenID, int bitmapWidth,
	int bitmapHeight, const ZIL_UINT8 *bitmapArray, const UI_PALETTE *,
	ZIL_BITMAP_HANDLE *colorBitmap, ZIL_BITMAP_HANDLE *monoBitmap)
{
	// Make sure there is a valid bitmap.
	if (!bitmapArray || *colorBitmap)
		return;

	if (!_bitmapInfo)
	{
		_bitmapInfo = (BITMAPINFO2 *)new ZIL_INT8[sizeof(BITMAPINFO2) + 255 * sizeof(RGB2)];
		memset(_bitmapInfo, 0, sizeof(BITMAPINFO2) + 255 * sizeof(RGB2));
		_bitmapInfo->cbFix = sizeof(BITMAPINFO2) - sizeof(RGB2);
		_bitmapInfo->cPlanes = 1;
		_bitmapInfo->cBitCount = 8;
		_bitmapInfo->ulCompression = BCA_UNCOMP;
		_bitmapInfo->usUnits = BRU_METRIC;
		_bitmapInfo->usRecording = BRA_BOTTOMUP;
		_bitmapInfo->usRendering = BRH_NOTHALFTONED;
		_bitmapInfo->ulColorEncoding = BCE_RGB;
		for (int i = 0; i < maxColors; i++)
		{
			ZIL_COLOR color = MapColor(&colorMap[i], TRUE);
			_bitmapInfo->argbColor[i].bBlue = (color & 0x000000FF);
			_bitmapInfo->argbColor[i].bGreen = (color & 0x0000FF00) >> 8;
			_bitmapInfo->argbColor[i].bRed = (color & 0x00FF0000) >> 16;
		}

		_bitmapHeaderInfo = new BITMAPINFOHEADER2;
		memset(_bitmapHeaderInfo, 0, sizeof(BITMAPINFOHEADER2));
		_bitmapHeaderInfo->cbFix = sizeof(BITMAPINFOHEADER2);
		_bitmapHeaderInfo->cPlanes = 1;
		_bitmapHeaderInfo->cBitCount = 8;
	}

	// Set the bitmap width and height.
	_bitmapInfo->cx = _bitmapHeaderInfo->cx = bitmapWidth;
	_bitmapInfo->cy = _bitmapHeaderInfo->cy = bitmapHeight;

	// alignWidth pads the bitmap scan-lines for display purposes.
	int alignWidth = ((bitmapWidth + sizeof(ULONG) - 1) / sizeof(ULONG)) * sizeof(ULONG);

	// AND then XOR the bitmap onto the screen.
	int useBackground = FALSE;
	ZIL_UINT8 *bitmap = new ZIL_UINT8[alignWidth * bitmapHeight];
	VirtualGet(screenID, 0, 0, bitmapWidth - 1, bitmapHeight - 1);
	for (int mix = monoBitmap ? 0 : 1; mix <= 1; mix++)
	{
		const ZIL_UINT8 *tBitmapArray = bitmapArray;
		for (int y = 0; y < bitmapHeight; y++)
		{
			ZIL_UINT8 *color = &bitmap[(bitmapHeight - y - 1) * alignWidth];
			for (int x = 0; x < bitmapWidth; x++, tBitmapArray++)
				if (*tBitmapArray == BACKGROUND)
				{
					color[x] = mix ? 0x00 : 0x0F;
					useBackground = TRUE;
				}
				else if (mix)
					color[x] = *tBitmapArray;
				else
					color[x] = 0x00;
			for (x = bitmapWidth; x < alignWidth; x++)
				color[x] = 0x00;
		}
		if (mix && colorBitmap)
			*colorBitmap = GpiCreateBitmap(hps, _bitmapHeaderInfo, CBM_INIT, (PBYTE)bitmap, _bitmapInfo);
		else if (useBackground && !mix && monoBitmap)
			*monoBitmap = GpiCreateBitmap(hps, _bitmapHeaderInfo, CBM_INIT, (PBYTE)bitmap, _bitmapInfo);
	}
	VirtualPut(screenID);
	delete bitmap;
}

void UI_OS2_DISPLAY::BitmapHandleToArray(ZIL_SCREENID, ZIL_BITMAP_HANDLE colorBitmap,
	ZIL_BITMAP_HANDLE monoBitmap, int *bitmapWidth, int *bitmapHeight, ZIL_UINT8 **bitmapArray)
{
	SIZEL size;
	BITMAPINFOHEADER bitmapInfoHeader;
	GpiQueryBitmapParameters(colorBitmap, &bitmapInfoHeader);
	size.cx = bitmapInfoHeader.cx;
	size.cy = bitmapInfoHeader.cy;
	DEVOPENSTRUC devOpenData = { 0L, (PSZ)"DISPLAY", NULL, 0L, 0L, 0L, 0L, 0L, 0L };
	HDC memoryDC = DevOpenDC(hab, OD_MEMORY, (PSZ)"*", 5L, (PDEVOPENDATA)&devOpenData, ZIL_NULLH(HDC));
	HPS memoryPS = GpiCreatePS(hab, memoryDC, &size, PU_PELS | GPIT_MICRO | GPIA_ASSOC);
	GpiSetBitmap(memoryPS, colorBitmap);

	*bitmapWidth = size.cx;
	*bitmapHeight = size.cy;
	if (!(*bitmapArray))
		*bitmapArray = new ZIL_UINT8[size.cx * size.cy];
	ZIL_UINT8 *pixel = *bitmapArray;
	POINTL coord;
	for (coord.y = size.cy - 1; coord.y >= 0; coord.y--)
		for (coord.x = 0; coord.x < size.cx; coord.x++)
		{
			ZIL_COLOR color = GpiQueryRGBColor(memoryPS, LCOLOPT_REALIZED,
				GpiQueryPel(memoryPS, &coord));
			*pixel = 0;
			for (int i = 0; i <= MAX_COLORMAP_INDEX; i++)
				if (color == MapColor(&colorMap[i], TRUE))
				{
					*pixel = i;
					break;
				}
			pixel++;
		}

	if (monoBitmap)
	{
		GpiSetBitmap(memoryPS, monoBitmap);
		pixel = *bitmapArray;
		for (coord.y = size.cy - 1; coord.y >= 0; coord.y--)
			for (coord.x = 0; coord.x < size.cx; coord.x++)
			{
				ZIL_COLOR color = GpiQueryRGBColor(memoryPS, LCOLOPT_REALIZED,
					GpiQueryPel(memoryPS, &coord));
				if (color == MapColor(&colorMap[WHITE], TRUE))
					*pixel = 0xFF;
				pixel++;
			}
	}

	GpiSetBitmap(memoryPS, ZIL_NULLH(ZIL_BITMAP_HANDLE));
	GpiDestroyPS(memoryPS);
	DevCloseDC(memoryDC);
}

void UI_OS2_DISPLAY::DestroyBitmapHandle(ZIL_SCREENID, ZIL_BITMAP_HANDLE *colorBitmap, ZIL_BITMAP_HANDLE *monoBitmap)
{
	// Free the color and monochrome bitmap handles.
	if (colorBitmap && *colorBitmap)
	{
		GpiDeleteBitmap(*colorBitmap);
	 	*colorBitmap = 0;
	}
	if (monoBitmap && *monoBitmap)
	{
		GpiDeleteBitmap(*monoBitmap);
	 	*monoBitmap = 0;
	}
}

void UI_OS2_DISPLAY::Ellipse(ZIL_SCREENID screenID, int column, int line,
	int startAngle, int endAngle, int xRadius, int yRadius,
	const UI_PALETTE *palette, int fill, int _xor, const UI_REGION *clip)
{
	// Virtualize the display. (This call set up UI_OS2_DISPLAY::hps.)
	int left = column - xRadius + 1;
	int top = line - yRadius + 1;
	int right = column + xRadius - 1;
	int bottom = line + yRadius - 1;
	while (startAngle < 0)
		startAngle += 360, endAngle += 360;
	if (startAngle > 360)
		startAngle %= 360;
	while (endAngle < 0)
		endAngle += 360;
	if (endAngle > 360)
		endAngle %= 360;

	VirtualGet(screenID, left, top, right, bottom);
	HRGN hRegion = 0;
	if (clip)
	{
		RECTL os2Region;
		os2Region.xLeft = clip->left - _virtualRegion.left;
		os2Region.yBottom = _virtualRegion.bottom - clip->bottom;
		os2Region.xRight = clip->right - _virtualRegion.left + 1;
		os2Region.yTop = _virtualRegion.bottom - clip->top + 1;
		hRegion = GpiCreateRegion(hps, 1, &os2Region);
		if (hRegion)
			GpiSetClipRegion(hps, hRegion, ZIL_NULLP(HRGN));
	}

	// Set the color and xor combinations.
	ZIL_COLOR colorForeground, colorBackground;
	if (palette)
	{
		colorForeground = GpiQueryColor(hps);
		colorBackground = GpiQueryBackColor(hps);
		GpiSetColor(hps, (palette->fillPattern == PTN_SYSTEM_COLOR) ?
			palette->colorForeground : MapColor(palette, FALSE));
		GpiSetBackColor(hps, (palette->fillPattern == PTN_SYSTEM_COLOR) ?
			palette->colorBackground : MapColor(palette, TRUE));
	}
	LONG mix;
	if (_xor)
	{
		mix = GpiQueryMix(hps);
		GpiSetMix(hps, FM_XOR);
	}

	// Compute the arc points.
	ARCPARAMS arc;
	arc.lP = xRadius;
	arc.lQ = yRadius;
	arc.lR = 0;
	arc.lS = 0;
	GpiSetArcParams(hps, &arc);

	POINTL os2Position;
	os2Position.x = column - _virtualRegion.left;
	os2Position.y = _virtualRegion.bottom - line;
	GpiMove(hps, &os2Position);

	// Draw the arc.
	if (startAngle == 0 && endAngle == 360)
		GpiFullArc(hps, fill ? DRO_OUTLINEFILL : DRO_OUTLINE, MAKEFIXED(1, 0));
	else if (fill)
	{
		if (palette)
			GpiSetColor(hps, (palette->fillPattern == PTN_SYSTEM_COLOR) ?
				palette->colorBackground : MapColor(palette, FALSE));
		GpiBeginArea(hps, BA_ALTERNATE);
		GpiPartialArc(hps, &os2Position, MAKEFIXED(1, 0),
			MAKEFIXED(startAngle, 0), MAKEFIXED(endAngle - startAngle, 0));
		GpiLine(hps, &os2Position);
		GpiEndArea(hps);
	}
	else
	{
		GpiPartialArc(hps, &os2Position, MAKEFIXED(1, 0),
			MAKEFIXED(startAngle, 0), MAKEFIXED(endAngle, 0));
		GpiLine(hps, &os2Position);
	}

	// Reset the color and xor combinations.
	if (palette)
	{
		GpiSetBackColor(hps, colorBackground);
		GpiSetColor(hps, colorForeground);
	}
	if (_xor)
		GpiSetMix(hps, mix);

	// Un-virtualize the display.
	if (hRegion)
	{
		GpiSetClipRegion(hps, ZIL_NULLH(HRGN), ZIL_NULLP(HRGN));
		GpiDestroyRegion(hps, hRegion);
	}
	VirtualPut(screenID);
}

void UI_OS2_DISPLAY::IconArrayToHandle(ZIL_SCREENID screenID, int iconWidth,
	int iconHeight, const ZIL_UINT8 *iconArray, const UI_PALETTE *palette,
	ZIL_ICON_HANDLE *icon)
{
	// Make sure there is a valid icon.
	if (!iconArray || *icon)
		return;

	int ICON_WIDTH;
	int ICON_HEIGHT;
	if (columns > 800 && lines > 600)
	{
		ICON_WIDTH = 40;
		ICON_HEIGHT = 40;
	}
	else
	{
		ICON_WIDTH = 32;
		ICON_HEIGHT = 32;
	}

	ZIL_UINT8 *newArray = ZIL_NULLP(ZIL_UINT8);
	if (iconWidth < ICON_WIDTH || iconHeight < ICON_HEIGHT)
	{
		int xMargin = (ICON_WIDTH - iconWidth) / 2;
		int yMargin = (ICON_HEIGHT - iconHeight) / 2;
		int index = 0;

		newArray = new ZIL_UINT8[ICON_WIDTH * ICON_HEIGHT];
		memset(newArray, 0xFF, ICON_WIDTH * ICON_HEIGHT);
		for (int y = 0; y < ICON_HEIGHT; y++)
		{
			if (y >= yMargin && y < yMargin + iconHeight)
				for (int x = 0; x < ICON_WIDTH; x++)
					if (x >= xMargin && x < xMargin + iconWidth)
						newArray[y * ICON_WIDTH + x] = iconArray[index++];
		}
		iconArray = newArray;
		iconWidth = ICON_WIDTH;
		iconHeight = ICON_HEIGHT;
	}

	if (!_monoBitmapInfo)
	{
		_monoBitmapInfo = (BITMAPINFO2 *)new ZIL_INT8[sizeof(BITMAPINFO2) + 255 * sizeof(RGB2)];
		memset(_monoBitmapInfo, 0, sizeof(BITMAPINFO2) + 255 * sizeof(RGB2));
		_monoBitmapInfo->cbFix = sizeof(BITMAPINFO2) - sizeof(RGB2);
		_monoBitmapInfo->cPlanes = 1;
		_monoBitmapInfo->cBitCount = 1;
		_monoBitmapInfo->ulCompression = BCA_UNCOMP;
		_monoBitmapInfo->usUnits = BRU_METRIC;
		_monoBitmapInfo->usRecording = BRA_BOTTOMUP;
		_monoBitmapInfo->usRendering = BRH_NOTHALFTONED;
		_monoBitmapInfo->ulColorEncoding = BCE_RGB;
		_monoBitmapInfo->argbColor[0].bBlue = 0x00;
		_monoBitmapInfo->argbColor[0].bGreen = 0x00;
		_monoBitmapInfo->argbColor[0].bRed = 0x00;
		_monoBitmapInfo->argbColor[1].bBlue = 0xFF;
		_monoBitmapInfo->argbColor[1].bGreen = 0xFF;
		_monoBitmapInfo->argbColor[1].bRed = 0xFF;

		_monoBitmapHeaderInfo = new BITMAPINFOHEADER2;
		memset(_monoBitmapHeaderInfo, 0, sizeof(BITMAPINFOHEADER2));
		_monoBitmapHeaderInfo->cbFix = sizeof(BITMAPINFOHEADER2);
		_monoBitmapHeaderInfo->cPlanes = 1;
		_monoBitmapHeaderInfo->cBitCount = 1;
	}

	// Set the bitmap width and height.
	_monoBitmapHeaderInfo->cPlanes = 1;
	_monoBitmapHeaderInfo->cBitCount = 1;
	_monoBitmapInfo->cx = _monoBitmapHeaderInfo->cx = iconWidth;
	_monoBitmapInfo->cy = _monoBitmapHeaderInfo->cy = iconHeight * 2;

	// alignWidth pads the bitmap scan-lines for display purposes.
	int lineWidth = (iconWidth + (8 * sizeof(ULONG)) - 1) / (8 * sizeof(ULONG)) * sizeof(ULONG);
	int size = lineWidth * iconHeight;

	VirtualGet(screenID, 0, 0, iconWidth - 1, iconHeight - 1);

	// Generate AND and XOR monochrome bitmaps.
	ZIL_UINT8 *bitmap = new ZIL_UINT8[size * 2];
	for (int _and = 0; _and <= 1; _and++)
	{
		const ZIL_UINT8 *tBitmapArray = iconArray;
		for (int y = 0; y < iconHeight; y++)
		{
			int offset = (iconHeight - y - 1) * lineWidth + _and * size;
			bitmap[offset] = 0x00;
			ZIL_UINT16 mask = 0x80;
			for (int x = 0; x < iconWidth; x++)
			{
				if (mask == 0x00)
				{
					offset++;
					bitmap[offset] = 0x00;
					mask = 0x80;
				}
				if (_and && *tBitmapArray++ == BACKGROUND)
						bitmap[offset] |= mask;
				mask = mask >> 1;
			}
		}
	}
	ZIL_BITMAP_HANDLE pointerBitmap = GpiCreateBitmap(hps, _monoBitmapHeaderInfo, CBM_INIT, (PBYTE)bitmap, _monoBitmapInfo);
	delete bitmap;

	ZIL_BITMAP_HANDLE colorBitmap = 0;
	BitmapArrayToHandle(screenID, iconWidth, iconHeight, iconArray,
		palette, &colorBitmap, ZIL_NULLP(ZIL_BITMAP_HANDLE));

	POINTERINFO pInfo;
	pInfo.fPointer = FALSE;
	pInfo.xHotspot = 15;
	pInfo.yHotspot = 16;
	pInfo.hbmPointer = pointerBitmap;
	pInfo.hbmColor = colorBitmap;
	pInfo.hbmMiniPointer = pointerBitmap;
	pInfo.hbmMiniColor = colorBitmap;

	*icon = WinCreatePointerIndirect(HWND_DESKTOP, &pInfo);
	GpiDeleteBitmap(pointerBitmap);
	GpiDeleteBitmap(colorBitmap);

	if (newArray)
		delete newArray;

	VirtualPut(screenID);
}

void UI_OS2_DISPLAY::IconHandleToArray(ZIL_SCREENID screenID, ZIL_ICON_HANDLE icon,
	int *iconWidth, int *iconHeight, ZIL_UINT8 **iconArray)
{
}

void UI_OS2_DISPLAY::DestroyIconHandle(ZIL_SCREENID, ZIL_ICON_HANDLE *icon)
{
	*icon = 0;
}

void UI_OS2_DISPLAY::Line(ZIL_SCREENID screenID, int column1, int line1,
	int column2, int line2, const UI_PALETTE *palette, int width, int _xor,
	const UI_REGION *clip)
{
	// Virtualize the display. (This call set up UI_OS2_DISPLAY::hps.)
	VirtualGet(screenID, MinValue(column1, column2), MinValue(line1, line2),
		MaxValue(column1, column2), MaxValue(line1, line2));
	HRGN hRegion = 0;
	if (clip)
	{
		RECTL os2Region;
		os2Region.xLeft = clip->left - _virtualRegion.left;
		os2Region.yBottom = _virtualRegion.bottom - clip->bottom;
		os2Region.xRight = clip->right - _virtualRegion.left + 1;
		os2Region.yTop = _virtualRegion.bottom - clip->top + 1;
		hRegion = GpiCreateRegion(hps, 1, &os2Region);
		if (hRegion)
			GpiSetClipRegion(hps, hRegion, ZIL_NULLP(HRGN));
	}

	// Set the color and xor combinations.
	ZIL_COLOR color;
	if (palette)
	{
		color = GpiQueryColor(hps);
		GpiSetColor(hps, (palette->fillPattern == PTN_SYSTEM_COLOR) ?
			palette->colorForeground : MapColor(palette, TRUE));
	}
	LONG mix;
	if (_xor)
	{
		mix = GpiQueryMix(hps);
		GpiSetMix(hps, FM_XOR);
	}

	if (width > 1)
		GpiBeginPath(hps, 1);

	// Draw the line.
	POINTL os2Position;
	os2Position.x = column1 - _virtualRegion.left;
	os2Position.y = _virtualRegion.bottom - line1;
	GpiMove(hps, &os2Position);	// Move to the starting position.
	os2Position.x = column2 - _virtualRegion.left;
	os2Position.y = _virtualRegion.bottom - line2;
	GpiLine(hps, &os2Position);	// Draw to the ending position.

	// Reset the color and xor combinations.
	if (width > 1)
	{
		GpiEndPath(hps);
		GpiSetLineWidthGeom(hps, width);
		GpiStrokePath(hps, 1, 0);
	}

	if (palette)
		GpiSetColor(hps, color);
	if (_xor)
		GpiSetMix(hps, mix);

	// Un-virtualize the display.
	if (hRegion)
	{
		GpiSetClipRegion(hps, ZIL_NULLH(HRGN), ZIL_NULLP(HRGN));
		GpiDestroyRegion(hps, hRegion);
	}
	VirtualPut(screenID);
}

ZIL_COLOR UI_OS2_DISPLAY::MapColor(const UI_PALETTE *palette, int foreground)
{
	ZIL_COLOR color = foreground ? palette->colorForeground : palette->colorBackground;
	LONG colorData[QCD_LCT_FORMAT + 1];
	GpiQueryColorData(hps, 1, colorData);
	if (palette->fillPattern == PTN_RGB_COLOR)
	{
		if (colorData[QCD_LCT_FORMAT] != LCOLF_RGB)
			GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, ZIL_NULLP(LONG));
	}
	else
	{
		if (colorData[QCD_LCT_FORMAT] == LCOLF_RGB)
			GpiCreateLogColorTable(hps, LCOL_RESET, LCOLF_CONSECRGB, 0, 0, ZIL_NULLP(LONG));
		if (palette->fillPattern == PTN_SYSTEM_COLOR)
			color = GpiQueryRGBColor(hps, LCOLOPT_REALIZED, color);
		else if (foreground)
			color = colorMap[color].colorForeground;
		else
			color = colorMap[color].colorBackground;
	}
	return (color);
}

void UI_OS2_DISPLAY::Polygon(ZIL_SCREENID screenID, int numPoints,
	const int *polygonPoints, const UI_PALETTE *palette, int fill, int _xor,
	const UI_REGION *clip)
{
	// Determine the maximum region that bounds the polygon.
	int left = 0xFFFF, top = 0xFFFF;
	int right = 0, bottom = 0;
	POINTL *os2Polygon = new POINTL[numPoints];
	for (int i = 0; i < numPoints; i++)
	{
		int column = polygonPoints[i*2];
		int line = polygonPoints[i*2+1];
		if (column < left)
			left = column;
		if (column > right)
			right = column;
		if (line < top)
			top = line;
		if (line > bottom)
			bottom = line;
	}

	// Virtualize the display. (This call set up UI_OS2_DISPLAY::hps.)
	VirtualGet(screenID, left, top, right, bottom);
	HRGN hRegion = 0;
	if (clip)
	{
		RECTL os2Region;
		os2Region.xLeft = clip->left - _virtualRegion.left;
		os2Region.yBottom = _virtualRegion.bottom - clip->bottom;
		os2Region.xRight = clip->right - _virtualRegion.left + 1;
		os2Region.yTop = _virtualRegion.bottom - clip->top + 1;
		hRegion = GpiCreateRegion(hps, 1, &os2Region);
		if (hRegion)
			GpiSetClipRegion(hps, hRegion, ZIL_NULLP(HRGN));
	}

	// Set up the polygon points.
	for (i = 0; i < numPoints; i++)
	{
		os2Polygon[i].x = polygonPoints[i*2] - _virtualRegion.left;
		os2Polygon[i].y = _virtualRegion.bottom - polygonPoints[i*2+1];
	}

	// Set the color and xor combinations.
	ZIL_COLOR colorForeground, colorBackground;
	if (palette)
	{
		colorForeground = GpiQueryColor(hps);
		colorBackground = GpiQueryBackColor(hps);
		GpiSetColor(hps, (palette->fillPattern == PTN_SYSTEM_COLOR) ?
			palette->colorForeground : MapColor(palette, TRUE));
		GpiSetBackColor(hps, (palette->fillPattern == PTN_SYSTEM_COLOR) ?
			palette->colorBackground : MapColor(palette, FALSE));
	}
	LONG mix;
	if (_xor)
	{
		mix = GpiQueryMix(hps);
		GpiSetMix(hps, FM_XOR);
	}

	// Draw the polygon.
	if (fill)
	{
		GpiBeginArea(hps, BA_ALTERNATE);
		GpiMove(hps, &os2Polygon[numPoints-1]);
		GpiPolyLine(hps, numPoints, os2Polygon);
		GpiEndArea(hps);
	}
	else
	{
		GpiMove(hps, &os2Polygon[0]);
		GpiPolyLine(hps, numPoints, os2Polygon);
	}

	// Reset the color and xor combinations.
	if (palette)
	{
		GpiSetBackColor(hps, colorBackground);
		GpiSetColor(hps, colorForeground);
	}
	if (_xor)
		GpiSetMix(hps, mix);

	// Un-virtualize the display.
	if (hRegion)
	{
		GpiSetClipRegion(hps, ZIL_NULLH(HRGN), ZIL_NULLP(HRGN));
		GpiDestroyRegion(hps, hRegion);
	}
	VirtualPut(screenID);
}

void UI_OS2_DISPLAY::Rectangle(ZIL_SCREENID screenID, int left, int top,
	int right, int bottom, const UI_PALETTE * palette, int width, int fill,
	int _xor, const UI_REGION *clip)
{
	// Virtualize the display. (This call set up UI_OS2_DISPLAY::hps.)
	VirtualGet(screenID, left, top, right, bottom);
	HRGN hRegion = 0;
	if (clip)
	{
		RECTL os2Region;
		os2Region.xLeft = clip->left - _virtualRegion.left;
		os2Region.yBottom = _virtualRegion.bottom - clip->bottom;
		os2Region.xRight = clip->right - _virtualRegion.left + 1;
		os2Region.yTop = _virtualRegion.bottom - clip->top + 1;
		hRegion = GpiCreateRegion(hps, 1, &os2Region);
		if (hRegion)
			GpiSetClipRegion(hps, hRegion, ZIL_NULLP(HRGN));
	}

	// Set the color and xor combinations.
	ZIL_COLOR color;
	if (palette)
	{
		color = GpiQueryColor(hps);
		GpiSetColor(hps, (palette->fillPattern == PTN_SYSTEM_COLOR) ?
			palette->colorForeground : MapColor(palette, TRUE));
	}
	LONG mix;
	LONG backMix;
	if (_xor)
	{
		mix = GpiQueryMix(hps);
		backMix = GpiQueryBackMix(hps);
		GpiSetMix(hps, FM_XOR);
		GpiSetBackMix(hps, BM_XOR);
//		GpiSetLineType(hps, LINETYPE_ALTERNATE);
	}

	// Draw the outer rectangle.
	POINTL os2Position1, os2Position2;
	os2Position1.x = left - _virtualRegion.left;
	os2Position1.y = _virtualRegion.bottom - bottom;
	os2Position2.x = right - _virtualRegion.left;
	os2Position2.y = _virtualRegion.bottom - top;
	for (int i = 0; i < width; i++)
	{
		GpiMove(hps, &os2Position1);
		os2Position1.x++;
		os2Position1.y++;
		GpiBox(hps, DRO_OUTLINE, &os2Position2, 0, 0);
		os2Position2.x--;
		os2Position2.y--;
	}
	
	// Fill the inner rectangle.
	if (fill)
	{
		GpiMove(hps, &os2Position1);
		os2Position1.x++;
		os2Position1.y++;
		ZIL_COLOR colorBackground;
		if (palette)
			colorBackground = (palette->fillPattern == PTN_SYSTEM_COLOR) ?
				palette->colorBackground : MapColor(palette, FALSE);
		else
			colorBackground = GpiQueryBackColor(hps);
		if (colorBackground == CLR_ERROR)
			colorBackground = SYSCLR_ENTRYFIELD;
		GpiSetColor(hps, colorBackground);
		GpiBox(hps, DRO_FILL, &os2Position2, 0, 0);
		os2Position2.x--;
		os2Position2.y--;
	}

	// Reset the xor combination and fill the inner rectangle.
	if (_xor)
	{
		GpiSetMix(hps, mix);
		GpiSetBackMix(hps, backMix);
//		GpiSetLineType(hps, LINETYPE_DEFAULT);
	}

	// Reset the clip region and color combination.
	if (palette)
		GpiSetColor(hps, color);

	// Un-virtualize the display.
	if (hRegion)
	{
		GpiSetClipRegion(hps, ZIL_NULLH(HRGN), ZIL_NULLP(HRGN));
		GpiDestroyRegion(hps, hRegion);
	}
	VirtualPut(screenID);
}

void UI_OS2_DISPLAY::RectangleXORDiff(const UI_REGION &oldRegion,
	const UI_REGION &newRegion, ZIL_SCREENID screenID, const UI_REGION *)
{
	// Virtualize the display. (This call set up UI_OS2_DISPLAY::hps.)
	VirtualGet(screenID, MinValue(oldRegion.left, newRegion.left),
		MinValue(oldRegion.top, newRegion.top), MaxValue(oldRegion.right, newRegion.right),
		MinValue(oldRegion.bottom, newRegion.bottom));

	// Set the xor mix.
	LONG mix = GpiQueryMix(hps);
	GpiSetMix(hps, FM_XOR);

	// Draw the two rectangles.
	POINTL os2Position;
	os2Position.x = oldRegion.left - _virtualRegion.left;
	os2Position.y = _virtualRegion.bottom - oldRegion.bottom;
	GpiMove(hps, &os2Position);
	os2Position.x = oldRegion.right - _virtualRegion.left + 1;
	os2Position.y = _virtualRegion.bottom - oldRegion.top + 1;
	GpiBox(hps, DRO_OUTLINE, &os2Position, 0, 0);
	os2Position.x = newRegion.left - _virtualRegion.left;
	os2Position.y = _virtualRegion.bottom - newRegion.bottom;
	GpiMove(hps, &os2Position);
	os2Position.x = newRegion.right - _virtualRegion.left + 1;
	os2Position.y = _virtualRegion.bottom - newRegion.top + 1;
	GpiBox(hps, DRO_OUTLINE, &os2Position, 0, 0);

	// Reset the xor mix.
	GpiSetMix(hps, mix);

	// Un-virtualize the display.
	VirtualPut(screenID);
}

void UI_OS2_DISPLAY::RegionMove(const UI_REGION &oldRegion, int newColumn,
	int newLine, ZIL_SCREENID oldScreenID, ZIL_SCREENID newScreenID)
{
	// Get the device context.
	HPS _hDestPS, _hSrcPS;

	if (newScreenID == ID_SCREEN)
		newScreenID = HWND_DESKTOP;
	else
	{
		ZIL_SCREENID clientID = WinWindowFromID(newScreenID, FID_CLIENT);
		if (clientID)
		{
			if (oldScreenID == newScreenID)
				oldScreenID = clientID;
			newScreenID = clientID;
		}
	}
	_hDestPS = WinGetPS(newScreenID);
	if (oldScreenID == newScreenID)
		_hSrcPS = _hDestPS;
	else
	{
		if (oldScreenID == ID_SCREEN)
			oldScreenID = HWND_DESKTOP;
		else
		{
			ZIL_SCREENID clientID = WinWindowFromID(oldScreenID, FID_CLIENT);
			if (clientID)
				oldScreenID = clientID;
		}
		_hSrcPS = WinGetPS(oldScreenID);
	}

	SWP oldSwp, newSwp;
	WinQueryWindowPos(newScreenID, &oldSwp);
	WinQueryWindowPos(newScreenID, &newSwp);

	POINTL point[3];
	point[0].x = newColumn;
	point[0].y = newSwp.cy - newLine - oldRegion.Height();
	point[1].x = point[0].x + oldRegion.Width();
	point[1].y = point[0].y + oldRegion.Height();
	point[2].x = oldRegion.left;
	point[2].y = oldSwp.cy - oldRegion.bottom - 1;
	GpiBitBlt(_hDestPS, _hSrcPS, 3, point, ROP_SRCCOPY, BBO_IGNORE);

	WinReleasePS(_hDestPS);
	if (oldScreenID != newScreenID)
		WinReleasePS(_hSrcPS);
}

void UI_OS2_DISPLAY::Text(ZIL_SCREENID screenID, int left, int top,
	const ZIL_ICHAR *text, const UI_PALETTE *palette, int length, int fill,
	int _xor, const UI_REGION *clip, ZIL_LOGICAL_FONT font)
{
	// Make sure there is a valid string.
	if (!text || text[0] == '\0')
		return;

	// Virtualize the display. (This call set up UI_OS2_DISPLAY::hps.)
	int right = left + TextWidth(text, screenID, font) - 1;
	int bottom = top + TextHeight(text, screenID, font) - 1;
	VirtualGet(screenID, left, top, right, bottom);
	HRGN hRegion = 0;
	if (clip)
	{
		RECTL os2Region;
		os2Region.xLeft = clip->left - _virtualRegion.left;
		os2Region.yBottom = _virtualRegion.bottom - clip->bottom;
		os2Region.xRight = clip->right - _virtualRegion.left;
		os2Region.yTop = _virtualRegion.bottom - clip->top;
		hRegion = GpiCreateRegion(hps, 1, &os2Region);
		if (hRegion)
			GpiSetClipRegion(hps, hRegion, ZIL_NULLP(HRGN));
	}

	// Initialize the region.
	RECTL os2Region;
	os2Region.xLeft = left - _virtualRegion.left;
	os2Region.yBottom = _virtualRegion.bottom - bottom;
	os2Region.xRight = right - _virtualRegion.left + 1;
	os2Region.yTop = _virtualRegion.bottom - top;

	// Set the font, color and xor combinations.
	ULONG underscore = FlagSet(font, FNT_IGNORE_UNDERSCORE) ? 0 : DT_MNEMONIC;
	font &= 0x0FFF;

	LONG oldFont = GpiQueryCharSet(hps);
	SetFont(font);
	ZIL_COLOR colorForeground, colorBackground;
	if (palette)
	{
		colorForeground = GpiQueryColor(hps);
		colorBackground = GpiQueryBackColor(hps);
		GpiSetColor(hps, (palette->fillPattern == PTN_SYSTEM_COLOR) ?
			palette->colorForeground : MapColor(palette, TRUE));
		GpiSetBackColor(hps, (palette->fillPattern == PTN_SYSTEM_COLOR) ?
			palette->colorBackground : MapColor(palette, FALSE));
	}
	else
	{
		colorForeground = GpiQueryColor(hps);
		GpiSetColor(hps, SYSCLR_OUTPUTTEXT);
	}
	LONG mix;
	if (_xor)
	{
		mix = GpiQueryMix(hps);
		GpiSetMix(hps, FM_XOR);
	}

	// Compute the text and draw the text.
	ULONG drawFlags = DT_LEFT | DT_VCENTER | DT_TEXTATTRS | underscore;
	if (fill)
		drawFlags |= DT_ERASERECT;
#if defined (ZIL_UNICODE) || defined(ZIL_ISO8859_1)
	ZIL_ICHAR *tmp = strdup(text);
	if (length != -1)
		tmp[length] = 0;
	char *TEXT_ = MapText(tmp, ZIL_NULLP(char), FALSE);
	delete tmp;
	length = ::strlen(TEXT_);
#else
	if (length == -1)
		length = strlen(text);
#	define TEXT_ text
#endif
	WinDrawText(hps, length, (PSZ)TEXT_, &os2Region, 0, 0, drawFlags);

	// Reset the font, color and xor combinations.
	if (font < ZIL_MAXFONTS && fontTable[font].lMatch)
		GpiSetCharSet(hps, oldFont);
	if (palette)
		GpiSetBackColor(hps, colorBackground);
	GpiSetColor(hps, colorForeground);
	if (_xor)
		GpiSetMix(hps, mix);

	// Un-virtualize the display.
	if (hRegion)
	{
		GpiSetClipRegion(hps, ZIL_NULLH(HRGN), ZIL_NULLP(HRGN));
		GpiDestroyRegion(hps, hRegion);
	}
	VirtualPut(screenID);
}

int UI_OS2_DISPLAY::TextHeight(const ZIL_ICHAR *string, ZIL_SCREENID screenID,
	ZIL_LOGICAL_FONT font)
{
	// Make sure there is a valid string.
	if (!string || !string[0] || (!screenID && !hps))
		return (cellHeight - preSpace - postSpace);

	VirtualGet(screenID, 0, 0, lines - 1, columns - 1);

	// Set the font.
	SetFont(font & 0x0FFF);

	RECTL os2Region;
	os2Region.xLeft = _virtualRegion.left;
	os2Region.yBottom = _virtualRegion.top;
	os2Region.xRight = _virtualRegion.right;
	os2Region.yTop = _virtualRegion.bottom;
#if defined (ZIL_UNICODE) || defined(ZIL_ISO8859_1)
	char *TEXT_ = MapText(string, ZIL_NULLP(char), FALSE);
#else
#	define TEXT_ string
#endif
	WinDrawText(hps, ::strlen(TEXT_), (PSZ)TEXT_, &os2Region, 0, 0, DT_QUERYEXTENT | DT_MNEMONIC);
	VirtualPut(screenID);

	return (os2Region.yTop - os2Region.yBottom + 1);
}

int UI_OS2_DISPLAY::TextWidth(const ZIL_ICHAR *string, ZIL_SCREENID screenID,
	ZIL_LOGICAL_FONT font)
{
	// Make sure there is a valid string.
	if (!string || !string[0] || (!screenID && !hps))
		return (strlen(string) * cellWidth);

	HPS _hps = hps ? hps : WinGetPS(screenID);

	// Set the font.
	SetFont(font & 0x0FFF);

	RECTL os2Region;
	os2Region.xLeft = 0;
	os2Region.yBottom = 0;
	os2Region.xRight = columns - 1;
	os2Region.yTop = lines - 1;
#if defined (ZIL_UNICODE) || defined(ZIL_ISO8859_1)
	char *TEXT_ = MapText(string, ZIL_NULLP(char), FALSE);
#else
#	define TEXT_ string
#endif
	WinDrawText(_hps, ::strlen(TEXT_), (PSZ)TEXT_, &os2Region, 0, 0, DT_QUERYEXTENT | DT_MNEMONIC);

	if (!hps)
 		WinReleasePS(_hps);

	return (os2Region.xRight - os2Region.xLeft + 1);
}

void UI_OS2_DISPLAY::SetFont(ZIL_LOGICAL_FONT font)
{
	if (GpiQueryCharSet(hps) != font + 1)
	{
		if (font < ZIL_MAXFONTS && fontTable[font].lMatch)
		{
			if (!GpiSetCharSet(hps, font + 1))
			{
				FATTRS drawFont;
				drawFont.usRecordLength = sizeof(FATTRS);
				drawFont.fsSelection = fontTable[font].fsSelection;
				drawFont.lMatch = fontTable[font].lMatch;
				::strcpy(drawFont.szFacename, fontTable[font].szFacename);
				drawFont.idRegistry = fontTable[font].idRegistry;
				drawFont.usCodePage = fontTable[font].usCodePage;
				drawFont.lMaxBaselineExt = fontTable[font].lMaxBaselineExt;
				drawFont.lAveCharWidth = fontTable[font].lAveCharWidth;
				drawFont.fsType = 0;
				drawFont.fsFontUse = FATTR_FONTUSE_NOMIX;
				GpiCreateLogFont(hps, ZIL_NULLP(STR8), font + 1, &drawFont);
				GpiSetCharSet(hps, font + 1);
			}
		}
		else
			GpiSetCharSet(hps, 0);
	}
}

int UI_OS2_DISPLAY::VirtualGet(ZIL_SCREENID screenID, int left, int top,
	int right, int bottom)
{
	if (--_virtualCount == -1)
	{
		if (screenID == ID_DIRECT)
		{
			_virtualRegion.left = left;
			_virtualRegion.top = top;
			_virtualRegion.right = right;
			_virtualRegion.bottom = bottom;
		}
		else if (screenID == ID_SCREEN)
		{
			hps = WinGetPS(HWND_DESKTOP);
			_virtualRegion.left = _virtualRegion.top = 0;
			_virtualRegion.right = columns - 1;
			_virtualRegion.bottom = lines - 1;
		}
		else
		{
			hps = WinGetPS(screenID);

			RECTL rect;
			WinQueryWindowRect(screenID, &rect);
			_virtualRegion.left = left;
			_virtualRegion.top = top;
			_virtualRegion.right = right;
			_virtualRegion.bottom = top + rect.yTop - rect.yBottom - 1;

		}
	}
	return (TRUE);
}

int UI_OS2_DISPLAY::VirtualPut(ZIL_SCREENID screenID)
{
	if (++_virtualCount == 0)
	{
		if (screenID != ID_DIRECT && hps)
			WinReleasePS(hps);
		hps = 0;
	}
	return (TRUE);
}
