/*
 * Copyright (c) 1989-1993 The Regents of the University of California.
 * All rights reserved.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 * 
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * $Header: /user6/ouster/wish/RCS/tk3d.c,v 1.119 93/11/21 14:55:48 ouster Exp $ SPRITE (Berkeley)
 */

/* TkWin version 0.2 -- Tk for Windows
 * Copyright (C) 1994 K. K. Kubota
 * Mathematical Sciences Computing Facility, University of Kentucky,
 * Lexington, KY 40506, USA (e-mail: ken@ms.uky.edu)
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose is granted without fee, provided that the above copyright notice
 * and the following paragraph appear in all copies of this software.
 * and supporting documentation.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY, even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  In no event shall the author be
 * liable to any party for direct, indirect, special, incidental, or
 * consequential damages arising out of the use of this software and its
 * documentation.
 */

#include "tkInt.h"
#include "tkport.h"

static void BorderInit();
static void ShiftLine(POINT *, POINT *, int, POINT *);
static int Intersect(POINT *, POINT *, POINT *, POINT *, POINT *);
#ifdef NOTDEF
extern HWND hwndDbg;
extern UINT dbgfd;
void dbgMsg(char *p, char *q){
	MessageBox(hwndDbg, p, q, MB_OK);
}
#endif

typedef struct {
    int refCount;
    PALETTEENTRY *bgColorPtr;
    HBRUSH lightBrush, bgBrush, darkBrush;
    Tcl_HashEntry *hashPtr;	/* Entry in borderTable (needed in
				 * order to delete structure). */
} Border;

static Tcl_HashTable borderTable;
typedef Tk_Uid BorderKey;

#define MAX_INTENSITY	255	/* Maximum value for color intensity */
static int initialized = 0;	/* 0 means static structures haven't 
				 * been initialized yet */

/*
 *--------------------------------------------------------------
 *
 * Tk_GetColor -- Hacked this in here until we get palette stuff working
 *
 *--------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
static Tcl_HashTable nameTable;
typedef Tk_Uid nameKey;
static Tcl_HashTable valueTable;
static colorInitialized = 0;
#include "rgb.h"	/* color table values */
    
static long swap(long w){
    char *p;
    char t;
    t = w;
    p = (char *)&w;
    *p = *(p+2);
    *(p+2) = t;
    return w;
}

static void colorInit(void){
    Tcl_HashEntry *hashPtr;
    int new;
    struct color *colorPtr;
    struct syscolor *syscolorPtr;
    colorInitialized = 1;
    Tcl_InitHashTable(&nameTable, TCL_STRING_KEYS);
    Tcl_InitHashTable(&valueTable, TCL_ONE_WORD_KEYS);
    for(colorPtr = colorList; colorPtr->name != NULL; colorPtr++){
	hashPtr = Tcl_CreateHashEntry(&nameTable,
	    (char *)Tk_GetUid(colorPtr->name), &new);
	Tcl_SetHashValue(hashPtr, colorPtr);
	hashPtr = Tcl_CreateHashEntry(&valueTable,
	     (char *)(colorPtr->value), &new);
	if(new) Tcl_SetHashValue(hashPtr, colorPtr);
    }
    for(syscolorPtr = sysColorList; syscolorPtr->name != NULL; syscolorPtr++){
        syscolorPtr->index = (long)swap(GetSysColor(syscolorPtr->index));
	hashPtr = Tcl_CreateHashEntry(&nameTable,
	    (char *)Tk_GetUid(syscolorPtr->name), &new);
	Tcl_SetHashValue(hashPtr, syscolorPtr);
	hashPtr = Tcl_CreateHashEntry(&valueTable,
	     (char *)(syscolorPtr->index), &new);
	if(new) Tcl_SetHashValue(hashPtr, syscolorPtr);
    }
}

#ifdef TCL_MSDOS_PORT
PALETTEENTRY * _export
#else
PALETTEENTRY *
#endif
Tk_GetColor(Tcl_Interp *interp, Tk_Uid colorName){
	PALETTEENTRY *pePtr = (PALETTEENTRY *)ckalloc(sizeof(PALETTEENTRY));
	int red, green, blue;
	int new;
	Tcl_HashEntry *hashPtr;
	struct color *colorPtr;
	char cName[50], *p;
	
	if(!colorInitialized) colorInit();
	strncpy(cName, colorName, sizeof(cName));
	for(p = cName; *p; p++) if(*p >= 'A' && *p <= 'Z') *p += 'a' - 'A';
	pePtr->peFlags = PC_RESERVED;
        if((hashPtr = Tcl_FindHashEntry(&nameTable,
	    (char *)Tk_GetUid(cName))) != NULL){
	    colorPtr = (struct color *) Tcl_GetHashValue(hashPtr);
	    pePtr->peRed = (colorPtr->value >> 16)&0xff;
	    pePtr->peGreen = (colorPtr->value >> 8)&0xff;
	    pePtr->peBlue = (colorPtr->value)&0xff;
	    return pePtr;
	}
	if(sscanf(colorName, "#%4x%4x%4x", &red, &green, &blue) != 3 &&
	    sscanf(colorName, "#%2x%2x%2x", &red, &green, &blue) != 3) {
		ckfree(pePtr);
		return NULL;
	} else {
		pePtr->peRed = red;
		pePtr->peGreen = green;
		pePtr->peBlue = blue;
		return pePtr;
	}
}

/*
 *--------------------------------------------------------------
 *
 * Tk_NameOfColor -- Hacked this in here until we get palette stuff working
 *
 *--------------------------------------------------------------
 */
char *Tk_NameOfColor(XColor *colorPtr){
	static char name[50];
	int new;
	Tcl_HashEntry *hashPtr;
	struct color *cPtr;
	long value = ((((long)colorPtr->peRed)&0xff) << 16)
		| ((((long)colorPtr->peGreen)&0xff) << 8)
		| ((long)colorPtr->peBlue)&0xff;
	if(!colorInitialized) colorInit();
        if((hashPtr = Tcl_CreateHashEntry(&valueTable, (char *)value, &new))
	   != NULL) {
	    cPtr = (struct color *) Tcl_GetHashValue(hashPtr);
	    return cPtr->name;
	}
	sprintf(name, "#%02x%02x%02x", colorPtr->peRed, colorPtr->peGreen,
	    colorPtr->peBlue);
	return name;
}

/*
 *--------------------------------------------------------------
 * Tk_FreeColor -- Free a color created by Tk_GetColor
 *--------------------------------------------------------------
 */
void
Tk_FreeColor(colorPtr)
    XColor *colorPtr;
{
#ifdef DNOTDEF
/* Problem here: Need to be able to check if it is ok to deallocate */
    ckfree(colorPtr);
#endif
}
#endif

/*
 *--------------------------------------------------------------
 *
 * Tk_Get3DBorder --
 *
 *	Create a data structure for displaying a 3-D border.
 *
 * Results:
 *	The return value is a token for a data structure
 *	describing a 3-D border.  This token may be passed
 *	to Tk_Draw3DRectangle and Tk_Free3DBorder.  If an
 *	error prevented the border from being created then
 *	NULL is returned and an error message will be left
 *	in interp->result.
 *
 * Side effects:
 *	Data structures, graphics contexts, etc. are allocated.
 *	It is the caller's responsibility to eventually call
 *	Tk_Free3DBorder to release the resources.
 *
 *--------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
Tk_3DBorder _export
#else
Tk_3DBorder
#endif
Tk_Get3DBorder(Tcl_Interp *interp, Tk_Uid colorName)
       /* Args:
    	* interp	Place to store an error message.
    	* colorName	String giving name of color for window background. 
	*/
{
    BorderKey key;
    Tcl_HashEntry *hashPtr;
    register Border FAR* borderPtr;
    int newone;
#ifdef NOTDEF
    long values[3]; /* debug stuff */
    char msg[50]; /* debug stuff */
#endif

    if (!initialized) {
	BorderInit();
    }

    /*
     * First, check to see if there's already a border that will work
     * for this request.
     */

    key = colorName;

#ifdef NOTDEF
    hashPtr = Tcl_CreateHashEntry(&borderTable, (char *) &key, &newone);
#endif
    hashPtr = Tcl_CreateHashEntry(&borderTable, (char *) key, &newone);
    if (!newone) {
	borderPtr = (Border FAR*) Tcl_GetHashValue(hashPtr);
	borderPtr->refCount++;
#ifdef NOTDEF
	wsprintf(msg, "bdr %lx refCount: %d", borderPtr, borderPtr->refCount);
	MessageBox(hwndDbg, msg, "DEBUG", MB_OK);
#endif
    } else {

	/*
	 * No satisfactory border exists yet.  Initialize a new one.
	 */
    
	int red, green, blue;
	borderPtr = (Border *) ckalloc(sizeof(Border));
	borderPtr->refCount = 1;
	borderPtr->bgBrush = NULL;
	borderPtr->lightBrush = NULL;
	borderPtr->darkBrush = NULL;
	borderPtr->hashPtr = hashPtr;
	Tcl_SetHashValue(hashPtr, borderPtr);
    
	/*
	 * Figure out what colors and GC's to use for the light
	 * and dark areas and set up the graphics contexts.
	 * Monochrome displays get handled differently than
	 * color displays.
	 */
    
	borderPtr->bgColorPtr = Tk_GetColor(interp, colorName);
	if (borderPtr->bgColorPtr == NULL) {
	    goto error;
	}
	red = 14*borderPtr->bgColorPtr->peRed/10;
	if(red > MAX_INTENSITY) red = MAX_INTENSITY;
	green = 14*borderPtr->bgColorPtr->peGreen/10;
	if(green > MAX_INTENSITY) green = MAX_INTENSITY;
	blue = 14*borderPtr->bgColorPtr->peBlue/10;
	if(blue > MAX_INTENSITY) blue = MAX_INTENSITY;
	borderPtr->lightBrush = CreateSolidBrush(RGB(red, green, blue));
	red = 6*borderPtr->bgColorPtr->peRed/10;
	green = 6*borderPtr->bgColorPtr->peGreen/10;
	blue = 6*borderPtr->bgColorPtr->peBlue/10;
	borderPtr->darkBrush = CreateSolidBrush(RGB(red, green, blue));
	borderPtr->bgBrush = CreateSolidBrush(RGB(borderPtr->bgColorPtr->peRed,
	    borderPtr->bgColorPtr->peGreen, borderPtr->bgColorPtr->peBlue));
#ifdef NOTDEF
	wsprintf(msg, "Colors: %6lx %6lx %6lx", values[0],
		values[1], values[2]);
	MessageBox(hwndDbg, msg, "DEBUG", MB_OK);
#endif
    }
    return (Tk_3DBorder) borderPtr;

    error:
    Tk_Free3DBorder((Tk_3DBorder) borderPtr);
    return NULL;
}

/*
 *--------------------------------------------------------------
 *
 * Tk_3DVerticalBevel --
 *
 *	This procedure draws a vertical bevel along one side of
 *	an object.  The bevel is always rectangular in shape:
 *			|||
 *			|||
 *			|||
 *			|||
 *			|||
 *			|||
 *	An appropriate shadow color is chosen for the bevel based
 *	on the leftBevel and relief arguments.  Normally this
 *	procedure is called first, then Tk_3DHorizontalBevel is
 *	called next to draw neat corners.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Graphics are drawn in drawable.
 *
 *--------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
void
Tk_3DVerticalBevel(hdc, border, x, y, width, height,
	leftBevel, relief)
    HDC hdc;
#else
void
Tk_3DVerticalBevel(display, drawable, border, x, y, width, height,
	leftBevel, relief)
    Display *display;		/* X display in which to draw. */
    Drawable drawable;		/* X window or pixmap in which to draw. */
#endif
    Tk_3DBorder border;		/* Token for border to draw. */
    int x, y, width, height;	/* Area of vertical bevel. */
    int leftBevel;		/* Non-zero means this bevel forms the
				 * left side of the object;  0 means it
				 * forms the right side. */
    int relief;			/* Kind of bevel to draw.  For example,
				 * TK_RELIEF_RAISED means interior of
				 * object should appear higher than
				 * exterior. */
{
    Border *borderPtr = (Border *) border;
#ifdef TCL_MSDOS_PORT
    HBRUSH leftBrush, rightBrush;
    RECT rect;
#else
    GC left, right;
#endif

    if (relief == TK_RELIEF_RAISED) {
#ifdef TCL_MSDOS_PORT
	SetRect(&rect, x, y, x + width, y + height);
	FillRect(hdc, &rect,
	    (leftBevel) ? borderPtr->lightBrush : borderPtr->darkBrush);
#else
	XFillRectangle(display, drawable, 
		(leftBevel) ? borderPtr->lightGC : borderPtr->darkGC,
		x, y, width, height);
#endif
    } else if (relief == TK_RELIEF_SUNKEN) {
#ifdef TCL_MSDOS_PORT
	SetRect(&rect, x, y, x + width, y + height);
	FillRect(hdc, &rect,
	    (leftBevel) ? borderPtr->darkBrush : borderPtr->lightBrush);
#else
	XFillRectangle(display, drawable, 
		(leftBevel) ? borderPtr->darkGC : borderPtr->lightGC,
		x, y, width, height);
#endif
    } else if (relief == TK_RELIEF_RIDGE) {
	int half;

#ifdef TCL_MSDOS_PORT
	leftBrush = borderPtr->lightBrush;
	rightBrush = borderPtr->darkBrush;
#else
	left = borderPtr->lightGC;
	right = borderPtr->darkGC;
#endif
	ridgeGroove:
	half = width/2;
	if (!leftBevel && (width & 1)) {
	    half++;
	}
#ifdef TCL_MSDOS_PORT
	SetRect(&rect, x, y, x + half, y + height);
	FillRect(hdc, &rect, leftBrush);
	SetRect(&rect, x + half, y, x + width, y + height);
	FillRect(hdc, &rect, rightBrush);
#else
	XFillRectangle(display, drawable, left, x, y, half, height);
	XFillRectangle(display, drawable, right, x+half, y, width-half,
		height);
#endif
    } else if (relief == TK_RELIEF_GROOVE) {
#ifdef TCL_MSDOS_PORT
	leftBrush = borderPtr->darkBrush;
	rightBrush = borderPtr->lightBrush;
#else
	left = borderPtr->darkGC;
	right = borderPtr->lightGC;
#endif
	goto ridgeGroove;
    } else if (relief == TK_RELIEF_FLAT) {
#ifdef TCL_MSDOS_PORT
	SetRect(&rect, x, y, x + width, y + height);
	FillRect(hdc, &rect, borderPtr->bgBrush);
#else
	XFillRectangle(display, drawable, borderPtr->bgGC, x, y,
		width, height);
#endif
    }
}

/*
 *--------------------------------------------------------------
 *
 * Tk_3DHorizontalBevel --
 *
 *	This procedure draws a horizontal bevel along one side of
 *	an object.  The bevel has mitered corners (depending on
 *	leftIn and rightIn arguments).
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
void
Tk_3DHorizontalBevel(hdc, border, x, y, width, height,
	leftIn, rightIn, topBevel, relief)
    HDC hdc;
#else
void
Tk_3DHorizontalBevel(display, drawable, border, x, y, width, height,
	leftIn, rightIn, topBevel, relief)
    Display *display;		/* X display in which to draw. */
    Drawable drawable;		/* X window or pixmap in which to draw. */
#endif
    Tk_3DBorder border;		/* Token for border to draw. */
    int x, y, width, height;	/* Bounding box of area of bevel.  Height
				 * gives width of border. */
    int leftIn, rightIn;	/* Describes whether the left and right
				 * edges of the bevel angle in or out as
				 * they go down.  For example, if "leftIn"
				 * is true, the left side of the bevel
				 * looks like this:
				 *	___________
				 *	 __________
				 *	  _________
				 *	   ________
				 */
    int topBevel;		/* Non-zero means this bevel forms the
				 * top side of the object;  0 means it
				 * forms the bottom side. */
    int relief;			/* Kind of bevel to draw.  For example,
				 * TK_RELIEF_RAISED means interior of
				 * object should appear higher than
				 * exterior. */
{
    Border *borderPtr = (Border *) border;
    int bottom, halfway, x1, x2, x1Delta, x2Delta;
#ifdef TCL_MSDOS_PORT
    HPEN topPen = None, bottomPen = None, oldPen;
    int npen;
    LOGBRUSH logBr;
    POINT pt[2];
#else
    GC topGC = None, bottomGC = None;
#endif
				/* Initializations needed only to prevent
				 * compiler warnings. */

    /*
     * Compute a GC for the top half of the bevel and a GC for the
     * bottom half (they're the same in many cases).
     */

    switch (relief) {
#ifdef TCL_MSDOS_PORT
	case TK_RELIEF_RAISED:
 	    GetObject(
		(topBevel) ? borderPtr->lightBrush : borderPtr->darkBrush,
		sizeof(LOGBRUSH), (LPSTR)&logBr);
	    topPen = bottomPen = CreatePen(PS_SOLID, 1, logBr.lbColor);
	    npen = 1;
	    break;
	case TK_RELIEF_SUNKEN:
 	    GetObject(
	        (topBevel) ? borderPtr->darkBrush : borderPtr->lightBrush,
		sizeof(LOGBRUSH), (LPSTR)&logBr);
	    topPen = bottomPen = CreatePen(PS_SOLID, 1, logBr.lbColor);
	    npen = 1;
	    break;
	case TK_RELIEF_RIDGE:
 	    GetObject(borderPtr->lightBrush, sizeof(LOGBRUSH), (LPSTR)&logBr);
	    topPen = CreatePen(PS_SOLID, 1, logBr.lbColor);
 	    GetObject(borderPtr->darkBrush, sizeof(LOGBRUSH), (LPSTR)&logBr);
	    bottomPen = CreatePen(PS_SOLID, 1, logBr.lbColor);
	    npen = 2;
	    break;
	case TK_RELIEF_GROOVE:
 	    GetObject(borderPtr->darkBrush, sizeof(LOGBRUSH), (LPSTR)&logBr);
	    topPen = CreatePen(PS_SOLID, 1, logBr.lbColor);
 	    GetObject(borderPtr->lightBrush, sizeof(LOGBRUSH), (LPSTR)&logBr);
	    bottomPen = CreatePen(PS_SOLID, 1, logBr.lbColor);
	    npen = 2;
	    break;
	case TK_RELIEF_FLAT:
 	    GetObject(borderPtr->bgBrush, sizeof(LOGBRUSH), (LPSTR)&logBr);
	    topPen = bottomPen = CreatePen(PS_SOLID, 1, logBr.lbColor);
	    npen = 1;
	    break;
#else
	case TK_RELIEF_RAISED:
	    topGC = bottomGC =
		    (topBevel) ? borderPtr->lightGC : borderPtr->darkGC;
	    break;
	case TK_RELIEF_SUNKEN:
	    topGC = bottomGC =
		    (topBevel) ? borderPtr->darkGC : borderPtr->lightGC;
	    break;
	case TK_RELIEF_RIDGE:
	    topGC = borderPtr->lightGC;
	    bottomGC = borderPtr->darkGC;
	    break;
	case TK_RELIEF_GROOVE:
	    topGC = borderPtr->darkGC;
	    bottomGC = borderPtr->lightGC;
	    break;
	case TK_RELIEF_FLAT:
	    topGC = bottomGC = borderPtr->bgGC;
	    break;
#endif
    }

    /*
     * Compute various other geometry-related stuff.
     */

    x1 = x;
    if (!leftIn) {
	x1 += height;
    }
    x2 = x+width;
    if (!rightIn) {
	x2 -= height;
    }
    x1Delta = (leftIn) ? 1 : -1;
    x2Delta = (rightIn) ? -1 : 1;
    halfway = y + height/2;
    if (!topBevel && (height & 1)) {
	halfway++;
    }
    bottom = y + height;

    /*
     * Draw one line for each y-coordinate covered by the bevel.
     */

#ifdef TCL_MSDOS_PORT
    oldPen = SelectObject(hdc, topPen);
#endif
    for ( ; y < bottom; y++) {
	/*
	 * In some weird cases (such as large border widths for skinny
	 * rectangles) x1 can be >= x2.  Don't draw the lines
	 * in these cases.
	 */

#ifdef TCL_MSDOS_PORT
	if(y == halfway) SelectObject(hdc, bottomPen);
        pt[0].y = pt[1].y = y;
#endif
	if (x1 < x2) {
#ifdef TCL_MSDOS_PORT
            pt[0].x = x1;
	    pt[1].x = x2;
	    Polyline(hdc, pt, 2);
#else
	    XDrawLine(display, drawable, (y < halfway) ? topGC : bottomGC,
		    x1, y, x2-1, y);
#endif
	}
	x1 += x1Delta;
	x2 += x2Delta;
    }
#ifdef TCL_MSDOS_PORT
    SelectObject(hdc, oldPen);
    DeleteObject(topPen);
    if(npen == 2) DeleteObject(bottomPen);
#endif
}

#ifdef TCL_MSDOS_PORT
void _export
#else
void
#endif
Tk_Draw3DRectangle(HDC hdc, Tk_3DBorder border, int x, int y, int width,
	int height, int borderWidth, int relief) {
    register Border FAR* borderPtr = (Border FAR*) border;
    HBRUSH topBrush, bottomBrush;
    RECT rect;
    POINT points[7];
    HBRUSH hbrushOld;
    HPEN hpenOld;
#ifdef NOTDEF
    HRGN hrgn, oldHRgn;
#endif

    if ((width < 2*borderWidth) || (height < 2*borderWidth)) {
	return;
    }

    /*
     * Handle grooves and ridges with recursive calls.
     */

    if ((relief == TK_RELIEF_GROOVE) || (relief == TK_RELIEF_RIDGE)) {
	int halfWidth, insideOffset;
	
	halfWidth = borderWidth/2;
	insideOffset = borderWidth - halfWidth;
	Tk_Draw3DRectangle(hdc, border, x, y, width, height,
		 halfWidth, (relief == TK_RELIEF_GROOVE) ? TK_RELIEF_SUNKEN
		 : TK_RELIEF_RAISED);
	Tk_Draw3DRectangle(hdc, border, x + insideOffset,
		 y + insideOffset, width - insideOffset*2,
		 height - insideOffset*2, halfWidth, 
		 (relief == TK_RELIEF_GROOVE) ? TK_RELIEF_RAISED
		 : TK_RELIEF_SUNKEN);
	return;
    }

    if (relief == TK_RELIEF_RAISED) {
	topBrush = borderPtr->lightBrush;
	bottomBrush = borderPtr->darkBrush;
    } else if (relief == TK_RELIEF_SUNKEN) {
	topBrush = borderPtr->darkBrush;
	bottomBrush = borderPtr->lightBrush;
    } else {
	topBrush = bottomBrush = borderPtr->bgBrush;
    }
    hbrushOld = SelectObject(hdc, topBrush);
    hpenOld = SelectObject(hdc, GetStockObject(NULL_PEN));
    SetRect(&rect, x, y+height-borderWidth, x+width, y+height);
    FillRect(hdc, &rect, bottomBrush);
#ifdef DNOTDEF
    hrgn = CreateRectRgnIndirect(&rect);
    oldHRgn = SelectObject(hdc, hrgn);
    FillRgn(hdc, hrgn, bottomBrush);
    SelectObject(hdc, oldHRgn);
    DeleteObject(hrgn);
#endif
    SetRect(&rect, x+width-borderWidth, y, x+width, y+height);
    FillRect(hdc, &rect, bottomBrush);
#ifdef NOTDEF
    hrgn = CreateRectRgnIndirect(&rect);
    oldHRgn = SelectObject(hdc, hrgn);
    FillRgn(hdc, hrgn, bottomBrush);
    SelectObject(hdc, oldHRgn);
    DeleteObject(hrgn);
#endif
    points[0].x = points[1].x = points[6].x = x;
    points[0].y = points[6].y = y + height;
    points[1].y = points[2].y = y;
    points[2].x = x + width;
    points[3].x = x + width - borderWidth;
    points[3].y = points[4].y = y + borderWidth;
    points[4].x = points[5].x = x + borderWidth;
    points[5].y = y + height - borderWidth;
    if(!Polygon(hdc, points, 7)) panic("Polygon failed");
#ifdef NOTDEF
    hrgn = CreatePolygonRgn(points, 7, ALTERNATE);
    oldHRgn = SelectObject(hdc, hrgn);
    FillRgn(hdc, hrgn, topBrush);
    SelectObject(hdc, oldHRgn);
    DeleteObject(hrgn);
#endif
    SelectObject(hdc, hbrushOld);
    SelectObject(hdc, hpenOld);
    return;
}

/*
 *--------------------------------------------------------------
 *
 * Tk_NameOf3DBorder --
 *
 *	Given a border, return a textual string identifying the
 *	border's color.
 *
 * Results:
 *	The return value is the string that was used to create
 *	the border.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */

char *
Tk_NameOf3DBorder(Tk_3DBorder border)
#ifdef NOTDEF
    Tk_3DBorder border;		/* Token for border. */
#endif
{
    Border *borderPtr = (Border *) border;

#ifdef TCL_MSDOS_PORT
    return borderPtr->hashPtr->key.string;
#else
    return ((BorderKey *) borderPtr->hashPtr->key.words)->colorName;
#endif
}

/*
 *--------------------------------------------------------------------
 *
 * Tk_3DBorderColor --
 *
 *	Given a 3D border, return the X color used for the "flat"
 *	surfaces.
 *
 * Results:
 *	Returns the color used drawing flat surfaces with the border.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------------
 */
#ifdef TCL_MSDOS_PORT
PALETTEENTRY * _export
#else
PALETTEENTRY *
#endif
Tk_3DBorderColor(Tk_3DBorder border)
#ifdef NOTDEF
    Tk_3DBorder border;
#endif
{
    return(((Border *) border)->bgColorPtr);
}

/*
 *--------------------------------------------------------------
 *
 * Tk_Free3DBorder --
 *
 *	This procedure is called when a 3D border is no longer
 *	needed.  It frees the resources associated with the
 *	border.  After this call, the caller should never again
 *	use the "border" token.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Resources are freed.
 *
 *--------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
void _export
#else
void
#endif
Tk_Free3DBorder(Tk_3DBorder border)
#ifdef NOTDEF
    Tk_3DBorder border;		/* Token for border to be released. */
#endif
{
    register Border *borderPtr = (Border *) border;

    borderPtr->refCount--;
    if (borderPtr->refCount == 0) {
	if(borderPtr->bgColorPtr != NULL)
	    ckfree((char *)borderPtr->bgColorPtr);
	if(borderPtr->lightBrush != NULL)
	    DeleteObject(borderPtr->lightBrush);
	if(borderPtr->darkBrush != NULL)
	    DeleteObject(borderPtr->darkBrush);
	if(borderPtr->bgBrush != NULL)
	    DeleteObject(borderPtr->bgBrush);
	Tcl_DeleteHashEntry(borderPtr->hashPtr);
	ckfree((char *) borderPtr);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_SetBackgroundFromBorder --
 *
 *	Change the background of a window to one appropriate for a given
 *	3-D border.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Tkwin's background gets modified.
 *
 *----------------------------------------------------------------------
 */

void
Tk_SetBackgroundFromBorder(tkwin, border)
    Tk_Window tkwin;		/* Window whose background is to be set. */
    Tk_3DBorder border;		/* Token for border. */
{
    register Border *borderPtr = (Border *) border;

#ifdef TCL_MSDOS_PORT
    Tk_SetWindowBackground(tkwin, *(borderPtr->bgColorPtr));
#else
    Tk_SetWindowBackground(tkwin, borderPtr->bgColorPtr->pixel);
#endif
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_GetRelief --
 *
 *	Parse a relief description and return the corresponding
 *	relief value, or an error.
 *
 * Results:
 *	A standard Tcl return value.  If all goes well then
 *	*reliefPtr is filled in with one of the values
 *	TK_RELIEF_RAISED, TK_RELIEF_FLAT, or TK_RELIEF_SUNKEN.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
int _export
#else
int
#endif
Tk_GetRelief(Tcl_Interp *interp, char *name, int *reliefPtr)
#ifdef NOTDEF
    Tcl_Interp *interp;		/* For error messages. */
    char *name;			/* Name of a relief type. */
    int *reliefPtr;		/* Where to store converted relief. */
#endif
{
    char c;
    int length;

    c = name[0];
    length = lstrlen(name);
    if ((c == 'f') && (strncmp(name, "flat", length) == 0)) {
	*reliefPtr = TK_RELIEF_FLAT;
    } else if ((c == 'g') && (strncmp(name, "groove", length) == 0)
	    && (length >= 2)) {
        *reliefPtr = TK_RELIEF_GROOVE;
    } else if ((c == 'r') && (strncmp(name, "raised", length) == 0)
	    && (length >= 2)) {
	*reliefPtr = TK_RELIEF_RAISED;
    } else if ((c == 'r') && (strncmp(name, "ridge", length) == 0)) {
        *reliefPtr = TK_RELIEF_RIDGE;
    } else if ((c == 's') && (strncmp(name, "sunken", length) == 0)) {
	*reliefPtr = TK_RELIEF_SUNKEN;
    } else {
	sprintf(interp->result, "bad relief type \"%.50s\":  must be %s",
		name, "flat, groove, raised, ridge, or sunken");
	return TCL_ERROR;
    }
    return TCL_OK;
}

/*
 *--------------------------------------------------------------
 *
 * Tk_NameOfRelief --
 *
 *	Given a relief value, produce a string describing that
 *	relief value.
 *
 * Results:
 *	The return value is a static string that is equivalent
 *	to relief.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
char * _export
#else
char *
#endif
Tk_NameOfRelief(int relief)
#ifdef NOTDEF
    int relief;		/* One of TK_RELIEF_FLAT, TK_RELIEF_RAISED,
			 * or TK_RELIEF_SUNKEN. */
#endif
{
    if (relief == TK_RELIEF_FLAT) {
	return "flat";
    } else if (relief == TK_RELIEF_SUNKEN) {
	return "sunken";
    } else if (relief == TK_RELIEF_RAISED) {
	return "raised";
    } else if (relief == TK_RELIEF_GROOVE) {
	return "groove";
    } else if (relief == TK_RELIEF_RIDGE) {
	return "ridge";
    } else {
	return "unknown relief";
    }
}

/*
 *--------------------------------------------------------------
 *
 * Tk_Draw3DPolygon --
 *
 *	Draw a border with 3-D appearance around the edge of a
 *	given polygon.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Information is drawn in "drawable" in the form of a
 *	3-D border borderWidth units width wide on the left
 *	of the trajectory given by pointPtr and numPoints (or
 *	-borderWidth units wide on the right side, if borderWidth
 *	is negative).
 *
 *--------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
void _export
#else
void
#endif
Tk_Draw3DPolygon(HDC hdc, Tk_3DBorder border, POINT *pointPtr, int numPoints,
	int borderWidth, int leftRelief)
       /*
	* hdc		Device Context to draw polygon
	* border	Token for border to draw
    	* pointPtr	Array of points describing polygon.
    	* numPoints	Number of points at *pointPtr.
    	* borderWidth	Width of border, measured in pixels to the left of
	*		the polygon's trajectory.   May be negative.
    	* leftRelief	TK_RELIEF_RAISED or TK_RELIEF_SUNKEN: indicates how
	* 		stuff to left of trajectory looks relative to stuff
	*		on right.
	*/
{
    POINT poly[4], b1, b2, newB1, newB2;
    POINT perp, c, shift1, shift2;	/* Used for handling parallel lines. */
    register POINT *p1Ptr, *p2Ptr;
    Border *borderPtr = (Border *) border;
    HBRUSH hBrush, hbrushOld;
    HPEN hpenOld;
    int pfmOld;
    int i, lightOnLeft, dx, dy, parallel, pointsSeen;
#ifdef NOTDEF
    HRGN hrgn, oldHRgn;
#endif

    /*
     * Handle grooves and ridges with recursive calls.
     */

    if ((leftRelief == TK_RELIEF_GROOVE) || (leftRelief == TK_RELIEF_RIDGE)) {
	int halfWidth;

	halfWidth = borderWidth/2;
	Tk_Draw3DPolygon(hdc, border, pointPtr, numPoints,
		halfWidth, (leftRelief == TK_RELIEF_GROOVE) ? TK_RELIEF_RAISED
		: TK_RELIEF_SUNKEN);
	Tk_Draw3DPolygon(hdc, border, pointPtr, numPoints,
		-halfWidth, (leftRelief == TK_RELIEF_GROOVE) ? TK_RELIEF_SUNKEN
		: TK_RELIEF_RAISED);
	return;
    }

    /*
     * If the polygon is already closed, drop the last point from it
     * (we'll close it automatically).
     */

    p1Ptr = &pointPtr[numPoints-1];
    p2Ptr = &pointPtr[0];
    if ((p1Ptr->x == p2Ptr->x) && (p1Ptr->y == p2Ptr->y)) {
	numPoints--;
    }

    /*
     * The loop below is executed once for each vertex in the polgon.
     * At the beginning of each iteration things look like this:
     *
     *          poly[1]       /
     *             *        /
     *             |      /
     *             b1   * poly[0] (pointPtr[i-1])
     *             |    |
     *             |    |
     *             |    |
     *             |    |
     *             |    |
     *             |    | *p1Ptr            *p2Ptr
     *             b2   *--------------------*
     *             |
     *             |
     *             x-------------------------
     *
     * The job of this iteration is to do the following:
     * (a) Compute x (the border corner corresponding to
     *     pointPtr[i]) and put it in poly[2].  As part of
     *	   this, compute a new b1 and b2 value for the next
     *	   side of the polygon.
     * (b) Put pointPtr[i] into poly[3].
     * (c) Draw the polygon given by poly[0..3].
     * (d) Advance poly[0], poly[1], b1, and b2 for the
     *     next side of the polygon.
     */

    /*
     * The above situation doesn't first come into existence until
     * two points have been processed;  the first two points are
     * used to "prime the pump", so some parts of the processing
     * are ommitted for these points.  The variable "pointsSeen"
     * keeps track of the priming process;  it has to be separate
     * from i in order to be able to ignore duplicate points in the
     * polygon.
     */

    pointsSeen = 0;
    for (i = -2, p1Ptr = &pointPtr[numPoints-2], p2Ptr = p1Ptr+1;
	    i < numPoints; i++, p1Ptr = p2Ptr, p2Ptr++) {
	if ((i == -1) || (i == numPoints-1)) {
	    p2Ptr = pointPtr;
	}
	if ((p2Ptr->x == p1Ptr->x) && (p2Ptr->y == p1Ptr->y)) {
	    /*
	     * Ignore duplicate points (they'd cause core dumps in
	     * ShiftLine calls below).
	     */
	    continue;
	}
	ShiftLine(p1Ptr, p2Ptr, borderWidth, &newB1);
	newB2.x = newB1.x + (p2Ptr->x - p1Ptr->x);
	newB2.y = newB1.y + (p2Ptr->y - p1Ptr->y);
	poly[3] = *p1Ptr;
	parallel = 0;
	if (pointsSeen >= 1) {
	    parallel = Intersect(&newB1, &newB2, &b1, &b2, &poly[2]);
#ifdef NOTDEF
{ char msg[100];
  wsprintf(msg, "%d %d, %d %d, %d %d, %d %d, %d %d\n", 
	newB1.x, newB1.y, newB2.x, newB2.y, b1.x, b1.y, b2.x, b2.y,
	poly[2].x, poly[2].y);
  _lwrite(dbgfd, msg, lstrlen(msg));
}
#endif

	    /*
	     * If two consecutive segments of the polygon are parallel,
	     * then things get more complex.  Consider the following
	     * diagram:
	     *
	     * poly[1]
	     *    *----b1-----------b2------a
	     *                                \
	     *                                  \
	     *         *---------*----------*    b
	     *        poly[0]  *p2Ptr   *p1Ptr  /
	     *                                /
	     *              --*--------*----c
	     *              newB1    newB2
	     *
	     * Instead of using x and *p1Ptr for poly[2] and poly[3], as
	     * in the original diagram, use a and b as above.  Then instead
	     * of using x and *p1Ptr for the new poly[0] and poly[1], use
	     * b and c as above.
	     *
	     * Do the computation in three stages:
	     * 1. Compute a point "perp" such that the line p1Ptr-perp
	     *    is perpendicular to p1Ptr-p2Ptr.
	     * 2. Compute the points a and c by intersecting the lines
	     *    b1-b2 and newB1-newB2 with p1Ptr-perp.
	     * 3. Compute b by shifting p1Ptr-perp to the right and
	     *    intersecting it with p1Ptr-p2Ptr.
	     */

	    if (parallel) {
		perp.x = p1Ptr->x + (p2Ptr->y - p1Ptr->y);
		perp.y = p1Ptr->y - (p2Ptr->x - p1Ptr->x);
		(void) Intersect(p1Ptr, &perp, &b1, &b2, &poly[2]);
		(void) Intersect(p1Ptr, &perp, &newB1, &newB2, &c);
		ShiftLine(p1Ptr, &perp, borderWidth, &shift1);
		shift2.x = shift1.x + (perp.x - p1Ptr->x);
		shift2.y = shift1.y + (perp.y - p1Ptr->y);
		(void) Intersect(p1Ptr, p2Ptr, &shift1, &shift2, &poly[3]);
	    }
	}
	if (pointsSeen >= 2) {
	    dx = poly[3].x - poly[0].x;
	    dy = poly[3].y - poly[0].y;
	    if (dx > 0) {
		lightOnLeft = (dy <= dx);
	    } else {
		lightOnLeft = (dy < dx);
	    }
	    if (lightOnLeft ^ (leftRelief == TK_RELIEF_RAISED)) {
		hBrush = borderPtr->lightBrush;
	    } else {
		hBrush = borderPtr->darkBrush;
	    }
	    hbrushOld = SelectObject(hdc, hBrush);
	    hpenOld = SelectObject(hdc, GetStockObject(NULL_PEN));
	    pfmOld = SetPolyFillMode(hdc, WINDING);
	    Polygon(hdc, poly, 4);
	    SetPolyFillMode(hdc, pfmOld);
	    SelectObject(hdc, hpenOld);
	    SelectObject(hdc, hbrushOld);
#ifdef NOTDEF
	    hrgn = CreatePolygonRgn(poly, 4, WINDING);
	    oldHRgn = SelectObject(hdc, hrgn);
	    FillRgn(hdc, hrgn, hBrush);
	    SelectObject(hdc, oldHRgn);
	    DeleteObject(hrgn);
#endif
	}
	b1.x = newB1.x;
	b1.y = newB1.y;
	b2.x = newB2.x;
	b2.y = newB2.y;
	poly[0].x = poly[3].x;
	poly[0].y = poly[3].y;
	if (parallel) {
	    poly[1].x = c.x;
	    poly[1].y = c.y;
	} else if (pointsSeen >= 1) {
	    poly[1].x = poly[2].x;
	    poly[1].y = poly[2].y;
	}
	pointsSeen++;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_Fill3DRectangle --
 *
 *	Fill a rectangular area, supplying a 3D border if desired.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Information gets drawn on the screen.
 *
 *----------------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
void _export
#else
void
#endif
Tk_Fill3DRectangle(HDC hdc, Tk_3DBorder border, int x, int y, int width,
	int height, int borderWidth, int relief)
       /* Args:
	* hdc			Device context with which to draw
	* border		Token for border to draw
    	* x, y, width, height	Outside area of rectangular region.
    	* borderWidth		Desired width for border, in
	*			pixels. Border will be *inside* region.
    	* relief		Indicates 3D effect: TK_RELIEF_FLAT,
	*			TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN.
	*/
{
    register Border *borderPtr = (Border *) border;
#ifdef NOTDEF
    HRGN hrgn, oldHRgn;
#endif
    RECT rect;
    SetRect(&rect, x, y, x+width, y+height);
    FillRect(hdc, &rect, borderPtr->bgBrush);
#ifdef NOTDEF
    hrgn = CreateRectRgnIndirect(&rect);
    oldHRgn = SelectObject(hdc, hrgn);
    FillRgn(hdc, hrgn, borderPtr->bgBrush);
    SelectObject(hdc, oldHRgn);
    DeleteObject(hrgn);
#endif

    if (relief != TK_RELIEF_FLAT) {
	Tk_Draw3DRectangle(hdc, border, x, y, width,
		height, borderWidth, relief);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_Fill3DPolygon --
 *
 *	Fill a polygonal area, supplying a 3D border if desired.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Information gets drawn on the screen.
 *
 *----------------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
void _export
#else
void
#endif
Tk_Fill3DPolygon(HDC hdc, Tk_3DBorder border, POINT *pointPtr, int numPoints,
	int borderWidth, int leftRelief)
       /* Args:
	* hdc		Device context to be used in drawing
    	* border	Token for border to draw.
    	* pointPtr	Array of points describing polygon.
    	* numPoints	Number of points at *pointPtr.
    	* borderWidth	Width of border, measured in pixels to the left
	*		of the polygon's trajectory.   May be negative. 
    	* leftRelief	Indicates 3D effect of left side of trajectory
	*		relative to right: TK_RELIEF_FLAT, TK_RELIEF_RAISED,
	*		or TK_RELIEF_SUNKEN. 
	*/
{
    register Border *borderPtr = (Border *) border;
    HBRUSH hbrushOld;
    HPEN hpenOld;
    int pfmOld;
#ifdef NOTDEF
    HRGN hrgn, oldHRgn;
#endif
    hbrushOld = SelectObject(hdc, borderPtr->bgBrush);
    hpenOld = SelectObject(hdc, GetStockObject(NULL_PEN));
    pfmOld = SetPolyFillMode(hdc, WINDING);
    Polygon(hdc, pointPtr, numPoints);
    SetPolyFillMode(hdc, pfmOld);
    SelectObject(hdc, hpenOld);
    SelectObject(hdc, hbrushOld);
#ifdef NOTDEF
    hrgn = CreatePolygonRgn(pointPtr, numPoints, GetPolyFillMode(hdc));
    oldHRgn = SelectObject(hdc, hrgn);
    FillRgn(hdc, hrgn, borderPtr->bgBrush);
    SelectObject(hdc, oldHRgn);
    DeleteObject(hrgn);
#endif
    if (leftRelief != TK_RELIEF_FLAT) {
	Tk_Draw3DPolygon(hdc, border, pointPtr, numPoints,
		borderWidth, leftRelief);
    }
}

/*
 *--------------------------------------------------------------
 *
 * BorderInit --
 *
 *	Initialize the structures used for border management.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Read the code.
 *
 *-------------------------------------------------------------
 */

static void
BorderInit()
{
    initialized = 1;
#ifdef NOTDEF
    Tcl_InitHashTable(&borderTable, sizeof(BorderKey)/sizeof(int));
#endif
    Tcl_InitHashTable(&borderTable, TCL_STRING_KEYS);
}

/*
 *--------------------------------------------------------------
 *
 * ShiftLine --
 *
 *	Given two points on a line, compute a point on a
 *	new line that is parallel to the given line and
 *	a given distance away from it.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */

static void
ShiftLine(POINT *p1Ptr, POINT *p2Ptr, int distance, POINT *p3Ptr)
	/* Arguments:
    	 * p1Ptr	 First point on line. 
    	 * p2Ptr	 Second point on line. 
    	 * distance	 New line is to be this many units to the left of
	 *		 original line, when looking from p1 to p2.
	 *		 May be negative.
    	 * p3Ptr	 Store coords of point on new line here. 
	 */
{
    long int dx, dy, dxNeg, dyNeg;

    /*
     * The table below is used for a quick approximation in
     * computing the new point.  An index into the table
     * is 128 times the slope of the original line (the slope
     * must always be between 0 and 1).  The value of the table
     * entry is 128 times the amount to displace the new line
     * in y for each unit of perpendicular distance.  In other
     * words, the table maps from the tangent of an angle to
     * the inverse of its cosine.  If the slope of the original
     * line is greater than 1, then the displacement is done in
     * x rather than in y.
     */

    static long int shiftTable[129];

    /*
     * Initialize the table if this is the first time it is
     * used.
     */

    if (shiftTable[0] == 0) {
	int i;
	double tangent, cosine;

	for (i = 0; i <= 128; i++) {
	    tangent = i/128.0;
	    cosine = 128/cos(atan(tangent)) + .5;
	    shiftTable[i] = cosine;
	}
    }

    *p3Ptr = *p1Ptr;
    dx = p2Ptr->x - p1Ptr->x;
    dy = p2Ptr->y - p1Ptr->y;
    if (dy < 0) {
	dyNeg = 1;
	dy = -dy;
    } else {
	dyNeg = 0;
    }
    if (dx < 0) {
	dxNeg = 1;
	dx = -dx;
    } else {
	dxNeg = 0;
    }
    if (dy <= dx) {
	dy = ((distance * shiftTable[(dy<<7)/dx]) + 64) >> 7;
	if (!dxNeg) {
	    dy = -dy;
	}
	p3Ptr->y += dy;
    } else {
	dx = ((distance * shiftTable[(dx<<7)/dy]) + 64) >> 7;
	if (dyNeg) {
	    dx = -dx;
	}
	p3Ptr->x += dx;
    }
}

/*
 *--------------------------------------------------------------
 *
 * Intersect --
 *
 *	Find the intersection point between two lines.
 *
 * Results:
 *	Under normal conditions 0 is returned and the point
 *	at *iPtr is filled in with the intersection between
 *	the two lines.  If the two lines are parallel, then
 *	-1 is returned and *iPtr isn't modified.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */

static int
Intersect(POINT *a1Ptr, POINT *a2Ptr, POINT *b1Ptr, POINT *b2Ptr, POINT *iPtr)
	/* args are: 2 points on each line and place to put intersection pt */
{
    long int dxadyb, dxbdya, dxadxb, dyadyb, p, q;

    /*
     * The code below is just a straightforward manipulation of two
     * equations of the form y = (x-x1)*(y2-y1)/(x2-x1) + y1 to solve
     * for the x-coordinate of intersection, then the y-coordinate.
     */

    dxadyb = (a2Ptr->x - a1Ptr->x)*(b2Ptr->y - b1Ptr->y);
    dxbdya = (b2Ptr->x - b1Ptr->x)*(a2Ptr->y - a1Ptr->y);
    dxadxb = (a2Ptr->x - a1Ptr->x)*(b2Ptr->x - b1Ptr->x);
    dyadyb = (a2Ptr->y - a1Ptr->y)*(b2Ptr->y - b1Ptr->y);

    if (dxadyb == dxbdya) {
	return -1;
    }
    p = (a1Ptr->x*dxbdya - b1Ptr->x*dxadyb + (b1Ptr->y - a1Ptr->y)*dxadxb);
    q = dxbdya - dxadyb;
    if (q < 0) {
	p = -p;
	q = -q;
    }
    if (p < 0) {
	iPtr->x = - ((-p + q/2)/q);
    } else {
	iPtr->x = (p + q/2)/q;
    }
    p = (a1Ptr->y*dxadyb - b1Ptr->y*dxbdya + (b1Ptr->x - a1Ptr->x)*dyadyb);
    q = dxadyb - dxbdya;
    if (q < 0) {
	p = -p;
	q = -q;
    }
    if (p < 0) {
	iPtr->y = - ((-p + q/2)/q);
    } else {
	iPtr->y = (p + q/2)/q;
    }
    return 0;
}

/*
 *--------------------------------------------------------------------
 *
 * Tk_3DBorderGC --
 *
 *	Given a 3D border, return the X color used for the "flat"
 *	surfaces.
 *
 * Results:
 *	Returns the color used drawing flat surfaces with the border.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------------
 */
HBRUSH
Tk_3DBorderGC(border, which)
    Tk_3DBorder border;		/* Border whose GC is wanted. */
    int which;			/* Selects one of the border's 3 GC's:
				 * TK_3D_FLAT_GC, TK_3D_LIGHT_GC, or
				 * TK_3D_DARK_GC. */
{
    if (which == TK_3D_FLAT_GC) {
	return ((Border *) border)->bgBrush;
    } else if (which == TK_3D_LIGHT_GC) {
	return ((Border *) border)->lightBrush;
    } else if (which == TK_3D_DARK_GC){
	return ((Border *) border)->darkBrush;
    }
    panic("bogus \"which\" value in Tk_3DBorderGC");

    /*
     * The code below will never be executed, but it's needed to
     * keep compilers happy.
     */

    return (HBRUSH) None;
}
