/* 
 * tkWindow.c --
 *
 *	This file provides basic window-manipulation procedures,
 *	which are equivalent to procedures in Xlib (and even
 *	invoke them) but also maintain the local Tk_Window
 *	structure.
 *
 * Copyright (c) 1989-1994 The Regents of the University of California.
 * Copyright (c) 1994 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

/* TkWin version 0.3 -- 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.
 */

static char sccsid[] = "@(#) tkWindow.c 1.162 94/12/21 21:20:37";

#include "tkPort.h"
#include "tkInt.h"
#include "patchlevel.h"
#ifdef TCL_MSDOS_PORT
#include <commdlg.h>
#include <toolhelp.h>
#include <winsock.h>
#endif

#ifdef TCL_MSDOS_PORT
/* One of these is created for each Application running under the
 * Windows .DLL.  This is because the .DLL is shared memory between
 * all the apps using it.
 */
typedef struct WinMainArgs {
    HANDLE hInstance;
    HANDLE hPrevInstance;
    LPSTR lpszCmdLine;
    int nCmdShow;
    Tcl_Interp *mainInterp;
    HWND hwnd;
    struct WinMainArgs *next;
} WinMainArgs;
static WinMainArgs *winMainArgs = NULL;
Tcl_Interp *mainInterp;			/* for debugging */
Tcl_Interp *defaultInterp;		/* for use by send */
Tcl_Interp *lastDestroyInterp;		/* for destroy debugging */
    
void _export Tk_SetInstance(Tcl_Interp *interp, HANDLE hInstance,
	HANDLE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow, HWND hwnd){
    WinMainArgs *wmap = ckalloc(sizeof(WinMainArgs));
    
    wmap->hInstance = hInstance;
    wmap->hPrevInstance = hPrevInstance;
    wmap->lpszCmdLine = lpszCmdLine;
    wmap->nCmdShow = nCmdShow;
    wmap->mainInterp = interp;
    wmap->hwnd = hwnd;
    wmap->next = winMainArgs;
#ifdef NOTDEF
{ char buf[40];
wsprintf(buf, "wmap: %lx, interp: %lx", (long)wmap, (long)(wmap->mainInterp));
MessageBox(NULL, buf, "Tk_SetInstance", MB_OK);
}
#endif
    winMainArgs = wmap;
    mainInterp = interp; /* for debugging */
}

WinMainArgs *getWinMainArgs(Tcl_Interp *interp){
    WinMainArgs *wmap;
    if(interp == NULL) interp = defaultInterp;
    for(wmap = winMainArgs; wmap != NULL; wmap = wmap->next){
	if(wmap->mainInterp == interp) return wmap;
    }
    /* The ClipInit is being called with a NULL interp, so this is hacked
     * in here.  Why is it NULL?????? 
     */
    if(interp == NULL) return winMainArgs;
#ifdef NOTDEF
{ char buf[30];
wsprintf(buf, "%lx %lx %lx", (long)winMainArgs, (long)(winMainArgs->mainInterp), (long)(interp));
MessageBox(NULL, buf, "getWinMainArgs", MB_OK);
}
#endif
    panic("WinMainArgs not found");
    return NULL;
}

int DisplayPlanes(void) {
	HDC hdc;
	int planes = 0;
	if((hdc = CreateIC("DISPLAY", NULL, NULL, NULL)) != NULL){
		planes = GetDeviceCaps(hdc, PLANES);
		DeleteDC(hdc);
	}
	return planes;
}

int BitsPixel(void) {
	HDC hdc;
	int ret = 0;
	if((hdc = CreateIC("DISPLAY", NULL, NULL, NULL)) != NULL){
		ret = GetDeviceCaps(hdc, BITSPIXEL);
		DeleteDC(hdc);
	}
	return ret;
}

int WidthOfScreen(void) {
	HDC hdc;
	int ret = 0;
	if((hdc = CreateIC("DISPLAY", NULL, NULL, NULL)) != NULL){
		ret = GetDeviceCaps(hdc, HORZRES);
		DeleteDC(hdc);
	}
	return ret;
}

int HeightOfScreen(void) {
	HDC hdc;
	int ret = 0;
	if((hdc = CreateIC("DISPLAY", NULL, NULL, NULL)) != NULL){
		ret = GetDeviceCaps(hdc, VERTRES);
		DeleteDC(hdc);
	}
	return ret;
}

int WidthMMOfScreen(void) {
	HDC hdc;
	int ret = 0;
	if((hdc = CreateIC("DISPLAY", NULL, NULL, NULL)) != NULL){
		ret = GetDeviceCaps(hdc, HORZSIZE)*5/4;
		DeleteDC(hdc);
	}
	return ret;
}

int HeightMMOfScreen(void) {
	HDC hdc;
	int ret = 0;
	if((hdc = CreateIC("DISPLAY", NULL, NULL, NULL)) != NULL){
		ret = GetDeviceCaps(hdc, VERTSIZE)*5/4;
		DeleteDC(hdc);
	}
	return ret;
}

/* Create Icon from bitmaps created from GetBitmap */
HICON TkCreateIconFromBitmaps(interp, hbm, hbmmask)
    Tcl_Interp *interp;
    HBITMAP hbm, hbmmask;
{
    HDC memDC, memDC2;
    HBITMAP cbm, cbmOld, cbmOld2, cbmmask;
    int width, height;
    char cbits[32*4], mbits[32*4];
    WinMainArgs *wmap;

    memDC = CreateCompatibleDC(NULL);
    SetMapMode(memDC, MM_TEXT);
    cbm = CreateCompatibleBitmap(memDC, 32, 32);
    cbmOld = SelectObject(memDC, cbm);
    if(hbm != 0){
        memDC2 = CreateCompatibleDC(NULL);
        SetMapMode(memDC2, MM_TEXT);
        cbmOld2 = SelectObject(memDC2, hbm);
        Tk_SizeOfBitmap(hbm, &width, &height);
        StretchBlt(memDC, 0, 0, 32, 32, memDC2, 0, 0, width, height, SRCCOPY);
        SelectObject(memDC, cbmOld);
        SelectObject(memDC2, cbmOld2);
        DeleteDC(memDC2);
    }
    DeleteDC(memDC);
    GetBitmapBits(cbm, 32*4, cbits);
    DeleteObject(cbm);

    memDC = CreateCompatibleDC(NULL);
    SetMapMode(memDC, MM_TEXT);
    cbm = CreateCompatibleBitmap(memDC, 32, 32);
    cbmOld = SelectObject(memDC, cbm);
    if(hbmmask != 0){
        memDC2 = CreateCompatibleDC(NULL);
        SetMapMode(memDC2, MM_TEXT);
        cbmOld2 = SelectObject(memDC2, hbmmask);
        Tk_SizeOfBitmap(hbmmask, &width, &height);
        StretchBlt(memDC, 0, 0, 32, 32, memDC2, 0, 0, width, height, SRCCOPY);
        SelectObject(memDC, cbmOld);
        SelectObject(memDC2, cbmOld2);
        DeleteDC(memDC2);
    }
    DeleteDC(memDC);
    GetBitmapBits(cbm, 32*4, mbits);
    DeleteObject(cbm);
    wmap = getWinMainArgs(interp);
    return CreateIcon(wmap->hInstance, 32, 32, 1, 1, mbits, cbits);
}
#endif

/*
 * Count of number of main windows currently open in this process.
 */

int tk_NumMainWindows;

/*
 * First in list of all main windows managed by this process.
 */

TkMainInfo *tkMainWindowList = NULL;

/*
 * List of all displays currently in use.
 */

TkDisplay *tkDisplayList = NULL;

/*
 * Have statics in this module been initialized?
 */

static initialized = 0;

/*
 * Context information used to map from X window id's to TkWindow
 * structures (during event handling, for example).  If the value
 * of an entry is 0 it means that the window has been deleted but
 * we haven't yet seen the DestroyNotify event from the server.
 */

#ifndef TCL_MSDOS_PORT
XContext tkWindowContext;
#endif

/*
 * The variables below hold several uid's that are used in many places
 * in the toolkit.
 */

Tk_Uid tkDisabledUid = NULL;
Tk_Uid tkActiveUid = NULL;
Tk_Uid tkNormalUid = NULL;

/*
 * Default values for "changes" and "atts" fields of TkWindows.  Note
 * that Tk always requests all events for all windows, except StructureNotify
 * events on internal windows:  these events are generated internally.
 */

#ifdef TCL_MSDOS_PORT
static XWindowChanges defChanges = {
    0, 0, 1, 1, 0, Above
};
#else
static XWindowChanges defChanges = {
    0, 0, 1, 1, 0, 0, Above
};
#endif
#define ALL_EVENTS_MASK \
    KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask| \
    EnterWindowMask|LeaveWindowMask|PointerMotionMask|ExposureMask| \
    VisibilityChangeMask|SubstructureNotifyMask| \
    FocusChangeMask|PropertyChangeMask|ColormapChangeMask
static XSetWindowAttributes defAtts= {
#ifndef TCL_MSDOS_PORT
    None,			/* background_pixmap */
#endif
    0,				/* background_pixel */
#ifndef TCL_MSDOS_PORT
    CopyFromParent,		/* border_pixmap */
    0,				/* border_pixel */
    ForgetGravity,		/* bit_gravity */
    NorthWestGravity,		/* win_gravity */
    NotUseful,			/* backing_store */
    ~0,				/* backing_planes */
    0,				/* backing_pixel */
    False,			/* save_under */
    ALL_EVENTS_MASK,		/* event_mask */
    0,				/* do_not_propagate_mask */
#endif
    False,			/* override_redirect */
#ifndef TCL_MSDOS_PORT
    CopyFromParent,		/* colormap */
#endif
    None			/* cursor */
};

/*
 * The following structure defines all of the commands supported by
 * Tk, and the C procedures that execute them.
 */

#ifdef TCL_MSDOS_PORT
int 
Tk_WinExecCmd(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;
    int argc;
    char **argv;
{
	char cmd[200];
	int i, hinst;
        if (argc < 2) {
	    sprintf(interp->result,
		"wrong # args: should be \"%.50s option [arg arg ...]\"",
		argv[0]);
	    return TCL_ERROR;
        }
	strcpy(cmd, argv[1]);
	for(i = 2; i < argc; i++){
	    strcat(cmd, " ");
	    strcat(cmd, argv[i]);
	}
	hinst = WinExec(cmd, SW_SHOW);
	sprintf(interp->result, "%d", hinst);
	return TCL_OK;
}
#endif

typedef struct {
    char *name;			/* Name of command. */
    int (*cmdProc) _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
	    int argc, char **argv));
				/* Command procedure. */
} TkCmd;

static TkCmd commands[] = {
    /*
     * Commands that are part of the intrinsics:
     */

    {"after",		Tk_AfterCmd},
    {"bell",		Tk_BellCmd},
    {"bind",		Tk_BindCmd},
    {"bindtags",	Tk_BindtagsCmd},
    {"clipboard",	Tk_ClipboardCmd},
    {"destroy",		Tk_DestroyCmd},
    {"exit",		Tk_ExitCmd},
#ifndef TCL_MSDOS_PORT
    {"fileevent",	Tk_FileeventCmd},
#endif
    {"focus",		Tk_FocusCmd},
    {"grab",		Tk_GrabCmd},
    {"image",		Tk_ImageCmd},
    {"lower",		Tk_LowerCmd},
    {"option",		Tk_OptionCmd},
    {"pack",		Tk_PackCmd},
    {"place",		Tk_PlaceCmd},
    {"raise",		Tk_RaiseCmd},
    {"selection",	Tk_SelectionCmd},
    {"tk",		Tk_TkCmd},
    {"tkwait",		Tk_TkwaitCmd},
    {"update",		Tk_UpdateCmd},
    {"winfo",		Tk_WinfoCmd},
    {"wm",		Tk_WmCmd},

    /*
     * Widget class commands.
     */
    {"button",		Tk_ButtonCmd},
    {"canvas",		Tk_CanvasCmd},
    {"checkbutton",	Tk_ButtonCmd},
    {"entry",		Tk_EntryCmd},
    {"frame",		Tk_FrameCmd},
    {"label",		Tk_ButtonCmd},
    {"listbox",		Tk_ListboxCmd},
    {"menu",		Tk_MenuCmd},
    {"menubutton",	Tk_MenubuttonCmd},
    {"message",		Tk_MessageCmd},
    {"radiobutton",	Tk_ButtonCmd},
    {"scale",		Tk_ScaleCmd},
    {"scrollbar",	Tk_ScrollbarCmd},
    {"text",		Tk_TextCmd},
    {"toplevel",	Tk_FrameCmd},
#ifdef TCL_MSDOS_PORT
    {"winexec",		Tk_WinExecCmd},
    {"win",		Tk_WinCmd},
#endif
    {(char *) NULL,	(int (*)()) NULL}
};

/*
 * Forward declarations to procedures defined later in this file:
 */

static void		AddToColormapWindows _ANSI_ARGS_((TkWindow *winPtr));
static Tk_Window	CreateTopLevelWindow _ANSI_ARGS_((Tcl_Interp *interp,
			    Tk_Window parent, char *name, char *screenName));
static void		DoConfigureNotify _ANSI_ARGS_((TkWindow *winPtr));
static TkDisplay *	GetScreen _ANSI_ARGS_((Tcl_Interp *interp,
			    char *screenName, int *screenPtr));
static int		NameWindow _ANSI_ARGS_((Tcl_Interp *interp,
			    TkWindow *winPtr, TkWindow *parentPtr,
			    char *name));
static TkWindow	*	NewWindow _ANSI_ARGS_((TkDisplay *dispPtr,
			    int screenNum, TkWindow *parentPtr));
static void		UnlinkWindow _ANSI_ARGS_((TkWindow *winPtr));

/*
 *----------------------------------------------------------------------
 *
 * CreateTopLevelWindow --
 *
 *	Make a new window that will be at top-level (its parent will
 *	be the root window of a screen).
 *
 * Results:
 *	The return value is a token for the new window, or NULL if
 *	an error prevented the new window from being created.  If
 *	NULL is returned, an error message will be left in
 *	interp->result.
 *
 * Side effects:
 *	A new window structure is allocated locally.  An X
 *	window is NOT initially created, but will be created
 *	the first time the window is mapped.
 *
 *----------------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT

HICON hDefaultIcon;		/* Default icon handle */

static void DrawBitmap(HDC hdc, HBITMAP hBitmap, int xStart, int yStart){
    BITMAP bm;
    HBITMAP hbmOld;
    HDC hdcMem;
    POINT ptSize, ptOrg;
    hdcMem = CreateCompatibleDC(hdc);
    hbmOld = SelectObject(hdcMem, hBitmap);
/*
    SetMapMode(hdcMem, GetMapMode(hdc));
    GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&bm);
    ptSize.x = bm.bmWidth;
    ptSize.y = bm.bmHeight;
    DPtoLP(hdc, &ptSize, 1);
    ptOrg.x = 0;
    ptOrg.y = 0;
    DPtoLP(hdcMem, &ptOrg, 1);
    BitBlt(hdc, xStart, yStart, ptSize.x, ptSize.y, hdcMem,
	ptOrg.x, ptOrg.y, SRCCOPY);
*/
    SetMapMode(hdcMem, MM_TEXT);
    Tk_SizeOfBitmap(hBitmap, &(ptSize.x), &(ptSize.y));
    BitBlt(hdc, xStart, yStart, ptSize.x, ptSize.y, hdcMem, 0, 0, SRCCOPY);
    SelectObject(hdcMem, hbmOld);
    DeleteDC(hdcMem);
}

static int openFlag;	/* Hack for deiconify */

static long FAR PASCAL WndProc (HWND hwnd, UINT message, UINT wParam,
                                                          LONG lParam)
    {
     if(InSendMessage() && message == WM_RENDERFORMAT){
	TkWindow *winPtr = (TkWindow *)GetWindowLong(hwnd, 0);
	TkConvertSelection(winPtr, wParam);
	return 0;
     }
     if(/* !InSendMessage() && */ message == WM_PAINT){
	if(!PostMessage(hwnd, WM_PAINT, 0, 0L)) panic("PostMessage failed");
#ifdef NOTDEF
	HDC hdc;
	PAINTSTRUCT ps;
	XEvent event;
	hdc = BeginPaint(hwnd, &ps);
	if(ps.fErase) SendMessage(hwnd, WM_ERASEBKGND,
	    (WPARAM) hdc, 0L);
	event.type = Expose;
	event.xexpose.send_event = InSendMessage();
	event.xexpose.window = hwnd;
	event.xexpose.x = ps.rcPaint.left;
	event.xexpose.y = ps.rcPaint.top;
	event.xexpose.width =
	    ps.rcPaint.right - ps.rcPaint.left;
	event.xexpose.height =
	    ps.rcPaint.bottom - ps.rcPaint.top;
	event.xexpose.count = 0;
	EndPaint(hwnd, &ps);
	TkPutBackEvent(&event, 1);
#endif
	return 0;
     }
#ifdef NOTDEF
     if(message == WM_SETFOCUS){
	PostMessage(hwnd, message, wParam, lParam);
	return DefWindowProc(hwnd, message, wParam, lParam);
     }
#endif
     if(message == WM_KILLFOCUS || message == WM_SETFOCUS){
	/* If one simply reposts all the messages, then when the focus
	 * is on a Tk_Window and you click on a menu button of the Wish
	 * console window, then one gets an infinite loop.  So, one
	 * flags the messages coming through here, and doesn't re-post
	 * those which have already come through here.  This has the effect
	 * that if the focus is on a Tk_Window and you click on a console
	 * window menu button, then the Tk_Windows don't get appropriate
	 * simulated FocusOut messages; but otherwise, all appears to working
	 * correctly.  Since the console is only there for debugging, this
	 * has not been fixed.
	 */
	char className[32];
#define FOCUSMAGIC	0x12345678L
	if (lParam != FOCUSMAGIC) {
	    PostMessage(hwnd, message, wParam, FOCUSMAGIC);
	}
	return 0;
     }
     if(message == WM_GETMINMAXINFO) {
	MINMAXINFO *lpmmi = (MINMAXINFO *)lParam;
	lpmmi->ptMinTrackSize.x = 70;
	return 0;
     }

     if(message == WM_SYSCOMMAND){
	TkWindow *winPtr = (TkWindow *)GetWindowLong(hwnd, 0);
	if(wParam == SC_MINIMIZE){
	    if(winPtr && (winPtr->flags & TK_TOP_LEVEL) && 
	        hwnd == winPtr->window && !winPtr->iconified){
	    	if(winPtr->wmInfoPtr->iconName != NULL)
 	    	    SetWindowText(winPtr->window, winPtr->wmInfoPtr->iconName);
		winPtr->wmInfoPtr->hints.initial_state = IconicState;
	        winPtr->iconified = TRUE;
#ifdef NOTDEF
	        if(winPtr->iconWindow){
			if(winPtr->wmInfoPtr->iconName != NULL){
			    SetWindowText(winPtr->iconWindow,
				winPtr->wmInfoPtr->iconName);
			}
			ShowWindow(winPtr->iconWindow, SW_MINIMIZE);
		}
	        ShowWindow(hwnd, SW_HIDE);
#endif
		ShowWindow(hwnd, SW_MINIMIZE);
	    }
            return DefWindowProc (hwnd, message, wParam, lParam) ;
	} else if(wParam == SC_RESTORE || wParam == SC_MAXIMIZE){
	    if(winPtr && (winPtr->flags & TK_TOP_LEVEL) && 
	        /* hwnd == winPtr->iconWindow && */ winPtr->iconified){
		RECT rect;
	 	if(winPtr->wmInfoPtr->titleUid != NULL)
	    	    SetWindowText(winPtr->window, winPtr->wmInfoPtr->titleUid);
	        winPtr->iconified = FALSE;
		winPtr->wmInfoPtr->hints.initial_state = NormalState;
		winPtr->wmInfoPtr->withdrawn = 0;
		if(wParam == SC_RESTORE){
		    Tk_MapWindow((Tk_Window) winPtr);
	            ShowWindow(winPtr->window, SW_NORMAL);
	            /* DoConfigureNotify(winPtr); */
#undef HACK
#ifdef HACK
		    GetWindowRect(winPtr->window, &rect);
		    /* Hack to trigger packer */
		    MoveWindow(winPtr->window, rect.left, rect.top,
		        rect.right - rect.left - 1, rect.bottom - rect.top - 1,
		        TRUE);
#endif
#undef HACK
		} else ShowWindow(winPtr->window, SW_SHOWMAXIMIZED);
		UpdateWindow(winPtr->window);
	    } else return DefWindowProc(hwnd, message, wParam, lParam);
	    return DefWindowProc (hwnd, message, wParam, lParam) ;
	} else if(wParam == SC_CLOSE){
	    if(winPtr && (winPtr->flags & TK_TOP_LEVEL)){
		Tk_DestroyWindow((Tk_Window)winPtr);
		if(strcmp(Tk_PathName((Tk_Window)winPtr), ".") == 0){
		    PostQuitMessage(0);
		}
	    }
	    return 0;
	} else return DefWindowProc (hwnd, message, wParam, lParam) ;
     }
     switch(message){
	   TkWindow *winPtr;
	case WM_QUERYOPEN:
	    openFlag = 1;
	    return 1;
	case WM_WINDOWPOSCHANGING: /* handle this or else taskman dies */
#define HACK
#ifdef HACK
	/* Force painting of the window after deiconify
	 * This causes top level window to shrink!
	 */
	    if(openFlag){
		WINDOWPOS *pwp = (WINDOWPOS *) lParam;
		--(pwp->cx);
		--(pwp->cy);
		openFlag = 0;
	    }
#endif
	    /* Fall right on through to DefWindowProc */
	case WM_SHOWWINDOW:
	case WM_NCCALCSIZE:
	case WM_NCACTIVATE:
	case WM_ICONERASEBKGND:
	case WM_GETMINMAXINFO:
	case WM_ENABLE:
	case WM_CHILDACTIVATE:
	case WM_ACTIVATE:
	case WM_WINDOWPOSCHANGED: /* handle this or else taskman dies */
	case WM_MOUSEACTIVATE:
	case WM_NCHITTEST:
	case WM_NCPAINT:
     	    return DefWindowProc (hwnd, message, wParam, lParam) ;
     }
     if(message == WM_DESTROY){
	Tk_Window tkwin = (Tk_Window)GetWindowLong(hwnd, 0);
	if(tkwin != NULL && Tk_PathName(tkwin) != NULL &&
	    strcmp(Tk_PathName(tkwin), ".") == 0){
	    KillTimer(((TkWindow *)tkwin)->window, 2);
            PostQuitMessage (0) ;
	}
        return 0 ;
     }
#ifdef NOTDEF
	/* Try to bounce the motion messages back through */
     if(message == WM_MOUSEMOVE){
	PostMessage(hwnd, message, wParam, lParam);
	return 0;
     }
#endif
     if(InSendMessage() && message != WM_GETTEXT && message != WM_ERASEBKGND
	&& message != WM_SHOWWINDOW){
	PostMessage(hwnd, message, wParam, lParam);
	if(message == WM_NCACTIVATE && wParam == FALSE) return TRUE;
	else return 0;
     } 
     if(message == WM_SIZE){
	TkWindow *winPtr = (TkWindow *)GetWindowLong(hwnd, 0);
	if((winPtr != NULL) && (winPtr->flags & TK_TOP_LEVEL)){
	    RECT rect;
#ifdef NOTDEF
	    struct TkWmInfo *ip = winPtr->wmInfoPtr;
#endif
#ifdef NOTDEF
	    if(wParam == SIZE_MINIMIZED && winPtr->wmInfoPtr->iconName != NULL)
		SetWindowText(hwnd, winPtr->wmInfoPtr->iconName);
#endif
#ifdef NOTDEF
	    if(wParam == SIZE_MINIMIZED && winPtr->wmInfoPtr->hIcon != NULL)
		SetClassWord(hwnd, GCW_HICON, winPtr->wmInfoPtr->hIcon);
#endif
#ifdef NOTDEF
	    if(wParam != SIZE_MINIMIZED && winPtr->wmInfoPtr->titleUid != NULL)
		SetWindowText(hwnd, winPtr->wmInfoPtr->titleUid);
#endif
#ifdef NOTDEF
	    if(wParam != SIZE_MINIMIZED && hDefaultIcon != None)
		SetClassWord(hwnd, GCW_HICON, hDefaultIcon);
#endif
	    GetWindowRect(hwnd, &rect);
	    winPtr->changes.width = LOWORD(lParam);
	    winPtr->changes.height = HIWORD(lParam);
	    /* LockWindowUpdate(NULL); */
	    if(winPtr->flags & TK_TOP_LEVEL) {   /* Need to fix this for popups */
#ifdef NOTDEF
		winPtr->changes.width -= 8;
		winPtr->changes.height -= 27; /* yuck */
#endif
		if(winPtr->changes.width < 1) winPtr->changes.width = 1;
		if(winPtr->changes.height < 1) winPtr->changes.height = 1;
	    }
	    winPtr->changes.x = rect.left;
	    winPtr->changes.y = rect.top;
	    DoConfigureNotify(winPtr);
#ifdef NOTDEF
	    /* Hack to force update of all the subwindows after deiconify */
	    if(openFlag && wParam != SIZE_MINIMIZED && ip != NULL){
		RECT rect;
		openFlag = 0;
		LockWindowUpdate(NULL);
#undef HACK
#ifdef HACK
 		GetWindowRect(winPtr->window, &rect);
		/* Hack to trigger packer */
		MoveWindow(winPtr->window, rect.left, rect.top,
		    rect.right-rect.left+1,
		    rect.bottom-rect.top+1, TRUE);
		UpdateWindow(winPtr->window);
#endif
#undef HACK
	    }
#endif /* NOTDEF */
	}
	return 0;
    }
     if(message == WM_MOVE){
	TkWindow *winPtr = (TkWindow *)GetWindowLong(hwnd, 0);
	if((winPtr != NULL) && (winPtr->flags & TK_TOP_LEVEL)){
	    RECT rect, rect2;
	    int bwidth, theight;
	    GetWindowRect(hwnd, &rect);
	    GetClientRect(hwnd, &rect2);
#ifdef NOTDEF  /* no, x and y should include border */
	    bwidth = (rect.right - rect.left -rect2.right + rect2.left)/2;
	    theight = rect.bottom - rect.top -rect2.bottom + rect2.top -bwidth;
	    winPtr->changes.x = LOWORD(lParam) - theight;
	    if(winPtr->changes.x < 0) winPtr->changes.x = 0;
	    winPtr->changes.y = HIWORD(lParam) - bwidth;
	    if(winPtr->changes.y < 0) winPtr->changes.y = 0;
#endif
	    winPtr->changes.x = LOWORD(lParam);
	    winPtr->changes.y = HIWORD(lParam);
	    winPtr->changes.width = rect2.right - rect2.left;
	    winPtr->changes.height = rect2.bottom - rect2.top;
	    DoConfigureNotify(winPtr);
	}
	return 0;
    }
    if(message == WM_ERASEBKGND){
	HDC hdc = (HDC) wParam;
	HBRUSH brush, hbrOld;
	RECT rect;
	TkWindow *winPtr = (TkWindow *)GetWindowLong(hwnd, 0);
	PALETTEENTRY pe;
	if(winPtr == NULL) return 1;
	else pe = winPtr->atts.background_pixel;
 	if(!wParam){
	    hdc = GetDC(hwnd);
	}
	if(winPtr->window != hwnd) panic("Bad TkPtr");
        brush = CreateSolidBrush(RGB(pe.peRed, pe.peGreen, pe.peBlue));
	SetMapMode(hdc, MM_TEXT);
	UnrealizeObject(brush);
	hbrOld = SelectObject(hdc, brush);
	GetClientRect(hwnd, &rect);
	FillRect(hdc, &rect, brush);
	SelectObject(hdc, hbrOld);
	DeleteObject(brush);
	if(!wParam) ReleaseDC(hwnd, hdc);
	return 1;
    }
    return DefWindowProc (hwnd, message, wParam, lParam) ;
    }
#endif

static Tk_Window
CreateTopLevelWindow(interp, parent, name, screenName)
    Tcl_Interp *interp;		/* Interpreter to use for error reporting. */
    Tk_Window parent;		/* Token for logical parent of new window
				 * (used for naming, options, etc.).  May
				 * be NULL. */
    char *name;			/* Name for new window;  if parent is
				 * non-NULL, must be unique among parent's
				 * children. */
    char *screenName;		/* Name of screen on which to create
				 * window.  NULL means use DISPLAY environment
				 * variable to determine.  Empty string means
				 * use parent's screen, or DISPLAY if no
				 * parent. */
{
    register TkWindow *winPtr;
    register TkDisplay *dispPtr;
    int screenId;
#ifdef TCL_MSDOS_PORT
    WinMainArgs *wmap = getWinMainArgs(interp);
#endif

    if (!initialized) {
#ifdef TCL_MSDOS_PORT
        WNDCLASS wndclass ;
        if (!wmap->hPrevInstance) 
          {
          wndclass.style         = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 4 ;
          wndclass.hInstance     = wmap->hInstance ;
          hDefaultIcon = wndclass.hIcon
			         = LoadIcon(NULL, IDI_APPLICATION);
		/* Change to NULL when cursors are implemented. */
          wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);
          wndclass.hbrBackground = NULL;
          wndclass.hbrBackground = NULL;
          wndclass.lpszMenuName  = (LPCSTR) NULL;
          wndclass.lpszClassName = "Tk_Window";

          RegisterClass (&wndclass) ;
          }
#else
	tkWindowContext = XUniqueContext();
#endif
	initialized = 1;
	tkActiveUid = Tk_GetUid("active");
	tkDisabledUid = Tk_GetUid("disabled");
	tkNormalUid = Tk_GetUid("normal");

	/*
	 * Create built-in image types.
	 */
    
	Tk_CreateImageType(&tkBitmapImageType);
#ifndef TCL_MSDOS_PORT
	Tk_CreateImageType(&tkPhotoImageType);
#endif
    
	/*
	 * Create built-in photo image formats.
	 */
    
#ifndef TCL_MSDOS_PORT
	Tk_CreatePhotoImageFormat(&tkImgFmtPPM);
#endif
    }

    if ((parent != NULL) && (screenName != NULL) && (screenName[0] == '\0')) {
	dispPtr = ((TkWindow *) parent)->dispPtr;
	screenId = Tk_ScreenNumber(parent);
    } else {
	dispPtr = GetScreen(interp, screenName, &screenId);
	if (dispPtr == NULL) {
	    return (Tk_Window) NULL;
	}
    }

    winPtr = NewWindow(dispPtr, screenId, (TkWindow *) parent);

    /*
     * Force the window to use a the border pixel instead of border
     * pixmap.  This is needed for the case where the window doesn't
     * use the default visual.  In this case, the default border is
     * a pixmap inherited from the root window, which won't work because
     * it will have the wrong visual.
     */

#ifndef TCL_MSDOS_PORT
    winPtr->dirtyAtts |= CWBorderPixel;
#endif

    /*
     * Internal windows don't normally ask for StructureNotify events,
     * since we can generate them internally.  However, for top-level
     * windows we need to as for the events because the window could
     * be manipulated externally.
     */

#ifndef TCL_MSDOS_PORT
    winPtr->atts.event_mask |= StructureNotifyMask;
#endif

    /*
     * (Need to set the TK_TOP_LEVEL flag immediately here;  otherwise
     * Tk_DestroyWindow will core dump if it is called before the flag
     * has been set.)
     */

    winPtr->flags |= TK_TOP_LEVEL;
    if (parent != NULL) {
	if (NameWindow(interp, winPtr, (TkWindow *) parent, name) != TCL_OK) {
	    Tk_DestroyWindow((Tk_Window) winPtr);
	    return (Tk_Window) NULL;
	}
    }
    TkWmNewWindow(winPtr);
    return (Tk_Window) winPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * GetScreen --
 *
 *	Given a string name for a display-plus-screen, find the
 *	TkDisplay structure for the display and return the screen
 *	number too.
 *
 * Results:
 *	The return value is a pointer to information about the display,
 *	or NULL if the display couldn't be opened.  In this case, an
 *	error message is left in interp->result.  The location at
 *	*screenPtr is overwritten with the screen number parsed from
 *	screenName.
 *
 * Side effects:
 *	A new connection is opened to the display if there is no
 *	connection already.  A new TkDisplay data structure is also
 *	setup, if necessary.
 *
 *----------------------------------------------------------------------
 */

static TkDisplay *
GetScreen(interp, screenName, screenPtr)
    Tcl_Interp *interp;		/* Place to leave error message. */
    char *screenName;		/* Name for screen.  NULL or empty means
				 * use DISPLAY envariable. */
    int *screenPtr;		/* Where to store screen number. */
{
    register TkDisplay *dispPtr;
    char *p;
    int length, screenId, i;

    /*
     * Separate the screen number from the rest of the display
     * name.  ScreenName is assumed to have the syntax
     * <display>.<screen> with the dot and the screen being
     * optional.
     */

    if ((screenName == NULL) || (screenName[0] == '\0')) {
#ifdef TCL_MSDOS_PORT
	screenName = "DISPLAY"; /* Just the local display for Windows */
#else
	screenName = getenv("DISPLAY");
#endif
	if (screenName == NULL) {
	    interp->result =
		    "no display name and no $DISPLAY environment variable";
	    return (TkDisplay *) NULL;
	}
    }
    length = strlen(screenName);
    screenId = 0;
    p = screenName+length-1;
    while (isdigit(UCHAR(*p)) && (p != screenName)) {
	p--;
    }
    if ((*p == '.') && (p[1] != '\0')) {
	length = p - screenName;
	screenId = strtoul(p+1, (char **) NULL, 10);
    }

    /*
     * See if we already have a connection to this display.  If not,
     * then open a new connection.
     */

    for (dispPtr = tkDisplayList; ; dispPtr = dispPtr->nextPtr) {
	if (dispPtr == NULL) {
#ifndef TCL_MSDOS_PORT
	    Display *display;

	    display = XOpenDisplay(screenName);
	    if (display == NULL) {
		Tcl_AppendResult(interp, "couldn't connect to display \"",
			screenName, "\"", (char *) NULL);
		return (TkDisplay *) NULL;
	    }
#endif
	    dispPtr = (TkDisplay *) ckalloc(sizeof(TkDisplay));
#ifndef TCL_MSDOS_PORT
	    dispPtr->display = display;
#endif
	    dispPtr->nextPtr = tkDisplayList;
	    dispPtr->name = (char *) ckalloc((unsigned) (length+1));
	    dispPtr->lastEventTime = CurrentTime;
	    strncpy(dispPtr->name, screenName, length);
	    dispPtr->name[length] = '\0';
	    dispPtr->bindInfoStale = 1;
	    dispPtr->numModKeyCodes = 0;
#ifndef TCL_MSDOS_PORT
	    dispPtr->modKeyCodes = NULL;
#endif
	    dispPtr->errorPtr = NULL;
	    dispPtr->deleteCount = 0;
	    dispPtr->commTkwin = NULL;
	    dispPtr->selectionInfoPtr = NULL;
	    dispPtr->multipleAtom = None;
	    dispPtr->clipWindow = NULL;
	    dispPtr->clipboardActive = 0;
	    dispPtr->atomInit = 0;
	    dispPtr->cursorFont = None;
	    dispPtr->grabWinPtr = NULL;
	    dispPtr->eventualGrabWinPtr = NULL;
	    dispPtr->buttonWinPtr = NULL;
	    dispPtr->serverWinPtr = NULL;
	    dispPtr->firstGrabEventPtr = NULL;
	    dispPtr->lastGrabEventPtr = NULL;
	    dispPtr->grabFlags = 0;
#ifndef TCL_MSDOS_PORT
	    TkInitXId(dispPtr);
#endif
	    dispPtr->cmapPtr = NULL;
	    dispPtr->colorModels = (Tk_ColorModel *) ckalloc((unsigned)
		    (ScreenCount(display)*sizeof(Tk_ColorModel)));
	    for (i = ScreenCount(display)-1; i >= 0; i--) {
#ifdef TCL_MSDOS_PORT
		if (BitsPixel()*DisplayPlanes() <= 4) {
#else
		if (DisplayPlanes(display, i) <= 4) {
#endif
		    dispPtr->colorModels[i] = TK_MONO;
		} else {
		    dispPtr->colorModels[i] = TK_COLOR;
		}
	    }
#ifdef TCL_MSDOS_PORT
	    /* This is a relic from Tk36 focus code -- it should disappear */
	    dispPtr->focusTopLevelPtr = NULL;
	    dispPtr->focussedOnEnter = 0;
#endif
	    tkDisplayList = dispPtr;
#ifndef TCL_MSDOS_PORT
	    Tk_CreateFileHandler2(ConnectionNumber(display), TkXFileProc,
		    (ClientData) display);
#endif
	    break;
	}
	if ((strncmp(dispPtr->name, screenName, length) == 0)
		&& (dispPtr->name[length] == '\0')) {
	    break;
	}
    }
    if (screenId >= ScreenCount(dispPtr->display)) {
	sprintf(interp->result, "bad screen number \"%d\"", screenId);
	return (TkDisplay *) NULL;
    }
    *screenPtr = screenId;
    return dispPtr;
}

/*
 *--------------------------------------------------------------
 *
 * NewWindow --
 *
 *	This procedure creates and initializes a TkWindow structure.
 *
 * Results:
 *	The return value is a pointer to the new window.
 *
 * Side effects:
 *	A new window structure is allocated and all its fields are
 *	initialized.
 *
 *--------------------------------------------------------------
 */

static TkWindow *
NewWindow(dispPtr, screenNum, parentPtr)
    TkDisplay *dispPtr;		/* Display associated with new window. */
    int screenNum;		/* Index of screen for new window. */
    TkWindow *parentPtr;	/* Parent from which this window should
				 * inherit visual information.  NULL means
				 * use screen defaults instead of
				 * inheriting. */
{
    register TkWindow *winPtr;

    winPtr = (TkWindow *) ckalloc(sizeof(TkWindow));
#ifndef TCL_MSDOS_PORT
    winPtr->display = dispPtr->display;
#endif
    winPtr->dispPtr = dispPtr;
    winPtr->screenNum = screenNum;
#ifdef TCL_MSDOS_PORT
    if ((parentPtr != NULL)
	    && (parentPtr->screenNum == winPtr->screenNum))
#else
    if ((parentPtr != NULL) && (parentPtr->display == winPtr->display)
	    && (parentPtr->screenNum == winPtr->screenNum))
#endif
    {
#ifndef TCL_MSDOS_PORT
	winPtr->visual = parentPtr->visual;
#endif
	winPtr->depth = parentPtr->depth;
    } else {
#ifndef TCL_MSDOS_PORT
	winPtr->visual = DefaultVisual(dispPtr->display, screenNum);
#endif
	winPtr->depth = DefaultDepth(dispPtr->display, screenNum);
    }
    winPtr->window = None;
    winPtr->childList = NULL;
    winPtr->lastChildPtr = NULL;
    winPtr->parentPtr = NULL;
    winPtr->nextPtr = NULL;
    winPtr->mainPtr = NULL;
    winPtr->pathName = NULL;
    winPtr->nameUid = NULL;
    winPtr->classUid = NULL;
    winPtr->changes = defChanges;
    winPtr->dirtyChanges = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
    winPtr->atts = defAtts;
#ifdef TCL_MSDOS_PORT
    winPtr->dirtyAtts = CWEventMask;
#else
    if ((parentPtr != NULL) && (parentPtr->display == winPtr->display)
	    && (parentPtr->screenNum == winPtr->screenNum)) {
	winPtr->atts.colormap = parentPtr->atts.colormap;
    } else {
	winPtr->atts.colormap = DefaultColormap(dispPtr->display, screenNum);
    }
    winPtr->dirtyAtts = CWEventMask|CWColormap;
#endif
    winPtr->flags = 0;
    winPtr->handlerList = NULL;
    winPtr->tagPtr = NULL;
    winPtr->numTags = 0;
    winPtr->optionLevel = -1;
    winPtr->selHandlerList = NULL;
    winPtr->geomMgrPtr = NULL;
    winPtr->geomData = NULL;
    winPtr->reqWidth = winPtr->reqHeight = 1;
    winPtr->internalBorderWidth = 0;
    winPtr->wmInfoPtr = NULL;
#ifdef TCL_MSDOS_PORT
    winPtr->iconified = FALSE;
#endif
#ifdef TCL_MSDOS_PORT
    /* This is a relic from Tk36 focus code -- it should disappear */
    winPtr->focusProc = NULL;
    winPtr->focusData = NULL;
#endif

    return winPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * NameWindow --
 *
 *	This procedure is invoked to give a window a name and insert
 *	the window into the hierarchy associated with a particular
 *	application.
 *
 * Results:
 *	A standard Tcl return value.
 *
 * Side effects:
 *      See above.
 *
 *----------------------------------------------------------------------
 */

static int
NameWindow(interp, winPtr, parentPtr, name)
    Tcl_Interp *interp;		/* Interpreter to use for error reporting. */
    register TkWindow *winPtr;	/* Window that is to be named and inserted. */
    TkWindow *parentPtr;	/* Pointer to logical parent for winPtr
				 * (used for naming, options, etc.). */
    char *name;			/* Name for winPtr;   must be unique among
				 * parentPtr's children. */
{
#define FIXED_SIZE 200
    char staticSpace[FIXED_SIZE];
    char *pathName;
    int new;
    Tcl_HashEntry *hPtr;
    int length1, length2;

    /*
     * Setup all the stuff except name right away, then do the name stuff
     * last.  This is so that if the name stuff fails, everything else
     * will be properly initialized (needed to destroy the window cleanly
     * after the naming failure).
     */
    winPtr->parentPtr = parentPtr;
    winPtr->nextPtr = NULL;
    if (parentPtr->childList == NULL) {
	parentPtr->childList = winPtr;
    } else {
	parentPtr->lastChildPtr->nextPtr = winPtr;
    }
    parentPtr->lastChildPtr = winPtr;
    winPtr->mainPtr = parentPtr->mainPtr;
    winPtr->mainPtr->refCount++;
    winPtr->nameUid = Tk_GetUid(name);

    /*
     * Don't permit names that start with an upper-case letter:  this
     * will just cause confusion with class names in the option database.
     */

    if (isupper(UCHAR(name[0]))) {
	Tcl_AppendResult(interp,
		"window name starts with an upper-case letter: \"",
		name, "\"", (char *) NULL);
	return TCL_ERROR;
    }

    /*
     * To permit names of arbitrary length, must be prepared to malloc
     * a buffer to hold the new path name.  To run fast in the common
     * case where names are short, use a fixed-size buffer on the
     * stack.
     */

#ifdef TCL_MSDOS_PORT
    length1 = lstrlen(parentPtr->pathName);
    length2 = lstrlen(name);
#else
    length1 = strlen(parentPtr->pathName);
    length2 = strlen(name);
#endif
    if ((length1+length2+2) <= FIXED_SIZE) {
	pathName = staticSpace;
    } else {
	pathName = (char *) ckalloc((unsigned) (length1+length2+2));
    }
    if (length1 == 1) {
	pathName[0] = '.';
	strcpy(pathName+1, name);
    } else {
	strcpy(pathName, parentPtr->pathName);
	pathName[length1] = '.';
	strcpy(pathName+length1+1, name);
    }
    hPtr = Tcl_CreateHashEntry(&parentPtr->mainPtr->nameTable, pathName, &new);
    if (pathName != staticSpace) {
	ckfree(pathName);
    }
    if (!new) {
	Tcl_AppendResult(interp, "window name \"", name,
		"\" already exists in parent", (char *) NULL);
	return TCL_ERROR;
    }
    Tcl_SetHashValue(hPtr, winPtr);
    winPtr->pathName = Tcl_GetHashKey(&parentPtr->mainPtr->nameTable, hPtr);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_CreateMainWindow --
 *
 *	Make a new main window.  A main window is a special kind of
 *	top-level window used as the outermost window in an
 *	application.
 *
 * Results:
 *	The return value is a token for the new window, or NULL if
 *	an error prevented the new window from being created.  If
 *	NULL is returned, an error message will be left in
 *	interp->result.
 *
 * Side effects:
 *	A new window structure is allocated locally;  "interp" is
 *	associated with the window and registered for "send" commands
 *	under "baseName".  BaseName may be extended with an instance
 *	number in the form "#2" if necessary to make it globally
 *	unique.  Tk-related commands are bound into interp.  The main
 *	window becomes a "toplevel" widget and its X window will be
 *	created and mapped as an idle handler.
 *
 *----------------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
Tk_Window _export
#else
Tk_Window
#endif
Tk_CreateMainWindow(interp, screenName, baseName, className)
    Tcl_Interp *interp;		/* Interpreter to use for error reporting. */
    char *screenName;		/* Name of screen on which to create
				 * window.  Empty or NULL string means
				 * use DISPLAY environment variable. */
    char *baseName;		/* Base name for application;  usually of the
				 * form "prog instance". */
    char *className;		/* Class to use for application (same as class
				 * for main window). */
{
    Tk_Window tkwin;
    int dummy;
    Tcl_HashEntry *hPtr;
    register TkMainInfo *mainPtr;
    register TkWindow *winPtr;
    register TkCmd *cmdPtr;
    char *libDir;
    static char *argv[] = {(char *) NULL};

    /*
     * Panic if someone updated the TkWindow structure without
     * also updating the Tk_FakeWin structure (or vice versa).
     */

    if (sizeof(TkWindow) != sizeof(Tk_FakeWin)) {
	panic("TkWindow and Tk_FakeWin are not the same size");
    }

    /*
     * Create the basic TkWindow structure.
     */

    tkwin = CreateTopLevelWindow(interp, (Tk_Window) NULL, baseName,
	    screenName);
    if (tkwin == NULL) {
	return NULL;
    }

    /*
     * Create the TkMainInfo structure for this application, and set
     * up name-related information for the new window.
     */

    winPtr = (TkWindow *) tkwin;
    mainPtr = (TkMainInfo *) ckalloc(sizeof(TkMainInfo));
    mainPtr->winPtr = winPtr;
    mainPtr->refCount = 1;
    mainPtr->interp = interp;
    Tcl_InitHashTable(&mainPtr->nameTable, TCL_STRING_KEYS);
    mainPtr->bindingTable = Tk_CreateBindingTable(interp);
    mainPtr->curDispPtr = NULL;
    mainPtr->curScreenIndex = -1;
    mainPtr->bindingDepth = 0;
#ifdef TCL_MSDOS_PORT
    /* This is a relic from Tk36 -- else is right for 4.0 */
    mainPtr->focusPtr = winPtr;
    mainPtr->focusDefaultPtr = NULL;
#else
    mainPtr->focusPtr = NULL;
#endif
    mainPtr->focusSerial = 0;
    mainPtr->optionRootPtr = NULL;
    Tcl_InitHashTable(&mainPtr->imageTable, TCL_STRING_KEYS);
    mainPtr->nextPtr = tkMainWindowList;
    tkMainWindowList = mainPtr;
    winPtr->mainPtr = mainPtr;
    hPtr = Tcl_CreateHashEntry(&mainPtr->nameTable, ".", &dummy);
    Tcl_SetHashValue(hPtr, winPtr);
    winPtr->pathName = Tcl_GetHashKey(&mainPtr->nameTable, hPtr);

    /*
     * Register the interpreter for "send" purposes.
     */

    winPtr->nameUid = Tk_GetUid(Tk_SetAppName(tkwin, baseName));

    /*
     * Bind in Tk's commands.
     */

    for (cmdPtr = commands; cmdPtr->name != NULL; cmdPtr++) {
	Tcl_CreateCommand(interp, cmdPtr->name, cmdPtr->cmdProc,
		(ClientData) tkwin, (void (*)()) NULL);
    }
    Tcl_CallWhenDeleted(interp, TkEventCleanupProc, (ClientData) NULL);

    /*
     * Set variables for the intepreter.
     */

    libDir = getenv("TK_LIBRARY");
    if (libDir == NULL) {
	libDir = TK_LIBRARY;
    }
    Tcl_SetVar(interp, "tk_library", libDir, TCL_GLOBAL_ONLY);
    Tcl_SetVar(interp, "tk_patchLevel", TK_PATCH_LEVEL, TCL_GLOBAL_ONLY);
    Tcl_SetVar(interp, "tk_version", TK_VERSION, TCL_GLOBAL_ONLY);

    /*
     * Make the main window into a toplevel widget, and give it an initial
     * requested size.
     */

    Tk_SetClass(tkwin, className);
    if (TkInitFrame(interp, tkwin, 1, 0, argv) == NULL) {
	return NULL;
    }
    Tk_GeometryRequest(tkwin, 200, 200);

    tk_NumMainWindows++;
    return tkwin;
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_Init --
 *
 *	This procedure is typically invoked by Tcl_AppInit procedures
 *	to perform additional Tk initialization for a Tcl interpreter,
 *	such as sourcing the "tk.tcl" script.
 *
 * Results:
 *	Returns a standard Tcl completion code and sets interp->result
 *	if there is an error.
 *
 * Side effects:
 *	Depends on what's in the tk.tcl script.
 *
 *----------------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
int _export
#else
int
#endif
Tk_Init(interp)
    Tcl_Interp *interp;		/* Interpreter to initialize. */
{
    static char initCmd[] =
	"if [file exists $tk_library/tk.tcl] {\n\
	    source $tk_library/tk.tcl\n\
	} else {\n\
	    set msg \"can't find $tk_library/tk.tcl; perhaps you \"\n\
	    append msg \"need to\\ninstall Tk or set your TK_LIBRARY \"\n\
	    append msg \"environment variable?\"\n\
	    error $msg\n\
	}";

    return Tcl_Eval(interp, initCmd);
}

/*
 *--------------------------------------------------------------
 *
 * Tk_CreateWindow --
 *
 *	Create a new internal or top-level window as a child of an
 *	existing window.
 *
 * Results:
 *	The return value is a token for the new window.  This
 *	is not the same as X's token for the window.  If an error
 *	occurred in creating the window (e.g. no such display or
 *	screen), then an error message is left in interp->result and
 *	NULL is returned.
 *
 * Side effects:
 *	A new window structure is allocated locally.  An X
 *	window is not initially created, but will be created
 *	the first time the window is mapped.
 *
 *--------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
Tk_Window _export
#else
Tk_Window
#endif
Tk_CreateWindow(interp, parent, name, screenName)
    Tcl_Interp *interp;		/* Interpreter to use for error reporting.
				 * Interp->result is assumed to be
				 * initialized by the caller. */
    Tk_Window parent;		/* Token for parent of new window. */
    char *name;			/* Name for new window.  Must be unique
				 * among parent's children. */
    char *screenName;		/* If NULL, new window will be internal on
				 * same screen as its parent.  If non-NULL,
				 * gives name of screen on which to create
				 * new window;  window will be a top-level
				 * window. */
{
    TkWindow *parentPtr = (TkWindow *) parent;
    TkWindow *winPtr;

    if (screenName == NULL) {
	winPtr = NewWindow(parentPtr->dispPtr, parentPtr->screenNum,
		parentPtr);
	if (NameWindow(interp, winPtr, parentPtr, name) != TCL_OK) {
	    Tk_DestroyWindow((Tk_Window) winPtr);
	    return NULL;
	} else {
	  return (Tk_Window) winPtr;
	}
    } else {
	return CreateTopLevelWindow(interp, parent, name, screenName);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_CreateWindowFromPath --
 *
 *	This procedure is similar to Tk_CreateWindow except that
 *	it uses a path name to create the window, rather than a
 *	parent and a child name.
 *
 * Results:
 *	The return value is a token for the new window.  This
 *	is not the same as X's token for the window.  If an error
 *	occurred in creating the window (e.g. no such display or
 *	screen), then an error message is left in interp->result and
 *	NULL is returned.
 *
 * Side effects:
 *	A new window structure is allocated locally.  An X
 *	window is not initially created, but will be created
 *	the first time the window is mapped.
 *
 *----------------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
Tk_Window _export
#else
Tk_Window
#endif
Tk_CreateWindowFromPath(interp, tkwin, pathName, screenName)
    Tcl_Interp *interp;		/* Interpreter to use for error reporting.
				 * Interp->result is assumed to be
				 * initialized by the caller. */
    Tk_Window tkwin;		/* Token for any window in application
				 * that is to contain new window. */
    char *pathName;		/* Path name for new window within the
				 * application of tkwin.  The parent of
				 * this window must already exist, but
				 * the window itself must not exist. */
    char *screenName;		/* If NULL, new window will be on same
				 * screen as its parent.  If non-NULL,
				 * gives name of screen on which to create
				 * new window;  window will be a top-level
				 * window. */
{
#define FIXED_SPACE 5
    char fixedSpace[FIXED_SPACE+1];
    char *p;
    Tk_Window parent;
    int numChars;

    /*
     * Strip the parent's name out of pathName (it's everything up
     * to the last dot).  There are two tricky parts: (a) must
     * copy the parent's name somewhere else to avoid modifying
     * the pathName string (for large names, space for the copy
     * will have to be malloc'ed);  (b) must special-case the
     * situation where the parent is ".".
     */

    p = strrchr(pathName, '.');
    if (p == NULL) {
	Tcl_AppendResult(interp, "bad window path name \"", pathName,
		"\"", (char *) NULL);
	return NULL;
    }
    numChars = p-pathName;
    if (numChars > FIXED_SPACE) {
	p = (char *) ckalloc((unsigned) (numChars+1));
    } else {
	p = fixedSpace;
    }
    if (numChars == 0) {
	*p = '.';
	p[1] = '\0';
    } else {
	strncpy(p, pathName, numChars);
	p[numChars] = '\0';
    }

    /*
     * Find the parent window.
     */

    parent = Tk_NameToWindow(interp, p, tkwin);
    if (p != fixedSpace) {
	ckfree(p);
    }
    if (parent == NULL) {
	return NULL;
    }

    /*
     * Create the window.
     */

    if (screenName == NULL) {
	TkWindow *parentPtr = (TkWindow *) parent;
	TkWindow *winPtr;

	winPtr = NewWindow(parentPtr->dispPtr, parentPtr->screenNum,
		parentPtr);
	if (NameWindow(interp, winPtr, parentPtr, pathName+numChars+1)
		!= TCL_OK) {
	    Tk_DestroyWindow((Tk_Window) winPtr);
	    return NULL;
	} else {
	    return (Tk_Window) winPtr;
	}
    } else {
	return CreateTopLevelWindow(interp, parent, pathName+numChars+1,
		screenName);
    }
}

/*
 *--------------------------------------------------------------
 *
 * Tk_DestroyWindow --
 *
 *	Destroy an existing window.  After this call, the caller
 *	should never again use the token.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The window is deleted, along with all of its children.
 *	Relevant callback procedures are invoked.
 *
 *--------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
void _export
#else
void
#endif
Tk_DestroyWindow(tkwin)
    Tk_Window tkwin;		/* Window to destroy. */
{
    register TkWindow *winPtr = (TkWindow *) tkwin;
    XEvent event;

#ifdef TCL_MSDOS_PORT
    if(winPtr == NULL || (winPtr->flags & TK_ALREADY_DEAD))
#else
    if (winPtr->flags & TK_ALREADY_DEAD)
#endif
    {
	/*
	 * A destroy event binding caused the window to be destroyed
	 * again.  Ignore the request.
	 */

	return;
    }
    winPtr->flags |= TK_ALREADY_DEAD;

    /*
     * If this is a main window, remove it from the list of main
     * windows.  This needs to be done now (rather than later with
     * all the other main window cleanup) to handle situations where
     * a destroy binding for a window calls "exit".  In this case
     * the child window cleanup isn't complete when exit is called,
     * so the reference count of its application doesn't go to zero
     * when exit calls Tk_DestroyWindow on ".", so the main window
     * doesn't get removed from the list and exit loops infinitely.
     * Even worse, if "destroy ." is called by the destroy binding
     * before calling "exit", "exit" will attempt to destroy
     * mainPtr->winPtr, which no longer exists, and there may be a
     * core dump.
     */

    if (winPtr->mainPtr->winPtr == winPtr) {
	if (tkMainWindowList == winPtr->mainPtr) {
	    tkMainWindowList = winPtr->mainPtr->nextPtr;
	} else {
	    TkMainInfo *prevPtr;

	    for (prevPtr = tkMainWindowList;
		    prevPtr->nextPtr != winPtr->mainPtr;
		    prevPtr = prevPtr->nextPtr) {
		/* Empty loop body. */
	    }
	    prevPtr->nextPtr = winPtr->mainPtr->nextPtr;
	}
	tk_NumMainWindows--;
    }

    /*
     * Recursively destroy children.
     */

    while (winPtr->childList != NULL) {
	TkWindow *childPtr;

	childPtr = winPtr->childList;
	Tk_DestroyWindow((Tk_Window) childPtr);
	if (winPtr->childList == childPtr) {
	    /*
	     * The child didn't remove itself from the child list, so
	     * let's remove it here.  This can happen in some strange
	     * conditions, such as when a Delete event handler for a
	     * window deletes the window's parent.
	     */

	    winPtr->childList = childPtr->nextPtr;
	    childPtr->parentPtr = NULL;
	}
    }

    /*
     * Generate a DestroyNotify event.  In order for the DestroyNotify
     * event to be processed correctly, need to make sure the window
     * exists.  This is a bit of a kludge, and may be unnecessarily
     * expensive, but without it no event handlers will get called for
     * windows that don't exist yet.
     *
     * Note: if the window's pathName is NULL it means that the window
     * was not successfully initialized in the first place, so we should
     * not make the window exist or generate the event.
     */

    if (winPtr->pathName != NULL) {
	if (winPtr->window == None) {
	    Tk_MakeWindowExist(tkwin);
	}
	event.type = DestroyNotify;
#ifndef TCL_MSDOS_PORT
	event.xdestroywindow.serial =
		LastKnownRequestProcessed(winPtr->display);
#endif
	event.xdestroywindow.send_event = False;
#ifndef TCL_MSDOS_PORT
	event.xdestroywindow.display = winPtr->display;
#endif
	event.xdestroywindow.event = winPtr->window;
	event.xdestroywindow.window = winPtr->window;
	Tk_HandleEvent(&event);
    }

    /*
     * Cleanup the data structures associated with this window.  Special
     * trick: if winPtr->parentPtr is NULL, it means that the parent got
     * deleted before us.  If this happens, the X window is already gone
     * so we can't destroy it here or X will produce an error.
     */

    if (winPtr->window != None) {
#ifdef TCL_MSDOS_PORT
	if (winPtr->parentPtr != NULL || winPtr->flags & TK_TOP_LEVEL) {
	    if(DestroyWindow(winPtr->window) == 0){
		char buf[50];
		wsprintf(buf, "interp: %lx, hwnd: %x destroy failed", 
		    (long)lastDestroyInterp, winPtr->window);
		MessageBox(NULL, buf, "DestroyWindow", MB_OK);
	    }
#else
	if (winPtr->parentPtr != NULL) {
	    XDestroyWindow(winPtr->display, winPtr->window);
#endif
	}

	/*
	 * Don't call Tk_FreeXId to free the window's resource id here
	 * (can't re-use the id until all pending events for the window
	 * have been processed).  Instead, set the window's 
	 * tkWindowContext value to 0 to indicate that the window is
	 * defunct.  The event handler will free the resource id when
	 * the DestroyNotify event arrives.
	 *
	 * Note: the XDeleteContext call shouldn't really be necessary,
	 * but in R4 and older versions of X it doesn't work to overwrite
	 * an existing entry:  a new entry gets created without deleting
	 * the old entry, and eventually the table corrupts itself as it
	 * grows.
	 */

#ifdef TCL_MSDOS_PORT
        SetWindowLong(winPtr->window, 0, 0L);
#else
	XDeleteContext(winPtr->display, winPtr->window, tkWindowContext);
	XSaveContext(winPtr->display, winPtr->window, tkWindowContext, 0);
#endif
	winPtr->window = None;
    }
    if (winPtr->tagPtr != NULL) {
	TkFreeBindingTags(winPtr);
    }
    /* CHANGE: [8-jan-95, kkk] Added UnlinkWindow and TkEventDeadWindow back in
     * even though they weren't there in Tk4.0b1 -- Should they be here?
     */
    UnlinkWindow(winPtr);
    TkEventDeadWindow(winPtr);
    TkFocusDeadWindow(winPtr);
    TkOptionDeadWindow(winPtr);
    TkSelDeadWindow(winPtr);
    if (winPtr->flags & TK_TOP_LEVEL) {
	TkWmDeadWindow(winPtr);
    }
    TkGrabDeadWindow(winPtr);
    if (winPtr->mainPtr != NULL) {
	if (winPtr->pathName != NULL) {
	    Tk_DeleteAllBindings(winPtr->mainPtr->bindingTable,
		    (ClientData) winPtr->pathName);
	    Tcl_DeleteHashEntry(Tcl_FindHashEntry(&winPtr->mainPtr->nameTable,
		    winPtr->pathName));
	}
	winPtr->mainPtr->refCount--;
	if (winPtr->mainPtr->refCount == 0) {
	    register TkCmd *cmdPtr;

	    /*
	     * We just deleted the last window in the application.  Delete
	     * the TkMainInfo structure too and replace all of Tk's commands
	     * with dummy commands that return errors (except don't replace
	     * the "exit" command, since it may be needed for the application
	     * to exit).  Also delete the "send" command to unregister the
	     * interpreter.
	     */

	    for (cmdPtr = commands; cmdPtr->name != NULL; cmdPtr++) {
		if (cmdPtr->cmdProc != Tk_ExitCmd) {
		    Tcl_CreateCommand(winPtr->mainPtr->interp, cmdPtr->name,
			    TkDeadAppCmd, (ClientData) NULL,
			    (void (*)()) NULL);
		}
	    }
	    Tcl_CreateCommand(winPtr->mainPtr->interp, "send",
		    TkDeadAppCmd, (ClientData) NULL, (void (*)()) NULL);
	    Tcl_DeleteHashTable(&winPtr->mainPtr->nameTable);
	    Tk_DeleteBindingTable(winPtr->mainPtr->bindingTable);
	    TkDeleteAllImages(winPtr->mainPtr);
	    ckfree((char *) winPtr->mainPtr);
	}
    }
    ckfree((char *) winPtr);
}

/*
 *--------------------------------------------------------------
 *
 * Tk_MapWindow --
 *
 *	Map a window within its parent.  This may require the
 *	window and/or its parents to actually be created.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The given window will be mapped.  Windows may also
 *	be created.
 *
 *--------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
void _export
#else
void
#endif
Tk_MapWindow(tkwin)
    Tk_Window tkwin;		/* Token for window to map. */
{
    register TkWindow *winPtr = (TkWindow *) tkwin;
    XEvent event;

    if (winPtr->flags & TK_MAPPED) {
	return;
    }
    if (winPtr->window == None) {
	Tk_MakeWindowExist(tkwin);
    }
    if (winPtr->flags & TK_TOP_LEVEL) {
	/*
	 * Lots of special processing has to be done for top-level
	 * windows.  Let tkWm.c handle everything itself.
	 */

	TkWmMapWindow(winPtr);
	return;
    }
    winPtr->flags |= TK_MAPPED;
#ifdef TCL_MSDOS_PORT
    ShowWindow(winPtr->window, SW_SHOWNORMAL);
    UpdateWindow(winPtr->window);
    event.type = Expose;
    event.xexpose.send_event = FALSE;
    event.xexpose.window = winPtr->window;
    event.xexpose.x = 0;
    event.xexpose.y = 0;
    event.xexpose.width = Tk_Width(winPtr);
    event.xexpose.height = Tk_Height(winPtr);
    event.xexpose.count = 0;
#else
    XMapWindow(winPtr->display, winPtr->window);
    event.type = MapNotify;
    event.xmap.serial = LastKnownRequestProcessed(winPtr->display);
    event.xmap.send_event = False;
    event.xmap.display = winPtr->display;
    event.xmap.event = winPtr->window;
    event.xmap.window = winPtr->window;
    event.xmap.override_redirect = winPtr->atts.override_redirect;
#endif
    Tk_HandleEvent(&event);
}

/*
 *--------------------------------------------------------------
 *
 * Tk_MakeWindowExist --
 *
 *	Ensure that a particular window actually exists.  This
 *	procedure shouldn't normally need to be invoked from
 *	outside the Tk package, but may be needed if someone
 *	wants to manipulate a window before mapping it.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	When the procedure returns, the X window associated with
 *	tkwin is guaranteed to exist.  This may require the
 *	window's ancestors to be created also.
 *
 *--------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
void _export
#else
void
#endif
Tk_MakeWindowExist(tkwin)
    Tk_Window tkwin;		/* Token for window. */
{
    register TkWindow *winPtr = (TkWindow *) tkwin;
    TkWindow *winPtr2;
    Window parent;
#ifdef TCL_MSDOS_PORT
    DWORD dwStyle;
    int width, height;
    WinMainArgs *wmap;
#endif

    if (winPtr->window != None) {
	return;
    }

    if (winPtr->flags & TK_TOP_LEVEL) {
#ifdef TCL_MSDOS_PORT
	parent = HWND_DESKTOP;
#else
	parent = XRootWindow(winPtr->display, winPtr->screenNum);
#endif
    } else {
	if (winPtr->parentPtr->window == None) {
	    Tk_MakeWindowExist((Tk_Window) winPtr->parentPtr);
	}
	parent = winPtr->parentPtr->window;
    }

#ifdef TCL_MSDOS_PORT
    dwStyle = WS_CLIPCHILDREN | ((winPtr->flags&TK_TOP_LEVEL) ?
	(WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS) : WS_CHILD);
    if(winPtr->classUid == Tk_GetUid("Menu")){
	extern void ComputeMenuGeometry();
	if (!(winPtr->dirtyAtts & CWOverrideRedirect)
	    || winPtr->atts.override_redirect != 0){
	    dwStyle &= ~(WS_OVERLAPPEDWINDOW);
	    dwStyle |= WS_POPUP;
	} else {
/*
	    ComputeMenuGeometry(tkwin);
*/
	}
    }
    width = winPtr->changes.width;
    height = winPtr->changes.height;
#ifdef NOTDEF
    if(winPtr->flags & TK_TOP_LEVEL) {  /* Need to fix this for popups */
	width += 8;
	height += 27;  /* yuck */
    }
#endif
    if(winPtr->mainPtr == NULL){
	wmap = getWinMainArgs(NULL); /* Comm Window */
    } else {
	wmap = getWinMainArgs(winPtr->mainPtr->interp);
    }
    winPtr->window = CreateWindowEx(WS_EX_NOPARENTNOTIFY,
	    "Tk_Window", NULL, dwStyle,
	    winPtr->changes.x, winPtr->changes.y, width, height,
	    parent, NULL, wmap->hInstance, NULL);
    SetWindowLong(winPtr->window, 0, (long)winPtr);
#ifdef NOTDEF
    if(winPtr->flags & TK_TOP_LEVEL){
        winPtr->iconWindow = CreateWindowEx(WS_EX_NOPARENTNOTIFY,
	    "Tk_Window", NULL, dwStyle,
	    winPtr->changes.x, winPtr->changes.y, 10, 10,
	    HWND_DESKTOP, NULL, wmap->hInstance, NULL);
        SetWindowLong(winPtr->iconWindow, 0, (long)winPtr);
    }
#endif
#else
    winPtr->window = XCreateWindow(winPtr->display, parent,
	    winPtr->changes.x, winPtr->changes.y,
	    winPtr->changes.width, winPtr->changes.height,
	    winPtr->changes.border_width, winPtr->depth,
	    InputOutput, winPtr->visual, winPtr->dirtyAtts,
	    &winPtr->atts);
    XSaveContext(winPtr->display, winPtr->window, tkWindowContext,
	    (caddr_t) winPtr);
#endif
    winPtr->dirtyAtts = 0;
    winPtr->dirtyChanges = 0;

    if (!(winPtr->flags & TK_TOP_LEVEL)) {
	/*
	 * If any siblings higher up in the stacking order have already
	 * been created then move this window to its rightful position
	 * in the stacking order.
	 *
	 * NOTE: this code ignores any changes anyone might have made
	 * to the sibling and stack_mode field of the window's attributes,
	 * so it really isn't safe for these to be manipulated except
	 * by calling Tk_RestackWindow.
	 */

	for (winPtr2 = winPtr->nextPtr; winPtr2 != NULL;
		winPtr2 = winPtr2->nextPtr) {
	    if ((winPtr2->window != None) && !(winPtr2->flags & TK_TOP_LEVEL)) {
#ifdef TCL_MSDOS_PORT
		/* Update the Display */
		while(1){
		    while(Tk_DoOneEvent(TK_DONT_WAIT) != 0) ;
		    if(Tk_DoOneEvent(TK_DONT_WAIT) == 0) break;
		}
		SetWindowPos(winPtr->window, winPtr2->window, 0, 0, 0, 0,
		   SWP_NOMOVE | SWP_NOSIZE);
#else
		XWindowChanges changes;
		changes.sibling = winPtr2->window;
		changes.stack_mode = Below;
		XConfigureWindow(winPtr->display, winPtr->window,
			CWSibling|CWStackMode, &changes);
#endif
		break;
	    }
	}

	/*
	 * If this window has a different colormap than its parent, add
	 * the window to the WM_COLORMAP_WINDOWS property for its top-level.
	 */
    
#ifndef TCL_MSDOS_PORT
	if (winPtr->atts.colormap != winPtr->parentPtr->atts.colormap) {
	    AddToColormapWindows(winPtr);
	}
#endif
    }

    /*
     * Issue a ConfigureNotify event if there were deferred configuration
     * changes.
     */

    if (winPtr->flags & TK_NEED_CONFIG_NOTIFY) {
	winPtr->flags &= ~TK_NEED_CONFIG_NOTIFY;
	DoConfigureNotify(winPtr);
    }
}

/*
 *--------------------------------------------------------------
 *
 * Tk_UnmapWindow, etc. --
 *
 *	There are several procedures under here, each of which
 *	mirrors an existing X procedure.  In addition to performing
 *	the functions of the corresponding procedure, each
 *	procedure also updates the local window structure and
 *	synthesizes an X event (if the window's structure is being
 *	managed internally).
 *
 * Results:
 *	See the manual entries.
 *
 * Side effects:
 *	See the manual entries.
 *
 *--------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
void _export
#else
void
#endif
Tk_UnmapWindow(tkwin)
    Tk_Window tkwin;		/* Token for window to unmap. */
{
    register TkWindow *winPtr = (TkWindow *) tkwin;

    if (!(winPtr->flags & TK_MAPPED) || (winPtr->flags & TK_ALREADY_DEAD)) {
	return;
    }
    if (winPtr->flags & TK_TOP_LEVEL) {
	/*
	 * Special processing has to be done for top-level windows.  Let
	 * tkWm.c handle everything itself.
	 */

	TkWmUnmapWindow(winPtr);
	return;
    }
    winPtr->flags &= ~TK_MAPPED;
#ifdef TCL_MSDOS_PORT
#ifdef NOTDEF  /* nonreachable */
    if (winPtr->flags & TK_TOP_LEVEL) ShowWindow(winPtr->window, SW_MINIMIZE);
#endif
    ShowWindow(winPtr->window, SW_HIDE);
#else
    XUnmapWindow(winPtr->display, winPtr->window);
#endif
    if (!(winPtr->flags & TK_TOP_LEVEL)) {
	XEvent event;

	event.type = UnmapNotify;
#ifndef TCL_MSDOS_PORT
	event.xunmap.serial = LastKnownRequestProcessed(winPtr->display);
#endif
	event.xunmap.send_event = False;
#ifndef TCL_MSDOS_PORT
	event.xunmap.display = winPtr->display;
#endif
	event.xunmap.event = winPtr->window;
	event.xunmap.window = winPtr->window;
	event.xunmap.from_configure = False;
	Tk_HandleEvent(&event);
    }
}

#ifdef TCL_MSDOS_PORT
void _export
#else
void
#endif
Tk_ConfigureWindow(tkwin, valueMask, valuePtr)
    Tk_Window tkwin;		/* Window to re-configure. */
    unsigned int valueMask;	/* Mask indicating which parts of
				 * *valuePtr are to be used. */
    XWindowChanges *valuePtr;	/* New values. */
{
    register TkWindow *winPtr = (TkWindow *) tkwin;

    if (valueMask & CWX) {
	winPtr->changes.x = valuePtr->x;
    }
    if (valueMask & CWY) {
	winPtr->changes.y = valuePtr->y;
    }
    if (valueMask & CWWidth) {
	winPtr->changes.width = valuePtr->width;
    }
    if (valueMask & CWHeight) {
	winPtr->changes.height = valuePtr->height;
    }
#ifndef TCL_MSDOS_PORT
    if (valueMask & CWBorderWidth) {
	winPtr->changes.border_width = valuePtr->border_width;
    }
#endif
    if (valueMask & (CWSibling|CWStackMode)) {
	panic("Can't set sibling or stack mode from Tk_ConfigureWindow.");
    }

    if (winPtr->window != None) {
#ifdef TCL_MSDOS_PORT
	MoveWindow(winPtr->window, winPtr->changes.x, winPtr->changes.y,
	        winPtr->changes.width, winPtr->changes.height, TRUE);
	/* These are not generated by Windows, so do it ourselves */
	if (!(winPtr->flags & TK_TOP_LEVEL)) {
            DoConfigureNotify(winPtr);
	}
#else
	XConfigureWindow(winPtr->display, winPtr->window,
		valueMask, valuePtr);
	if (!(winPtr->flags & TK_TOP_LEVEL)) {
            DoConfigureNotify(winPtr);
	}
#endif
    } else {
	winPtr->dirtyChanges |= valueMask;
	winPtr->flags |= TK_NEED_CONFIG_NOTIFY;
    }
}

#ifdef TCL_MSDOS_PORT
void _export
#else
void
#endif
Tk_MoveWindow(tkwin, x, y)
    Tk_Window tkwin;		/* Window to move. */
    int x, y;			/* New location for window (within
				 * parent). */
{
    register TkWindow *winPtr = (TkWindow *) tkwin;
#ifdef TCL_MSDOS_PORT
    RECT rect;
#endif

    winPtr->changes.x = x;
    winPtr->changes.y = y;
    if (winPtr->window != None) {
#ifdef TCL_MSDOS_PORT
        GetWindowRect(winPtr->window, &rect);
	MoveWindow(winPtr->window, x, y, rect.right - rect.left,
	    rect.top - rect.bottom, TRUE);
#else
	XMoveWindow(winPtr->display, winPtr->window, x, y);
#endif
#ifdef TCL_MSDOS_PORT
	/* These are not generated by Windows, so do it ourselves */
	if (!(winPtr->flags & TK_TOP_LEVEL)) {
	    DoConfigureNotify(winPtr);
	}
#else
	if (!(winPtr->flags & TK_TOP_LEVEL)) {
	    DoConfigureNotify(winPtr);
	}
#endif
    } else {
	winPtr->dirtyChanges |= CWX|CWY;
	winPtr->flags |= TK_NEED_CONFIG_NOTIFY;
    }
}

#ifdef TCL_MSDOS_PORT
void _export
#else
void
#endif
Tk_ResizeWindow(tkwin, width, height)
    Tk_Window tkwin;		/* Window to resize. */
    unsigned int width, height;	/* New dimensions for window. */
{
    register TkWindow *winPtr = (TkWindow *) tkwin;
#ifdef TCL_MSDOS_PORT
    RECT rect, rect2;
#endif

    winPtr->changes.width = width;
    winPtr->changes.height = height;
    if (winPtr->window != None) {
#ifdef TCL_MSDOS_PORT
        GetWindowRect(winPtr->window, &rect);
	GetClientRect(winPtr->window, &rect2);
#ifdef NOTDEF
	if(width > 900) width = 900;
	if(height > 650) height = 650;
#endif
	MoveWindow(winPtr->window, rect.left, rect.top,
	    width + rect.right - rect.left - rect2.right + rect2.left,
	    height + rect.bottom - rect.top - rect2.bottom + rect2.top, TRUE);
#else
	XResizeWindow(winPtr->display, winPtr->window, width, height);
#endif
#ifdef TCL_MSDOS_PORT
	/* These are not generated by Windows, so do it ourselves */
	if (!(winPtr->flags & TK_TOP_LEVEL)) {
	    DoConfigureNotify(winPtr);
	}
#else
	if (!(winPtr->flags & TK_TOP_LEVEL)) {
	    DoConfigureNotify(winPtr);
	}
#endif
    } else {
	winPtr->dirtyChanges |= CWWidth|CWHeight;
	winPtr->flags |= TK_NEED_CONFIG_NOTIFY;
    }
}

#ifdef TCL_MSDOS_PORT
void _export
#else
void
#endif
Tk_MoveResizeWindow(tkwin, x, y, width, height)
    Tk_Window tkwin;		/* Window to move and resize. */
    int x, y;			/* New location for window (within
				 * parent). */
    unsigned int width, height;	/* New dimensions for window. */
{
    register TkWindow *winPtr = (TkWindow *) tkwin;
#ifdef TCL_MSDOS_PORT
    RECT rect, rect2;
    GetWindowRect(winPtr->window, &rect);
    GetClientRect(winPtr->window, &rect2);
#endif

    winPtr->changes.x = x;
    winPtr->changes.y = y;
    winPtr->changes.width = width;
    winPtr->changes.height = height;
    if (winPtr->window != None) {
#ifdef TCL_MSDOS_PORT
#ifdef NOTDEF
if(width > 900) width = 900;
if(height > 650) height = 650;
#endif
	MoveWindow(winPtr->window, x, y,
	    width + rect.right - rect.left - rect2.right + rect2.left,
	    height + rect.bottom - rect.top -rect2.bottom + rect2.top, TRUE);
#else
	XMoveResizeWindow(winPtr->display, winPtr->window,
		x, y, width, height);
#endif
#ifdef TCL_MSDOS_PORT
	/* These are not generated by Windows, so do it ourselves */
	if (!(winPtr->flags & TK_TOP_LEVEL)) {
	    DoConfigureNotify(winPtr);
	}
#else
	if (!(winPtr->flags & TK_TOP_LEVEL)) {
	    DoConfigureNotify(winPtr);
	}
#endif
    } else {
	winPtr->dirtyChanges |= CWX|CWY|CWWidth|CWHeight;
	winPtr->flags |= TK_NEED_CONFIG_NOTIFY;
    }
}

#ifndef TCL_MSDOS_PORT
void
Tk_SetWindowBorderWidth(tkwin, width)
    Tk_Window tkwin;		/* Window to modify. */
    int width;			/* New border width for window. */
{
    register TkWindow *winPtr = (TkWindow *) tkwin;

    winPtr->changes.border_width = width;
    if (winPtr->window != None) {
	XSetWindowBorderWidth(winPtr->display, winPtr->window, width);
	if (!(winPtr->flags & TK_TOP_LEVEL)) {
	    DoConfigureNotify(winPtr);
	}
    } else {
	winPtr->dirtyChanges |= CWBorderWidth;
	winPtr->flags |= TK_NEED_CONFIG_NOTIFY;
    }
}
#endif

void
Tk_ChangeWindowAttributes(tkwin, valueMask, attsPtr)
    Tk_Window tkwin;		/* Window to manipulate. */
    unsigned long valueMask;	/* OR'ed combination of bits,
				 * indicating which fields of
				 * *attsPtr are to be used. */
    register XSetWindowAttributes *attsPtr;
				/* New values for some attributes. */
{
    register TkWindow *winPtr = (TkWindow *) tkwin;

#ifndef TCL_MSDOS_PORT
    if (valueMask & CWBackPixmap) {
	winPtr->atts.background_pixmap = attsPtr->background_pixmap;
    }
    if (valueMask & CWBackPixel) {
	winPtr->atts.background_pixel = attsPtr->background_pixel;
    }
    if (valueMask & CWBorderPixmap) {
	winPtr->atts.border_pixmap = attsPtr->border_pixmap;
    }
    if (valueMask & CWBorderPixel) {
	winPtr->atts.border_pixel = attsPtr->border_pixel;
    }
    if (valueMask & CWBitGravity) {
	winPtr->atts.bit_gravity = attsPtr->bit_gravity;
    }
    if (valueMask & CWWinGravity) {
	winPtr->atts.win_gravity = attsPtr->win_gravity;
    }
    if (valueMask & CWBackingStore) {
	winPtr->atts.backing_store = attsPtr->backing_store;
    }
    if (valueMask & CWBackingPlanes) {
	winPtr->atts.backing_planes = attsPtr->backing_planes;
    }
    if (valueMask & CWBackingPixel) {
	winPtr->atts.backing_pixel = attsPtr->backing_pixel;
    }
#endif
    if (valueMask & CWOverrideRedirect) {
	winPtr->atts.override_redirect = attsPtr->override_redirect;
#ifdef TCL_MSDOS_PORT
	{
	    long val = GetWindowLong(winPtr->window, GWL_STYLE);
            if(attsPtr->override_redirect) {
		val &= ~(WS_OVERLAPPED | WS_CAPTION | WS_THICKFRAME
		    | WS_SYSMENU | WS_MINIMIZEBOX);
		val |= WS_POPUP;
	    } else {
	        val &= ~WS_POPUP;
		val |= (WS_OVERLAPPED | WS_CAPTION | WS_THICKFRAME
		    | WS_SYSMENU | WS_MINIMIZEBOX);
	    }
	    SetWindowLong(winPtr->window, GWL_STYLE, val);
	    BringWindowToTop(winPtr->window);
	}
#endif
    }
#ifndef TCL_MSDOS_PORT
    if (valueMask & CWSaveUnder) {
	winPtr->atts.save_under = attsPtr->save_under;
    }
    if (valueMask & CWEventMask) {
	winPtr->atts.event_mask = attsPtr->event_mask;
    }
    if (valueMask & CWDontPropagate) {
	winPtr->atts.do_not_propagate_mask
		= attsPtr->do_not_propagate_mask;
    }
    if (valueMask & CWColormap) {
	winPtr->atts.colormap = attsPtr->colormap;
    }
    if (valueMask & CWCursor) {
	winPtr->atts.cursor = attsPtr->cursor;
    }
#endif

    if (winPtr->window != None) {
#ifndef TCL_MSDOS_PORT
	XChangeWindowAttributes(winPtr->display, winPtr->window,
		valueMask, attsPtr);
#endif
    } else {
	winPtr->dirtyAtts |= valueMask;
    }
}

void
Tk_SetWindowBackground(tkwin, pixel)
    Tk_Window tkwin;		/* Window to manipulate. */
#ifdef TCL_MSDOS_PORT
    PALETTEENTRY pixel;		/* Pixel value to use for
				 * window's background. */
#else
    unsigned long pixel;	/* Pixel value to use for
				 * window's background. */
#endif
{
    register TkWindow *winPtr = (TkWindow *) tkwin;

    winPtr->atts.background_pixel = pixel;

#ifndef TCL_MSDOS_PORT
    if (winPtr->window != None) {
	XSetWindowBackground(winPtr->display, winPtr->window, pixel);
    } else {
	winPtr->dirtyAtts = (winPtr->dirtyAtts & ~CWBackPixmap)
		| CWBackPixel;
    }
#endif
}

#ifndef TCL_MSDOS_PORT
void
Tk_SetWindowBackgroundPixmap(tkwin, pixmap)
    Tk_Window tkwin;		/* Window to manipulate. */
    Pixmap pixmap;		/* Pixmap to use for window's
				 * background. */
{
    register TkWindow *winPtr = (TkWindow *) tkwin;

    winPtr->atts.background_pixmap = pixmap;

    if (winPtr->window != None) {
	XSetWindowBackgroundPixmap(winPtr->display,
		winPtr->window, pixmap);
    } else {
	winPtr->dirtyAtts = (winPtr->dirtyAtts & ~CWBackPixel)
		| CWBackPixmap;
    }
}
#endif

#ifndef TCL_MSDOS_PORT
void
Tk_SetWindowBorder(tkwin, pixel)
    Tk_Window tkwin;		/* Window to manipulate. */
    unsigned long pixel;	/* Pixel value to use for
				 * window's border. */
{
    register TkWindow *winPtr = (TkWindow *) tkwin;

    winPtr->atts.border_pixel = pixel;

    if (winPtr->window != None) {
	XSetWindowBorder(winPtr->display, winPtr->window, pixel);
    } else {
	winPtr->dirtyAtts = (winPtr->dirtyAtts & ~CWBorderPixmap)
		| CWBorderPixel;
    }
}
#endif

#ifndef TCL_MSDOS_PORT
void
Tk_SetWindowBorderPixmap(tkwin, pixmap)
    Tk_Window tkwin;		/* Window to manipulate. */
    Pixmap pixmap;		/* Pixmap to use for window's
				 * border. */
{
    register TkWindow *winPtr = (TkWindow *) tkwin;

    winPtr->atts.border_pixmap = pixmap;

    if (winPtr->window != None) {
	XSetWindowBorderPixmap(winPtr->display,
		winPtr->window, pixmap);
    } else {
	winPtr->dirtyAtts = (winPtr->dirtyAtts & ~CWBorderPixel)
		| CWBorderPixmap;
    }
}
#endif

#ifndef TCL_MSDOS_PORT
void
Tk_DefineCursor(tkwin, cursor)
    Tk_Window tkwin;		/* Window to manipulate. */
    Cursor cursor;		/* Cursor to use for window (may be None). */
{
    register TkWindow *winPtr = (TkWindow *) tkwin;

    winPtr->atts.cursor = cursor;

    if (winPtr->window != None) {
	XDefineCursor(winPtr->display, winPtr->window, cursor);
    } else {
	winPtr->dirtyAtts = winPtr->dirtyAtts | CWCursor;
    }
}
#endif

#ifdef TCL_MSDOS_PORT
void _export
#else
void
#endif
Tk_UndefineCursor(tkwin)
    Tk_Window tkwin;		/* Window to manipulate. */
{
    Tk_DefineCursor(tkwin, None);
}

#ifndef TCL_MSDOS_PORT
void
Tk_SetWindowColormap(tkwin, colormap)
    Tk_Window tkwin;		/* Window to manipulate. */
    Colormap colormap;		/* Colormap to use for window. */
{
    register TkWindow *winPtr = (TkWindow *) tkwin;

    winPtr->atts.colormap = colormap;

    if (winPtr->window != None) {
	XSetWindowColormap(winPtr->display, winPtr->window, colormap);
	if (!(winPtr->flags & TK_TOP_LEVEL)) {
	    AddToColormapWindows(winPtr);
	}
    } else {
	winPtr->dirtyAtts |= CWColormap;
    }
}
#endif

/*
 *----------------------------------------------------------------------
 *
 * Tk_SetWindowVisual --
 *
 *	This procedure is called to specify a visual to be used
 *	for a Tk window when it is created.  This procedure, if
 *	called at all, must be called before the X window is created
 *	(i.e. before Tk_MakeWindowExist is called).
 *
 * Results:
 *	The return value is 1 if successful, or 0 if the X window has
 *	been already created.
 *
 * Side effects:
 *	The information given is stored for when the window is created.
 *
 *----------------------------------------------------------------------
 */

#ifndef TCL_MSDOS_PORT
int
Tk_SetWindowVisual(tkwin, visual, depth, colormap)
    Tk_Window tkwin;		/* Window to manipulate. */
    Visual *visual;		/* New visual for window. */
    unsigned int depth;		/* New depth for window. */
    Colormap colormap;		/* An appropriate colormap for the visual. */
{
    register TkWindow *winPtr = (TkWindow *) tkwin;

    if( winPtr->window != None ){
	/* Too late! */
	return 0;
    }

    winPtr->visual = visual;
    winPtr->depth = depth;
    winPtr->atts.colormap = colormap;

    /*
     * The following code is needed to make sure that the window doesn't
     * inherit the parent's border pixmap, which would result in a BadMatch
     * error.
     */

    if (!(winPtr->dirtyAtts & CWBorderPixmap)) {
	winPtr->dirtyAtts |= CWBorderPixel;
    }
    return 1;
}
#endif

/*
 *----------------------------------------------------------------------
 *
 * DoConfigureNotify --
 *
 *	Generate a ConfigureNotify event describing the current
 *	configuration of a window.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	An event is generated and processed by Tk_HandleEvent.
 *
 *----------------------------------------------------------------------
 */

static void
DoConfigureNotify(winPtr)
    register TkWindow *winPtr;		/* Window whose configuration
					 * was just changed. */
{
    XEvent event;

    event.type = ConfigureNotify;
#ifndef TCL_MSDOS_PORT
    event.xconfigure.serial = LastKnownRequestProcessed(winPtr->display);
#endif
    event.xconfigure.send_event = False;
#ifndef TCL_MSDOS_PORT
    event.xconfigure.display = winPtr->display;
#endif
    event.xconfigure.event = winPtr->window;
    event.xconfigure.window = winPtr->window;
    event.xconfigure.x = winPtr->changes.x;
    event.xconfigure.y = winPtr->changes.y;
    event.xconfigure.width = winPtr->changes.width;
    event.xconfigure.height = winPtr->changes.height;
#ifndef TCL_MSDOS_PORT
    event.xconfigure.border_width = winPtr->changes.border_width;
#endif
    if (winPtr->changes.stack_mode == Above) {
	event.xconfigure.above = winPtr->changes.sibling;
    } else {
	event.xconfigure.above = None;
    }
    event.xconfigure.override_redirect = winPtr->atts.override_redirect;
    Tk_HandleEvent(&event);
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_SetClass --
 *
 *	This procedure is used to give a window a class.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	A new class is stored for tkwin, replacing any existing
 *	class for it.
 *
 *----------------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
void _export
#else
void
#endif
Tk_SetClass(tkwin, className)
    Tk_Window tkwin;		/* Token for window to assign class. */
    char *className;		/* New class for tkwin. */
{
    register TkWindow *winPtr = (TkWindow *) tkwin;

    winPtr->classUid = Tk_GetUid(className);
    if (winPtr->flags & TK_TOP_LEVEL) {
	TkWmSetClass(winPtr);
    }
    TkOptionClassChanged(winPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_NameToWindow --
 *
 *	Given a string name for a window, this procedure
 *	returns the token for the window, if there exists a
 *	window corresponding to the given name.
 *
 * Results:
 *	The return result is either a token for the window corresponding
 *	to "name", or else NULL to indicate that there is no such
 *	window.  In this case, an error message is left in interp->result.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
Tk_Window _export
#else
Tk_Window
#endif
Tk_NameToWindow(interp, pathName, tkwin)
    Tcl_Interp *interp;		/* Where to report errors. */
    char *pathName;		/* Path name of window. */
    Tk_Window tkwin;		/* Token for window:  name is assumed to
				 * belong to the same main window as tkwin. */
{
    Tcl_HashEntry *hPtr;

    hPtr = Tcl_FindHashEntry(&((TkWindow *) tkwin)->mainPtr->nameTable,
	    pathName);
    if (hPtr == NULL) {
	Tcl_AppendResult(interp, "bad window path name \"",
		pathName, "\"", (char *) NULL);
	return NULL;
    }
    return (Tk_Window) Tcl_GetHashValue(hPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_DisplayName --
 *
 *	Return the textual name of a window's display.
 *
 * Results:
 *	The return value is the string name of the display associated
 *	with tkwin.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
char * _export
#else
char *
#endif
Tk_DisplayName(tkwin)
    Tk_Window tkwin;		/* Window whose display name is desired. */
{
    return ((TkWindow *) tkwin)->dispPtr->name;
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_SetColorModel --
 *
 *	This procedure changes the current color model for a window
 *	(actually, for the window's screen).
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The color model for tkwin's screen is set to "model".
 *
 *----------------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
void _export
#else
void
#endif
Tk_SetColorModel(tkwin, model)
    Tk_Window tkwin;		/* Token for window;  this selects a screen
				 * whose color model is to be modified. */
    Tk_ColorModel model;	/* New model for tkwin's screen. */
{
    TkWindow *winPtr = (TkWindow *) tkwin;
    winPtr->dispPtr->colorModels[winPtr->screenNum] = model;
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_GetColorModel --
 *
 *	This procedure returns the current color model for a window
 *	(actually, for the window's screen).
 *
 * Results:
 *	A color model.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
Tk_ColorModel _export
#else
Tk_ColorModel
#endif
Tk_GetColorModel(tkwin)
    Tk_Window tkwin;		/* Token for window;  this selects a screen
				 * whose color model is returned. */
{
    TkWindow *winPtr = (TkWindow *) tkwin;
    return winPtr->dispPtr->colorModels[winPtr->screenNum];
}

/*
 *----------------------------------------------------------------------
 *
 * UnlinkWindow --
 *
 *	This procedure removes a window from the childList of its
 *	parent.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The window is unlinked from its childList.
 *
 *----------------------------------------------------------------------
 */

static void
UnlinkWindow(winPtr)
    TkWindow *winPtr;			/* Child window to be unlinked. */
{
    TkWindow *prevPtr;

    if (winPtr->parentPtr == NULL) {
	return;
    }
    prevPtr = winPtr->parentPtr->childList;
    if (prevPtr == winPtr) {
	winPtr->parentPtr->childList = winPtr->nextPtr;
	if (winPtr->nextPtr == NULL) {
	    winPtr->parentPtr->lastChildPtr = NULL;
	}
    } else {
	while (prevPtr->nextPtr != winPtr) {
	    prevPtr = prevPtr->nextPtr;
	    if (prevPtr == NULL) {
		panic("UnlinkWindow couldn't find child in parent");
	    }
	}
	prevPtr->nextPtr = winPtr->nextPtr;
	if (winPtr->nextPtr == NULL) {
	    winPtr->parentPtr->lastChildPtr = prevPtr;
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_RestackWindow --
 *
 *	Change a window's position in the stacking order.
 *
 * Results:
 *	TCL_OK is normally returned.  If other is not a descendant
 *	of tkwin's parent then TCL_ERROR is returned and tkwin is
 *	not repositioned.
 *
 * Side effects:
 *	Tkwin is repositioned in the stacking order.
 *
 *----------------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
int _export
#else
int
#endif
Tk_RestackWindow(tkwin, aboveBelow, other)
    Tk_Window tkwin;		/* Token for window whose position in
				 * the stacking order is to change. */
    int aboveBelow;		/* Indicates new position of tkwin relative
				 * to other;  must be Above or Below. */
    Tk_Window other;		/* Tkwin will be moved to a position that
				 * puts it just above or below this window.
				 * If NULL then tkwin goes above or below
				 * all windows in the same parent. */
{
    TkWindow *winPtr = (TkWindow *) tkwin;
    TkWindow *otherPtr = (TkWindow *) other;
    XWindowChanges changes;
    unsigned int mask;

    /*
     * Special case:  if winPtr is a top-level window then just find
     * the top-level ancestor of otherPtr and restack winPtr above
     * otherPtr without changing any of Tk's childLists.
     */

    changes.stack_mode = aboveBelow;
    mask = CWStackMode;
    if (winPtr->flags & TK_TOP_LEVEL) {
	if (otherPtr != NULL) {
	    while (!(otherPtr->flags & TK_TOP_LEVEL)) {
		otherPtr = otherPtr->parentPtr;
	    }
	}
	TkWmRestackToplevel(winPtr, aboveBelow, otherPtr);
	return TCL_OK;
    }

    /*
     * Find an ancestor of otherPtr that is a sibling of winPtr.
     */

    if (otherPtr == NULL) {
	if (aboveBelow == Above) {
	    otherPtr = winPtr->parentPtr->lastChildPtr;
	} else {
	    otherPtr = winPtr->parentPtr->childList;
	}
    } else {
	while (winPtr->parentPtr != otherPtr->parentPtr) {
	    if (otherPtr->flags & TK_TOP_LEVEL) {
		return TCL_ERROR;
	    }
	    otherPtr = otherPtr->parentPtr;
	}
    }
    if (otherPtr == winPtr) {
	return TCL_OK;
    }

    /*
     * Reposition winPtr in the stacking order.
     */

    UnlinkWindow(winPtr);
    if (aboveBelow == Above) {
	winPtr->nextPtr = otherPtr->nextPtr;
	if (winPtr->nextPtr == NULL) {
	    winPtr->parentPtr->lastChildPtr = winPtr;
	}
	otherPtr->nextPtr = winPtr;
    } else {
	TkWindow *prevPtr;

	prevPtr = winPtr->parentPtr->childList;
	if (prevPtr == otherPtr) {
	    winPtr->parentPtr->childList = winPtr;
	} else {
	    while (prevPtr->nextPtr != otherPtr) {
		prevPtr = prevPtr->nextPtr;
	    }
	    prevPtr->nextPtr = winPtr;
	}
	winPtr->nextPtr = otherPtr;
    }

    /*
     * Notify the X server of the change.  If winPtr hasn't yet been
     * created then there's no need to tell the X server now, since
     * the stacking order will be handled properly when the window
     * is finally created.
     */

    if (winPtr->window != None) {
#ifdef TCL_MSDOS_PORT
	/* Update the Display */
	while(1){
	    while(Tk_DoOneEvent(TK_DONT_WAIT) != 0) ;
	    if(Tk_DoOneEvent(TK_DONT_WAIT) == 0) break;
	}
	if(aboveBelow == Above)
	    SetWindowPos(otherPtr->window, winPtr->window, 0, 0, 0, 0,
	    SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
	else SetWindowPos(winPtr->window, otherPtr->window, 0, 0, 0, 0,
	    SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
#endif
	changes.stack_mode = Above;
	for (otherPtr = winPtr->nextPtr; otherPtr != NULL;
		otherPtr = otherPtr->nextPtr) {
	    if ((otherPtr->window != None)
		    && !(otherPtr->flags & TK_TOP_LEVEL)){
		changes.sibling = otherPtr->window;
		changes.stack_mode = Below;
		mask = CWStackMode|CWSibling;
		break;
	    }
	}
#ifndef TCL_MSDOS_PORT
	XConfigureWindow(winPtr->display, winPtr->window, mask, &changes);
#endif
    }
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_MainWindow --
 *
 *	Returns the main window for an application.
 *
 * Results:
 *	If interp has a Tk application associated with it, the main
 *	window for the application is returned.  Otherwise NULL is
 *	returned and an error message is left in interp->result.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
Tk_Window _export
#else
Tk_Window
#endif
Tk_MainWindow(interp)
    Tcl_Interp *interp;			/* Interpreter that embodies the
					 * application. */
{
    Tcl_CmdInfo info;
    if (Tcl_GetCommandInfo(interp, "winfo", &info) == 0) {
	interp->result = "this isn't a Tk application";
	return NULL;
    }
    return (Tk_Window) info.clientData;
}

/*
 *----------------------------------------------------------------------
 *
 * AddToColormapWindows --
 *
 *	This procedure is called to add a given window to the
 *	WM_COLORMAP_WINDOWS property for its top-level, if it
 *	isn't already there.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	WinPtr's window gets added to the WM_COLORMAP_WINDOWS
 *	property of its nearest top-level ancestor.
 *
 *----------------------------------------------------------------------
 */

#ifndef TCL_MSDOS_PORT
static void
AddToColormapWindows(winPtr)
    TkWindow *winPtr;		/* Window with a non-default colormap.
				 * Should not be a top-level window. */
{
    TkWindow *topPtr;
    Window *oldPtr, *newPtr;
    int count, i;

    if (winPtr->window == None) {
	return;
    }

    for (topPtr = winPtr->parentPtr; !(topPtr->flags & TK_TOP_LEVEL);
	    topPtr = topPtr->parentPtr) {
	/* Empty loop body. */
    }

    /*
     * Fetch the old value of the property.
     */

    if (XGetWMColormapWindows(topPtr->display, topPtr->window,
	    &oldPtr, &count) == 0) {
	oldPtr = NULL;
	count = 0;
    }

    /*
     * Make sure that the window isn't already in the list.
     */

    for (i = 0; i < count; i++) {
	if (oldPtr[i] == winPtr->window) {
	    return;
	}
    }

    /*
     * Make a new bigger array and use it to reset the property.
     */

    newPtr = (Window *) ckalloc((unsigned) ((count+1)*sizeof(Window)));
    for (i = 0; i < count; i++) {
	newPtr[i] = oldPtr[i];
    }
    newPtr[count] = winPtr->window;
    XSetWMColormapWindows(topPtr->display, topPtr->window, newPtr, count+1);
    ckfree((char *) newPtr);
    if (oldPtr != NULL) {
	XFree((char *) oldPtr);
    }
}
#endif

#ifdef TCL_MSDOS_PORT
/* Printer Support Routines */

BOOL bUserAbort ;
HWND hDlgPrint ;

BOOL FAR PASCAL _export PrintDlgProc (HWND hDlg, UINT message, UINT wParam,
                                                               LONG lParam)
     {
     switch (message)
          {
          case WM_INITDIALOG:
               EnableMenuItem (GetSystemMenu (hDlg, FALSE), SC_CLOSE,
                                                            MF_GRAYED) ;
               return TRUE ;

          case WM_COMMAND:
               bUserAbort = TRUE ;
               EnableWindow (GetParent (hDlg), TRUE) ;
               DestroyWindow (hDlg) ;
               hDlgPrint = 0 ;
               return TRUE ;
          }
     return FALSE ;
     }          

BOOL FAR PASCAL _export AbortProc (HDC hPrinterDC, short nCode)
     {
     MSG msg ;

     while (!bUserAbort && PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
          {
          if (!hDlgPrint || !IsDialogMessage (hDlgPrint, &msg))
               {
               TranslateMessage (&msg) ;
               DispatchMessage (&msg) ;
               }
          }
     return !bUserAbort ;
     }

static HTASK getTask(HINSTANCE hinst){
    TASKENTRY taskEnt;
    taskEnt.dwSize = sizeof(taskEnt);
    if(TaskFirst(&taskEnt) == 0) return 0;
    do {
	if(taskEnt.hInst == hinst) return taskEnt.hTask;
    } while(TaskNext(&taskEnt) != 0);
    return 0;
}

int
Tk_WinCmd(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;
    int argc;
    char **argv;
{
    char c;
    if (argc < 2) {
	Tcl_AppendResult(interp, "wrong # args: should be \"",
	    argv[0], " arg ?arg?\"", (char *)NULL);
	return TCL_ERROR;
    }
    c = argv[1][0];
    if((c == 'h') && (strcmp(argv[1], "hostname") == 0)) {
	char host[80];
host[0] = 0;
        if(argc != 2) {
	    Tcl_AppendResult(interp, "wrong # args: should be \"",
	        argv[0], " hostname\"", (char *) NULL);
	    return TCL_ERROR;
	}
	if(gethostname(host, sizeof(host)) == 0){
	    lstrcpy(interp->result, host);
	} else {
	    return TCL_ERROR;
	}
    } else if((c == 'k') && (strcmp(argv[1], "kill") == 0)) {
	HTASK hTask;
        if(argc != 3) {
	    Tcl_AppendResult(interp, "wrong # args: should be \"",
	        argv[0], " kill pid\"", (char *) NULL);
	    return TCL_ERROR;
	}
	hTask = getTask((HINSTANCE)atoi(argv[2]));
	if(hTask && IsTask(hTask)) {
	    PostAppMessage(hTask, WM_QUIT, 0, 0L);
	}
	sprintf(interp->result, "%d", hTask);
	return TCL_OK;
    } else if((c == 'p') && (strcmp(argv[1], "print") == 0)) {
        FILE *fp;
        WinMainArgs *wmap;
        static PRINTDLG pd ;
        BOOL            bSuccess ;
        char            szJobName [40] ;
        FARPROC         lpfnAbortProc, lpfnPrintDlgProc ;
        NPSTR           npstrBuffer ;
        short           yChar, nCharsPerLine, nLinesPerPage, nPage, nLine ;
        TEXTMETRIC      tm ;
        WORD            nColCopy, nNonColCopy ;
        if(argc != 3) {
	    Tcl_AppendResult(interp, "wrong # args: should be \"",
	        argv[0], " print filename\"", (char *) NULL);
	    return TCL_ERROR;
	}
        if((fp = fopen(argv[2], "r")) == NULL){
	    Tcl_AppendResult(interp, "could not open file \"",
	        argv[2], "\" for printing", (char *)NULL);
	    return TCL_ERROR;
        }

        pd.lStructSize         = sizeof (PRINTDLG) ;
        pd.hwndOwner           = NULL ;
        pd.hDevMode            = NULL ;
        pd.hDevNames           = NULL ;
        pd.hDC                 = NULL ;
        pd.Flags               = PD_ALLPAGES | PD_COLLATE | PD_RETURNDC ;
        pd.nFromPage           = 0 ;
        pd.nToPage             = 0 ;
        pd.nMinPage            = 0 ;
        pd.nMaxPage            = 0 ;
        pd.nCopies             = 1 ;
        pd.hInstance           = NULL ;
        pd.lCustData           = 0L ;
        pd.lpfnPrintHook       = NULL ;
        pd.lpfnSetupHook       = NULL ;
        pd.lpPrintTemplateName = NULL ;
        pd.lpSetupTemplateName = NULL ;
        pd.hPrintTemplate      = NULL ;
        pd.hSetupTemplate      = NULL ;

        if (!PrintDlg (&pd)) {
#ifdef NOTDEF
	     MessageBox(NULL, "PrintDlg returned FALSE", "Win Print", MB_OK);
#endif
             return TCL_OK;
	}
	/* Clear queue -- otherwise will hang if you do printer setup */
	while(1){
	    while(Tk_DoOneEvent(TK_DONT_WAIT) != 0) ;
	    if(Tk_DoOneEvent(TK_DONT_WAIT) == 0) break;
	}

#ifdef NOTDEF
	MessageBox(NULL, "PrintDlg returned TRUE", "Win Print", MB_OK);
#endif

        GetTextMetrics (pd.hDC, &tm) ;
        yChar = tm.tmHeight + tm.tmExternalLeading ;

        nCharsPerLine = GetDeviceCaps (pd.hDC, HORZRES) / tm.tmAveCharWidth ;
        nLinesPerPage = GetDeviceCaps (pd.hDC, VERTRES) / yChar ;

        npstrBuffer = (NPSTR) LocalAlloc (LPTR, nCharsPerLine + 1) ;
        npstrBuffer[nCharsPerLine] = '\0';

	EnableWindow (((TkWindow *)clientData)->window, FALSE);

        bSuccess   = TRUE ;
        bUserAbort = FALSE ;

        wmap = getWinMainArgs(interp);
        lpfnPrintDlgProc = MakeProcInstance ((FARPROC) PrintDlgProc,
	    wmap->hInstance) ;
        hDlgPrint = CreateDialog (wmap->hInstance, "PrintDlgBox",
	    ((TkWindow *)clientData)->window, lpfnPrintDlgProc) ;
#define IDD_FNAME	10
        SetDlgItemText (hDlgPrint, IDD_FNAME, argv[1]) ;

        lpfnAbortProc = MakeProcInstance((FARPROC) AbortProc, wmap->hInstance) ;
        Escape (pd.hDC, SETABORTPROC, 0, (LPSTR) lpfnAbortProc, NULL) ;

        GetWindowText (((TkWindow *)clientData)->window, szJobName,
	    sizeof (szJobName)) ;

        if (Escape (pd.hDC, STARTDOC, strlen (szJobName), szJobName, NULL) > 0)
            {
            for (nColCopy = 0 ;
                nColCopy < (pd.Flags & PD_COLLATE ? pd.nCopies : 1) ;
                nColCopy++)
                {
                for (nPage = 0 ; !feof(fp) ; nPage++)
                    {
                    for (nNonColCopy = 0 ;
                         nNonColCopy < (pd.Flags & PD_COLLATE ? 1 : pd.nCopies);
                         nNonColCopy++)
                         {
                         for (nLine = 0 ; nLine < nLinesPerPage ; nLine++)
                              {
			      int len;
			      if(!fgets(npstrBuffer, nCharsPerLine, fp)) break;
			      if((len = lstrlen(npstrBuffer)) != 0)
				   npstrBuffer[len - 1] = '\0';

                              TextOut (pd.hDC, 0, yChar * nLine, npstrBuffer,
                                   strlen(npstrBuffer));
                              }

                         if (Escape (pd.hDC, NEWFRAME, 0, NULL, NULL) < 0)
                              {
                              bSuccess = FALSE ;
                              break ;
                              }

                         if (bUserAbort)
                              break ;
                         }

                    if (!bSuccess || bUserAbort)
                         break ;
                    }

                if (!bSuccess || bUserAbort)
                    break ;
                }
           }
        else
           bSuccess = FALSE ;

        if (bSuccess)
            Escape (pd.hDC, ENDDOC, 0, NULL, NULL) ;

        if (!bUserAbort){
	    EnableWindow(((TkWindow *)clientData)->window, TRUE) ;
	    DestroyWindow (hDlgPrint) ;
	}

        LocalFree ((LOCALHANDLE) npstrBuffer) ;
        FreeProcInstance (lpfnPrintDlgProc) ;
        FreeProcInstance (lpfnAbortProc) ;
        DeleteDC (pd.hDC) ;
        fclose(fp);

        if(bSuccess && !bUserAbort) return TCL_OK;
        else return TCL_ERROR;
    } else if((c == 't') && (strcmp(argv[1], "time") == 0)) {
	time_t t;
        if(argc != 2) {
	    Tcl_AppendResult(interp, "wrong # args: should be \"",
	        argv[0], " time\"", (char *) NULL);
	    return TCL_ERROR;
	}
	time(&t);
	tzset();
	wsprintf(interp->result, "%s", ctime(&t));
    } else {
	Tcl_AppendResult(interp, "bad option \"", argv[1],
	    "\": must be hostname, print, or time", (char *) NULL);
	return TCL_ERROR;
    }
    return TCL_OK;
}
#endif
