/*
 * bltWindow.c --
 *
 *	This module implements additional window functionality for
 *	the BLT toolkit, such as transparent Tk windows,
 *	and reparenting Tk windows.
 *
 * Copyright 1991-1998 Lucent Technologies, Inc.
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby
 * granted, provided that the above copyright notice appear in all
 * copies and that both that the copyright notice and warranty
 * disclaimer appear in supporting documentation, and that the names
 * of Lucent Technologies any of their entities not be used in
 * advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.
 *
 * Lucent Technologies disclaims all warranties with regard to this
 * software, including all implied warranties of merchantability and
 * fitness.  In no event shall Lucent Technologies be liable for any
 * special, indirect or consequential damages or any damages
 * whatsoever resulting from loss of use, data or profits, whether in
 * an action of contract, negligence or other tortuous action, arising
 * out of or in connection with the use or performance of this
 * software.
 */

#include "bltInt.h"

#include <X11/Xlib.h>
#ifndef WIN32
#include <X11/Xproto.h>
#endif

typedef struct TkIdStackStruct TkIdStack;
typedef struct TkErrorHandlerStruct TkErrorHandler;
typedef struct TkSelectionInfoStruct TkSelectionInfo;
typedef struct TkClipboardTargetStruct TkClipboardTarget;

#ifndef WIN32
typedef struct TkWindowStruct TkWindow;
#endif
typedef struct TkWindowEventStruct TkWindowEvent;
typedef struct TkMainInfoStruct TkMainInfo;
typedef struct TkEventHandlerStruct TkEventHandler;
typedef struct TkSelHandlerStruct TkSelHandler;
typedef struct TkWinInfoStruct TkWinInfo;
typedef struct TkClassProcsStruct TkClassProcs;
typedef struct TkWindowPrivateStruct TkWindowPrivate;
typedef struct TkGrabEventStruct TkGrabEvent;
typedef struct TkColormapStruct TkColormap;
typedef struct TkStressedCmapStruct TkStressedCmap;
typedef struct TkWmInfoStruct TkWmInfo;

#ifdef XNQueryInputStyle
#define TK_USE_INPUT_METHODS
#endif

/*
 * This defines whether we should try to use XIM over-the-spot style
 * input.  Allow users to override it.  It is a much more elegant use
 * of XIM, but uses a bit more memory.
 */
#ifndef TK_XIM_SPOT
#   define TK_XIM_SPOT	1
#endif

#ifndef TK_REPARENTED
#define TK_REPARENTED 	0
#endif

#if (TK_VERSION_NUMBER >= _VERSION(8,5,0))

typedef struct TkCaret {
    struct TkWindow *winPtr;	/* The window on which we requested caret
				 * placement. */
    int x;			/* Relative x coord of the caret. */
    int y;			/* Relative y coord of the caret. */
    int height;			/* Specified height of the window. */
} TkCaret;

/*
 * One of the following structures is maintained for each display containing a
 * window managed by Tk. In part, the structure is used to store thread-
 * specific data, since each thread will have its own TkDisplay structure.
 */

typedef struct TkDisplayStruct {
    Display *display;		/* Xlib's info about display. */
    struct TkDisplay *nextPtr;	/* Next in list of all displays. */
    char *name;			/* Name of display (with any screen identifier
				 * removed). Malloc-ed. */
    Time lastEventTime;		/* Time of last event received for this
				 * display. */

    /*
     * Information used primarily by tk3d.c:
     */

    int borderInit;		/* 0 means borderTable needs initializing. */
    Tcl_HashTable borderTable;	/* Maps from color name to TkBorder
				 * structure. */

    /*
     * Information used by tkAtom.c only:
     */

    int atomInit;		/* 0 means stuff below hasn't been initialized
				 * yet. */
    Tcl_HashTable nameTable;	/* Maps from names to Atom's. */
    Tcl_HashTable atomTable;	/* Maps from Atom's back to names. */

    /*
     * Information used primarily by tkBind.c:
     */

    int bindInfoStale;		/* Non-zero means the variables in this part
				 * of the structure are potentially incorrect
				 * and should be recomputed. */
    unsigned int modeModMask;	/* Has one bit set to indicate the modifier
				 * corresponding to "mode shift". If no such
				 * modifier, than this is zero. */
    unsigned int metaModMask;	/* Has one bit set to indicate the modifier
				 * corresponding to the "Meta" key. If no such
				 * modifier, then this is zero. */
    unsigned int altModMask;	/* Has one bit set to indicate the modifier
				 * corresponding to the "Meta" key. If no such
				 * modifier, then this is zero. */
    enum {LU_IGNORE, LU_CAPS, LU_SHIFT} lockUsage;
				/* Indicates how to interpret lock
				 * modifier. */
    int numModKeyCodes;		/* Number of entries in modKeyCodes array
				 * below. */
    KeyCode *modKeyCodes;	/* Pointer to an array giving keycodes for all
				 * of the keys that have modifiers associated
				 * with them. Malloc'ed, but may be NULL. */

    /*
     * Information used by tkBitmap.c only:
     */

    int bitmapInit;		/* 0 means tables above need initializing. */
    int bitmapAutoNumber;	/* Used to number bitmaps. */
    Tcl_HashTable bitmapNameTable;
				/* Maps from name of bitmap to the first
				 * TkBitmap record for that name. */
    Tcl_HashTable bitmapIdTable;/* Maps from bitmap id to the TkBitmap
				 * structure for the bitmap. */
    Tcl_HashTable bitmapDataTable;
				/* Used by Tk_GetBitmapFromData to map from a
				 * collection of in-core data about a bitmap
				 * to a reference giving an automatically-
				 * generated name for the bitmap. */

    /*
     * Information used by tkCanvas.c only:
     */

    int numIdSearches;
    int numSlowSearches;

    /*
     * Used by tkColor.c only:
     */

    int colorInit;		/* 0 means color module needs initializing. */
    TkStressedCmap *stressPtr;	/* First in list of colormaps that have filled
				 * up, so we have to pick an approximate
				 * color. */
    Tcl_HashTable colorNameTable;
				/* Maps from color name to TkColor structure
				 * for that color. */
    Tcl_HashTable colorValueTable;
				/* Maps from integer RGB values to TkColor
				 * structures. */

    /*
     * Used by tkCursor.c only:
     */

    int cursorInit;		/* 0 means cursor module need initializing. */
    Tcl_HashTable cursorNameTable;
				/* Maps from a string name to a cursor to the
				 * TkCursor record for the cursor. */
    Tcl_HashTable cursorDataTable;
				/* Maps from a collection of in-core data
				 * about a cursor to a TkCursor structure. */
    Tcl_HashTable cursorIdTable;
				/* Maps from a cursor id to the TkCursor
				 * structure for the cursor. */
    char cursorString[20];	/* Used to store a cursor id string. */
    Font cursorFont;		/* Font to use for standard cursors. None
				 * means font not loaded yet. */

    /*
     * Information used by tkError.c only:
     */

    struct TkErrorHandler *errorPtr;
				/* First in list of error handlers for this
				 * display. NULL means no handlers exist at
				 * present. */
    int deleteCount;		/* Counts # of handlers deleted since last
				 * time inactive handlers were garbage-
				 * collected. When this number gets big,
				 * handlers get cleaned up. */

    /*
     * Used by tkEvent.c only:
     */

    struct TkWindowEvent *delayedMotionPtr;
				/* Points to a malloc-ed motion event whose
				 * processing has been delayed in the hopes
				 * that another motion event will come along
				 * right away and we can merge the two of them
				 * together. NULL means that there is no
				 * delayed motion event. */

    /*
     * Information used by tkFocus.c only:
     */

    int focusDebug;		/* 1 means collect focus debugging
				 * statistics. */
    struct TkWindow *implicitWinPtr;
				/* If the focus arrived at a toplevel window
				 * implicitly via an Enter event (rather than
				 * via a FocusIn event), this points to the
				 * toplevel window. Otherwise it is NULL. */
    struct TkWindow *focusPtr;	/* Points to the window on this display that
				 * should be receiving keyboard events. When
				 * multiple applications on the display have
				 * the focus, this will refer to the innermost
				 * window in the innermost application. This
				 * information isn't used on Windows, but it's
				 * needed on the Mac, and also on X11 when XIM
				 * processing is being done. */

    /*
     * Information used by tkGC.c only:
     */

    Tcl_HashTable gcValueTable; /* Maps from a GC's values to a TkGC structure
				 * describing a GC with those values. */
    Tcl_HashTable gcIdTable;    /* Maps from a GC to a TkGC. */
    int gcInit;			/* 0 means the tables below need
				 * initializing. */

    /*
     * Information used by tkGeometry.c only:
     */

    Tcl_HashTable maintainHashTable;
				/* Hash table that maps from a master's
				 * Tk_Window token to a list of slaves managed
				 * by that master. */
    int geomInit;

    /*
     * Information used by tkGet.c only:
     */

    Tcl_HashTable uidTable;	/* Stores all Tk_Uid used in a thread. */
    int uidInit;		/* 0 means uidTable needs initializing. */

    /*
     * Information used by tkGrab.c only:
     */

    struct TkWindow *grabWinPtr;/* Window in which the pointer is currently
				 * grabbed, or NULL if none. */
    struct TkWindow *eventualGrabWinPtr;
				/* Value that grabWinPtr will have once the
				 * grab event queue (below) has been
				 * completely emptied. */
    struct TkWindow *buttonWinPtr;
				/* Window in which first mouse button was
				 * pressed while grab was in effect, or NULL
				 * if no such press in effect. */
    struct TkWindow *serverWinPtr;
				/* If no application contains the pointer then
				 * this is NULL. Otherwise it contains the
				 * last window for which we've gotten an Enter
				 * or Leave event from the server (i.e. the
				 * last window known to have contained the
				 * pointer). Doesn't reflect events that were
				 * synthesized in tkGrab.c. */
    TkGrabEvent *firstGrabEventPtr;
				/* First in list of enter/leave events
				 * synthesized by grab code. These events must
				 * be processed in order before any other
				 * events are processed. NULL means no such
				 * events. */
    TkGrabEvent *lastGrabEventPtr;
				/* Last in list of synthesized events, or NULL
				 * if list is empty. */
    int grabFlags;		/* Miscellaneous flag values. See definitions
				 * in tkGrab.c. */

    /*
     * Information used by tkGrid.c only:
     */

    int gridInit;		/* 0 means table below needs initializing. */
    Tcl_HashTable gridHashTable;/* Maps from Tk_Window tokens to corresponding
				 * Grid structures. */

    /*
     * Information used by tkImage.c only:
     */

    int imageId;		/* Value used to number image ids. */

    /*
     * Information used by tkMacWinMenu.c only:
     */

    int postCommandGeneration;

    /*
     * Information used by tkOption.c only.
     */

    /*
     * Information used by tkPack.c only.
     */

    int packInit;		/* 0 means table below needs initializing. */
    Tcl_HashTable packerHashTable;
				/* Maps from Tk_Window tokens to corresponding
				 * Packer structures. */

    /*
     * Information used by tkPlace.c only.
     */

    int placeInit;		/* 0 means tables below need initializing. */
    Tcl_HashTable masterTable;	/* Maps from Tk_Window toke to the Master
				 * structure for the window, if it exists. */
    Tcl_HashTable slaveTable;	/* Maps from Tk_Window toke to the Slave
				 * structure for the window, if it exists. */

    /*
     * Information used by tkSelect.c and tkClipboard.c only:
     */

    struct TkSelectionInfo *selectionInfoPtr;
				/* First in list of selection information
				 * records. Each entry contains information
				 * about the current owner of a particular
				 * selection on this display. */
    Atom multipleAtom;		/* Atom for MULTIPLE. None means selection
				 * stuff isn't initialized. */
    Atom incrAtom;		/* Atom for INCR. */
    Atom targetsAtom;		/* Atom for TARGETS. */
    Atom timestampAtom;		/* Atom for TIMESTAMP. */
    Atom textAtom;		/* Atom for TEXT. */
    Atom compoundTextAtom;	/* Atom for COMPOUND_TEXT. */
    Atom applicationAtom;	/* Atom for TK_APPLICATION. */
    Atom windowAtom;		/* Atom for TK_WINDOW. */
    Atom clipboardAtom;		/* Atom for CLIPBOARD. */
    Atom utf8Atom;		/* Atom for UTF8_STRING. */

    Tk_Window clipWindow;	/* Window used for clipboard ownership and to
				 * retrieve selections between processes. NULL
				 * means clipboard info hasn't been
				 * initialized. */
    int clipboardActive;	/* 1 means we currently own the clipboard
				 * selection, 0 means we don't. */
    struct TkMainInfo *clipboardAppPtr;
				/* Last application that owned clipboard. */
    struct TkClipboardTarget *clipTargetPtr;
				/* First in list of clipboard type information
				 * records. Each entry contains information
				 * about the buffers for a given selection
				 * target. */

    /*
     * Information used by tkSend.c only:
     */

    Tk_Window commTkwin;	/* Window used for communication between
				 * interpreters during "send" commands. NULL
				 * means send info hasn't been initialized
				 * yet. */
    Atom commProperty;		/* X's name for comm property. */
    Atom registryProperty;	/* X's name for property containing registry
				 * of interpreter names. */
    Atom appNameProperty;	/* X's name for property used to hold the
				 * application name on each comm window. */

    /*
     * Information used by tkXId.c only:
     */

    struct TkIdStack *idStackPtr;
				/* First in list of chunks of free resource
				 * identifiers, or NULL if there are no free
				 * resources. */
    XID (*defaultAllocProc) (Display *display);
				/* Default resource allocator for display. */
    struct TkIdStack *windowStackPtr;
				/* First in list of chunks of window ids that
				 * can't be reused right now. */
    Tcl_TimerToken idCleanupScheduled;
				/* If set, it means a call to WindowIdCleanup
				 * has already been scheduled, 0 means it
				 * hasn't. */

    /*
     * Information used by tkUnixWm.c and tkWinWm.c only:
     */

    struct TkWmInfo *firstWmPtr;/* Points to first top-level window. */
    struct TkWmInfo *foregroundWmPtr;
				/* Points to the foreground window. */

    /*
     * Information maintained by tkWindow.c for use later on by tkXId.c:
     */

    int destroyCount;		/* Number of Tk_DestroyWindow operations in
				 * progress. */
    unsigned long lastDestroyRequest;
				/* Id of most recent XDestroyWindow request;
				 * can re-use ids in windowStackPtr when
				 * server has seen this request and event
				 * queue is empty. */

    /*
     * Information used by tkVisual.c only:
     */

    TkColormap *cmapPtr;	/* First in list of all non-default colormaps
				 * allocated for this display. */

    /*
     * Miscellaneous information:
     */

#ifdef TK_USE_INPUT_METHODS
    XIM inputMethod;		/* Input method for this display. */
    XIMStyle inputStyle;	/* Input style selected for this display. */
    XFontSet inputXfs;		/* XFontSet cached for over-the-spot XIM. */
#endif /* TK_USE_INPUT_METHODS */
    Tcl_HashTable winTable;	/* Maps from X window ids to TkWindow ptrs. */

    int refCount;		/* Reference count of how many Tk applications
				 * are using this display. Used to clean up
				 * the display when we no longer have any Tk
				 * applications using it. */

    /*
     * The following field were all added for Tk8.3
     */

    int mouseButtonState;	/* Current mouse button state for this
				 * display. */
    Window mouseButtonWindow;	/* Window the button state was set in, added
				 * in Tk 8.4. */
    Window warpWindow;
    int warpX;
    int warpY;

    /*
     * The following field(s) were all added for Tk8.4
     */

    unsigned int flags;		/* Various flag values: these are all defined
				 * in below. */
    TkCaret caret;		/* Information about the caret for this
				 * display. This is not a pointer. */

    int iconDataSize;		/* Size of default iconphoto image data. */
    unsigned char *iconDataPtr;	/* Default iconphoto image data, if set. */
} TkDisplay;

#elif (TK_VERSION_NUMBER >= _VERSION(8,1,0))

typedef struct TkCaret {
    struct TkWindow *winPtr;	/* the window on which we requested caret
				 * placement */
    int x;			/* relative x coord of the caret */
    int y;			/* relative y coord of the caret */
    int height;			/* specified height of the window */
} TkCaret;

/*
 * One of the following structures is maintained for each display
 * containing a window managed by Tk.  In part, the structure is
 * used to store thread-specific data, since each thread will have
 * its own TkDisplay structure.
 */

typedef struct TkDisplayStruct {
    Display *display;		/* Xlib's info about display. */
    struct TkDisplayStruct *nextPtr; /* Next in list of all displays. */
    char *name;			/* Name of display (with any screen
				 * identifier removed).  Malloc-ed. */
    Time lastEventTime;		/* Time of last event received for this
				 * display. */

    /*
     * Information used primarily by tk3d.c:
     */

    int borderInit;		/* 0 means borderTable needs initializing. */
    Tcl_HashTable borderTable;	/* Maps from color name to TkBorder
				 * structure. */

    /*
     * Information used by tkAtom.c only:
     */

    int atomInit;		/* 0 means stuff below hasn't been
				 * initialized yet. */
    Tcl_HashTable nameTable;	/* Maps from names to Atom's. */
    Tcl_HashTable atomTable;	/* Maps from Atom's back to names. */

    /*
     * Information used primarily by tkBind.c:
     */

    int bindInfoStale;		/* Non-zero means the variables in this
				 * part of the structure are potentially
				 * incorrect and should be recomputed. */
    unsigned int modeModMask;	/* Has one bit set to indicate the modifier
				 * corresponding to "mode shift".  If no
				 * such modifier, than this is zero. */
    unsigned int metaModMask;	/* Has one bit set to indicate the modifier
				 * corresponding to the "Meta" key.  If no
				 * such modifier, then this is zero. */
    unsigned int altModMask;	/* Has one bit set to indicate the modifier
				 * corresponding to the "Meta" key.  If no
				 * such modifier, then this is zero. */
    enum {
	LU_IGNORE, LU_CAPS, LU_SHIFT
    } lockUsage;		/* Indicates how to interpret lock modifier. */
    int numModKeyCodes;		/* Number of entries in modKeyCodes array
				 * below. */
    KeyCode *modKeyCodes;	/* Pointer to an array giving keycodes for
				 * all of the keys that have modifiers
				 * associated with them.  Malloc'ed, but
				 * may be NULL. */

    /*
     * Information used by tkBitmap.c only:
     */

    int bitmapInit;		/* 0 means tables above need initializing. */
    int bitmapAutoNumber;	/* Used to number bitmaps. */
    Tcl_HashTable bitmapNameTable;
				/* Maps from name of bitmap to the first
				 * TkBitmap record for that name. */
    Tcl_HashTable bitmapIdTable;/* Maps from bitmap id to the TkBitmap
				 * structure for the bitmap. */
    Tcl_HashTable bitmapDataTable;
				/* Used by Tk_GetBitmapFromData to map from
				 * a collection of in-core data about a
				 * bitmap to a reference giving an auto-
				 * matically-generated name for the bitmap. */

    /*
     * Information used by tkCanvas.c only:
     */

    int numIdSearches;
    int numSlowSearches;

    /*
     * Used by tkColor.c only:
     */

    int colorInit;		/* 0 means color module needs initializing. */
    TkStressedCmap *stressPtr;	/* First in list of colormaps that have
				 * filled up, so we have to pick an
				 * approximate color. */
    Tcl_HashTable colorNameTable;
				/* Maps from color name to TkColor structure
				 * for that color. */
    Tcl_HashTable colorValueTable;
				/* Maps from integer RGB values to TkColor
				 * structures. */

    /*
     * Used by tkCursor.c only:
     */

    int cursorInit;		/* 0 means cursor module need initializing. */
    Tcl_HashTable cursorNameTable;
				/* Maps from a string name to a cursor to the
				 * TkCursor record for the cursor. */
    Tcl_HashTable cursorDataTable;
				/* Maps from a collection of in-core data
				 * about a cursor to a TkCursor structure. */
    Tcl_HashTable cursorIdTable;
				/* Maps from a cursor id to the TkCursor
				 * structure for the cursor. */
    char cursorString[20];	/* Used to store a cursor id string. */
    Font cursorFont;		/* Font to use for standard cursors.
				 * None means font not loaded yet. */

    /*
     * Information used by tkError.c only:
     */

    struct TkErrorHandler *errorPtr;
				/* First in list of error handlers
				 * for this display.  NULL means
				 * no handlers exist at present. */
    int deleteCount;		/* Counts # of handlers deleted since
				 * last time inactive handlers were
				 * garbage-collected.  When this number
				 * gets big, handlers get cleaned up. */

    /*
     * Used by tkEvent.c only:
     */

    struct TkWindowEvent *delayedMotionPtr;
				/* Points to a malloc-ed motion event
				 * whose processing has been delayed in
				 * the hopes that another motion event
				 * will come along right away and we can
				 * merge the two of them together.  NULL
				 * means that there is no delayed motion
				 * event. */

    /*
     * Information used by tkFocus.c only:
     */

    int focusDebug;		/* 1 means collect focus debugging
				 * statistics. */
    struct TkWindow *implicitWinPtr;
				/* If the focus arrived at a toplevel window
				 * implicitly via an Enter event (rather
				 * than via a FocusIn event), this points
				 * to the toplevel window.  Otherwise it is
				 * NULL. */
    struct TkWindow *focusPtr;	/* Points to the window on this display that
				 * should be receiving keyboard events.  When
				 * multiple applications on the display have
				 * the focus, this will refer to the
				 * innermost window in the innermost
				 * application.  This information isn't used
				 * under Unix or Windows, but it's needed on
				 * the Macintosh. */

    /*
     * Information used by tkGC.c only:
     */

    Tcl_HashTable gcValueTable;	/* Maps from a GC's values to a TkGC structure
				 * describing a GC with those values. */
    Tcl_HashTable gcIdTable;	/* Maps from a GC to a TkGC. */
    int gcInit;			/* 0 means the tables below need
				 * initializing. */

    /*
     * Information used by tkGeometry.c only:
     */

    Tcl_HashTable maintainHashTable;
				/* Hash table that maps from a master's
				 * Tk_Window token to a list of slaves
				 * managed by that master. */
    int geomInit;

    /*
     * Information used by tkGet.c only:
     */

    Tcl_HashTable uidTable;	/* Stores all Tk_Uids used in a thread. */
    int uidInit;		/* 0 means uidTable needs initializing. */

    /*
     * Information used by tkGrab.c only:
     */

    struct TkWindow *grabWinPtr;
				/* Window in which the pointer is currently
				 * grabbed, or NULL if none. */
    struct TkWindow *eventualGrabWinPtr;
				/* Value that grabWinPtr will have once the
				 * grab event queue (below) has been
				 * completely emptied. */
    struct TkWindow *buttonWinPtr;
				/* Window in which first mouse button was
				 * pressed while grab was in effect, or NULL
				 * if no such press in effect. */
    struct TkWindow *serverWinPtr;
				/* If no application contains the pointer then
				 * this is NULL.  Otherwise it contains the
				 * last window for which we've gotten an
				 * Enter or Leave event from the server (i.e.
				 * the last window known to have contained
				 * the pointer).  Doesn't reflect events
				 * that were synthesized in tkGrab.c. */
    TkGrabEvent *firstGrabEventPtr;
				/* First in list of enter/leave events
				 * synthesized by grab code.  These events
				 * must be processed in order before any other
				 * events are processed.  NULL means no such
				 * events. */
    TkGrabEvent *lastGrabEventPtr;
				/* Last in list of synthesized events, or NULL
				 * if list is empty. */
    int grabFlags;		/* Miscellaneous flag values.  See definitions
				 * in tkGrab.c. */

    /*
     * Information used by tkGrid.c only:
     */

    int gridInit;		/* 0 means table below needs initializing. */
    Tcl_HashTable gridHashTable;/* Maps from Tk_Window tokens to
				 * corresponding Grid structures. */

    /*
     * Information used by tkImage.c only:
     */

    int imageId;		/* Value used to number image ids. */

    /*
     * Information used by tkMacWinMenu.c only:
     */

    int postCommandGeneration;

    /*
     * Information used by tkOption.c only.
     */



    /*
     * Information used by tkPack.c only.
     */

    int packInit;		/* 0 means table below needs initializing. */
    Tcl_HashTable packerHashTable;
				/* Maps from Tk_Window tokens to
				 * corresponding Packer structures. */


    /*
     * Information used by tkPlace.c only.
     */

    int placeInit;		/* 0 means tables below need initializing. */
    Tcl_HashTable masterTable;	/* Maps from Tk_Window toke to the Master
				 * structure for the window, if it exists. */
    Tcl_HashTable slaveTable;	/* Maps from Tk_Window toke to the Slave
				 * structure for the window, if it exists. */

    /*
     * Information used by tkSelect.c and tkClipboard.c only:
     */


    struct TkSelectionInfo *selectionInfoPtr;
    /* First in list of selection information
				 * records.  Each entry contains information
				 * about the current owner of a particular
				 * selection on this display. */
    Atom multipleAtom;		/* Atom for MULTIPLE.  None means
				 * selection stuff isn't initialized. */
    Atom incrAtom;		/* Atom for INCR. */
    Atom targetsAtom;		/* Atom for TARGETS. */
    Atom timestampAtom;		/* Atom for TIMESTAMP. */
    Atom textAtom;		/* Atom for TEXT. */
    Atom compoundTextAtom;	/* Atom for COMPOUND_TEXT. */
    Atom applicationAtom;	/* Atom for TK_APPLICATION. */
    Atom windowAtom;		/* Atom for TK_WINDOW. */
    Atom clipboardAtom;		/* Atom for CLIPBOARD. */
#if (TK_VERSION_NUMBER >= _VERSION(8,4,0))
    Atom utf8Atom;
#endif
    Tk_Window clipWindow;	/* Window used for clipboard ownership and to
				 * retrieve selections between processes. NULL
				 * means clipboard info hasn't been
				 * initialized. */
    int clipboardActive;	/* 1 means we currently own the clipboard
				 * selection, 0 means we don't. */
    struct TkMainInfo *clipboardAppPtr;
				/* Last application that owned clipboard. */
    struct TkClipboardTarget *clipTargetPtr;
				/* First in list of clipboard type information
				 * records.  Each entry contains information
				 * about the buffers for a given selection
				 * target. */

    /*
     * Information used by tkSend.c only:
     */

    Tk_Window commTkwin;	/* Window used for communication
				 * between interpreters during "send"
				 * commands.  NULL means send info hasn't
				 * been initialized yet. */
    Atom commProperty;		/* X's name for comm property. */
    Atom registryProperty;	/* X's name for property containing
				 * registry of interpreter names. */
    Atom appNameProperty;	/* X's name for property used to hold the
				 * application name on each comm window. */

    /*
     * Information used by tkXId.c only:
     */

    struct TkIdStack *idStackPtr;
				/* First in list of chunks of free resource
				 * identifiers, or NULL if there are no free
				 * resources. */
    XID(*defaultAllocProc) _ANSI_ARGS_((Display *display));
				/* Default resource allocator for display. */
    struct TkIdStack *windowStackPtr;
				/* First in list of chunks of window
				 * identifers that can't be reused right
				 * now. */
#if (TK_VERSION_NUMBER < _VERSION(8,4,0))
    int idCleanupScheduled;	/* 1 means a call to WindowIdCleanup has
				 * already been scheduled, 0 means it
				 * hasn't. */
#else
    Tcl_TimerToken idCleanupScheduled;
				/* If set, it means a call to WindowIdCleanup
				 * has already been scheduled, 0 means it
				 * hasn't. */
#endif
    /*
     * Information used by tkUnixWm.c and tkWinWm.c only:
     */

#if (TK_VERSION_NUMBER < _VERSION(8,4,0))
    int wmTracing;		/* Used to enable or disable tracing in
				 * this module.  If tracing is enabled,
				 * then information is printed on
				 * standard output about interesting
				 * interactions with the window manager. */
#endif
    struct TkWmInfo *firstWmPtr; /* Points to first top-level window. */
    struct TkWmInfo *foregroundWmPtr;
				/* Points to the foreground window. */

    /*
     * Information maintained by tkWindow.c for use later on by tkXId.c:
     */


    int destroyCount;		/* Number of Tk_DestroyWindow operations
				 * in progress. */
    unsigned long lastDestroyRequest;
				/* Id of most recent XDestroyWindow request;
				 * can re-use ids in windowStackPtr when
				 * server has seen this request and event
				 * queue is empty. */

    /*
     * Information used by tkVisual.c only:
     */

    TkColormap *cmapPtr;	/* First in list of all non-default colormaps
				 * allocated for this display. */

    /*
     * Miscellaneous information:
     */

#ifdef TK_USE_INPUT_METHODS
    XIM inputMethod;		/* Input method for this display */
#if (TK_VERSION_NUMBER >= _VERSION(8,4,0))
#if TK_XIM_SPOT
    XFontSet inputXfs;		/* XFontSet cached for over-the-spot XIM. */
#endif /* TK_XIM_SPOT */
#endif /* TK_VERSION_NUMBER >= 8.4 */
#endif /* TK_USE_INPUT_METHODS */
    Tcl_HashTable winTable;	/* Maps from X window ids to TkWindow ptrs. */
    int refCount;		/* Reference count of how many Tk applications
                                 * are using this display. Used to clean up
                                 * the display when we no longer have any
                                 * Tk applications using it.
                                 */
    /*
     * The following field were all added for Tk8.3
     */
    int mouseButtonState;       /* current mouse button state for this
                                 * display */
#if (TK_VERSION_NUMBER < _VERSION(8,4,0))
    int warpInProgress;
#endif
    Window warpWindow;
    int warpX;
    int warpY;
#if (TK_VERSION_NUMBER < _VERSION(8,4,0))
    int useInputMethods;        /* Whether to use input methods */
#else
    /*
     * The following field(s) were all added for Tk8.4
     */
    long deletionEpoch;		/* Incremented by window deletions */
    unsigned int flags;		/* Various flag values:  these are all
				 * defined in below. */
    TkCaret caret;		/* information about the caret for this
				 * display.  This is not a pointer. */
#endif
} TkDisplay;

#else

/*
 * One of the following structures is maintained for each display
 * containing a window managed by Tk:
 */
typedef struct TkDisplayStruct {
    Display *display;		/* Xlib's info about display. */
    struct TkDisplayStruct *nextPtr; /* Next in list of all displays. */
    char *name;			/* Name of display (with any screen
				 * identifier removed).  Malloc-ed. */
    Time lastEventTime;		/* Time of last event received for this
				 * display. */

    /*
     * Information used primarily by tkBind.c:
     */

    int bindInfoStale;		/* Non-zero means the variables in this
				 * part of the structure are potentially
				 * incorrect and should be recomputed. */
    unsigned int modeModMask;	/* Has one bit set to indicate the modifier
				 * corresponding to "mode shift".  If no
				 * such modifier, than this is zero. */
    unsigned int metaModMask;	/* Has one bit set to indicate the modifier
				 * corresponding to the "Meta" key.  If no
				 * such modifier, then this is zero. */
    unsigned int altModMask;	/* Has one bit set to indicate the modifier
				 * corresponding to the "Meta" key.  If no
				 * such modifier, then this is zero. */
    enum {
	LU_IGNORE, LU_CAPS, LU_SHIFT
    } lockUsage;
    /* Indicates how to interpret lock modifier. */
    int numModKeyCodes;		/* Number of entries in modKeyCodes array
				 * below. */
    KeyCode *modKeyCodes;	/* Pointer to an array giving keycodes for
				 * all of the keys that have modifiers
				 * associated with them.  Malloc'ed, but
				 * may be NULL. */

    /*
     * Information used by tkError.c only:
     */

    TkErrorHandler *errorPtr;
    /* First in list of error handlers
				 * for this display.  NULL means
				 * no handlers exist at present. */
     int deleteCount;		/* Counts # of handlers deleted since
				 * last time inactive handlers were
				 * garbage-collected.  When this number
				 * gets big, handlers get cleaned up. */

    /*
     * Information used by tkSend.c only:
     */

    Tk_Window commTkwin;	/* Window used for communication
				 * between interpreters during "send"
				 * commands.  NULL means send info hasn't
				 * been initialized yet. */
    Atom commProperty;		/* X's name for comm property. */
    Atom registryProperty;	/* X's name for property containing
				 * registry of interpreter names. */
    Atom appNameProperty;	/* X's name for property used to hold the
				 * application name on each comm window. */

    /*
     * Information used by tkSelect.c and tkClipboard.c only:
     */

     TkSelectionInfo *selectionInfoPtr;
    /* First in list of selection information
				 * records.  Each entry contains information
				 * about the current owner of a particular
				 * selection on this display. */
    Atom multipleAtom;		/* Atom for MULTIPLE.  None means
				 * selection stuff isn't initialized. */
    Atom incrAtom;		/* Atom for INCR. */
    Atom targetsAtom;		/* Atom for TARGETS. */
    Atom timestampAtom;		/* Atom for TIMESTAMP. */
    Atom textAtom;		/* Atom for TEXT. */
    Atom compoundTextAtom;	/* Atom for COMPOUND_TEXT. */
    Atom applicationAtom;	/* Atom for TK_APPLICATION. */
    Atom windowAtom;		/* Atom for TK_WINDOW. */
    Atom clipboardAtom;		/* Atom for CLIPBOARD. */

    Tk_Window clipWindow;	/* Window used for clipboard ownership and to
				 * retrieve selections between processes. NULL
				 * means clipboard info hasn't been
				 * initialized. */
    int clipboardActive;	/* 1 means we currently own the clipboard
				 * selection, 0 means we don't. */
     TkMainInfo *clipboardAppPtr;
     /* Last application that owned clipboard. */
     TkClipboardTarget *clipTargetPtr;
     /* First in list of clipboard type information
				 * records.  Each entry contains information
				 * about the buffers for a given selection
				 * target. */

    /*
     * Information used by tkAtom.c only:
     */

    int atomInit;		/* 0 means stuff below hasn't been
				 * initialized yet. */
    Tcl_HashTable nameTable;	/* Maps from names to Atom's. */
    Tcl_HashTable atomTable;	/* Maps from Atom's back to names. */

    /*
     * Information used by tkCursor.c only:
     */

    Font cursorFont;		/* Font to use for standard cursors.
				 * None means font not loaded yet. */

    /*
     * Information used by tkGrab.c only:
     */

     TkWindow *grabWinPtr;
    /* Window in which the pointer is currently
				 * grabbed, or NULL if none. */
     TkWindow *eventualGrabWinPtr;
    /* Value that grabWinPtr will have once the
				 * grab event queue (below) has been
				 * completely emptied. */
     TkWindow *buttonWinPtr;
    /* Window in which first mouse button was
				 * pressed while grab was in effect, or NULL
				 * if no such press in effect. */
     TkWindow *serverWinPtr;
    /* If no application contains the pointer then
				 * this is NULL.  Otherwise it contains the
				 * last window for which we've gotten an
				 * Enter or Leave event from the server (i.e.
				 * the last window known to have contained
				 * the pointer).  Doesn't reflect events
				 * that were synthesized in tkGrab.c. */
    TkGrabEvent *firstGrabEventPtr;
    /* First in list of enter/leave events
				 * synthesized by grab code.  These events
				 * must be processed in order before any other
				 * events are processed.  NULL means no such
				 * events. */
    TkGrabEvent *lastGrabEventPtr;
    /* Last in list of synthesized events, or NULL
				 * if list is empty. */
    int grabFlags;		/* Miscellaneous flag values.  See definitions
				 * in tkGrab.c. */

    /*
     * Information used by tkXId.c only:
     */

     TkIdStack *idStackPtr;
    /* First in list of chunks of free resource
				 * identifiers, or NULL if there are no free
				 * resources. */
              XID(*defaultAllocProc) _ANSI_ARGS_((Display *display));
    /* Default resource allocator for display. */
     TkIdStack *windowStackPtr;
    /* First in list of chunks of window
				 * identifers that can't be reused right
				 * now. */
    int idCleanupScheduled;	/* 1 means a call to WindowIdCleanup has
				 * already been scheduled, 0 means it
				 * hasn't. */

    /*
     * Information maintained by tkWindow.c for use later on by tkXId.c:
     */


    int destroyCount;		/* Number of Tk_DestroyWindow operations
				 * in progress. */
    unsigned long lastDestroyRequest;
    /* Id of most recent XDestroyWindow request;
				 * can re-use ids in windowStackPtr when
				 * server has seen this request and event
				 * queue is empty. */

    /*
     * Information used by tkVisual.c only:
     */

    TkColormap *cmapPtr;	/* First in list of all non-default colormaps
				 * allocated for this display. */

    /*
     * Information used by tkFocus.c only:
     */
#if (TK_MAJOR_VERSION == 4)

     TkWindow *focusWinPtr;
				/* Window that currently has the focus for
				 * this display, or NULL if none. */
     TkWindow *implicitWinPtr;
				/* If the focus arrived at a toplevel window
				 * implicitly via an Enter event (rather
				 * than via a FocusIn event), this points
				 * to the toplevel window.  Otherwise it is
				 * NULL. */
     TkWindow *focusOnMapPtr;
				/* This points to a toplevel window that is
				 * supposed to receive the X input focus as
				 * soon as it is mapped (needed to handle the
				 * fact that X won't allow the focus on an
				 * unmapped window).  NULL means no delayed
				 * focus op in progress. */
    int forceFocus;		/* Associated with focusOnMapPtr:  non-zero
				 * means claim the focus even if some other
				 * application currently has it. */
#else
     TkWindow *implicitWinPtr;
				/* If the focus arrived at a toplevel window
				 * implicitly via an Enter event (rather
				 * than via a FocusIn event), this points
				 * to the toplevel window.  Otherwise it is
				 * NULL. */
     TkWindow *focusPtr;	/* Points to the window on this display that
				 * should be receiving keyboard events.  When
				 * multiple applications on the display have
				 * the focus, this will refer to the
				 * innermost window in the innermost
				 * application.  This information isn't used
				 * under Unix or Windows, but it's needed on
				 * the Macintosh. */
#endif /* TK_MAJOR_VERSION == 4 */

    /*
     * Used by tkColor.c only:
     */

    TkStressedCmap *stressPtr;	/* First in list of colormaps that have
				 * filled up, so we have to pick an
				 * approximate color. */

    /*
     * Used by tkEvent.c only:
     */

     TkWindowEvent *delayedMotionPtr;
				/* Points to a malloc-ed motion event
				 * whose processing has been delayed in
				 * the hopes that another motion event
				 * will come along right away and we can
				 * merge the two of them together.  NULL
				 * means that there is no delayed motion
				 * event. */
    /*
     * Miscellaneous information:
     */

#ifdef TK_USE_INPUT_METHODS
    XIM inputMethod;		/* Input method for this display */
#endif /* TK_USE_INPUT_METHODS */
    Tcl_HashTable winTable;	/* Maps from X window ids to TkWindow ptrs. */
#if (TK_MAJOR_VERSION > 4)
    int refCount;		/* Reference count of how many Tk applications
                                 * are using this display. Used to clean up
                                 * the display when we no longer have any
                                 * Tk applications using it.
                                 */
#endif /* TK_MAJOR_VERSION > 4 */

} TkDisplay;

#endif /* TK_VERSION_NUMBER >= _VERSION(8,1,0) */


struct TkWindowStruct {
    Display *display;
    TkDisplay *dispPtr;
    int screenNum;
    Visual *visual;
    int depth;
    Window window;
    TkWindow *childList;
    TkWindow *lastChildPtr;
    TkWindow *parentPtr;
    TkWindow *nextPtr;
    TkMainInfo *infoPtr;
    char *pathName;
    Tk_Uid nameUid;
    Tk_Uid classUid;
    XWindowChanges changes;
    unsigned int dirtyChanges;
    XSetWindowAttributes atts;
    unsigned long dirtyAtts;
    unsigned int flags;
    TkEventHandler *handlerList;
#ifdef TK_USE_INPUT_METHODS
    XIC inputContext;
#endif /* TK_USE_INPUT_METHODS */
    ClientData *tagPtr;
    int nTags;
    int optionLevel;
    TkSelHandler *selHandlerList;
    Tk_GeomMgr *geomMgrPtr;
    ClientData geomData;
    int reqWidth, reqHeight;
    int internalBorderWidth;
    TkWinInfo *wmInfoPtr;
#if (TK_MAJOR_VERSION > 4)
    TkClassProcs *classProcsPtr;
    ClientData instanceData;
#endif
    TkWindowPrivate *privatePtr;
};

#ifdef WIN32
/*
 *----------------------------------------------------------------------
 *
 * GetWindowHandle --
 *
 *      Returns the XID for the Tk_Window given.  Starting in Tk 8.0,
 *      the toplevel widgets are wrapped by another window.
 *      Currently there's no way to get at that window, other than
 *      what is done here: query the X window hierarchy and grab the
 *      parent.
 *
 * Results:
 *      Returns the X Window ID of the widget.  If it's a toplevel, then
 *	the XID of the wrapper is returned.
 *
 *----------------------------------------------------------------------
 */
static HWND
GetWindowHandle(Tk_Window tkwin)
{
    HWND hWnd;
    Window window;
    
    window = Tk_WindowId(tkwin);
    if (window == None) {
	Tk_MakeWindowExist(tkwin);
    }
    hWnd = Tk_GetHWND(Tk_WindowId(tkwin));
#if (TK_MAJOR_VERSION > 4)
    if (Tk_IsTopLevel(tkwin)) {
	hWnd = GetParent(hWnd);
    }
#endif /* TK_MAJOR_VERSION > 4 */
    return hWnd;
}

#else

Window
Blt_GetParent(display, window)
    Display *display;
    Window window;
{
    Window root, parent;
    Window *dummy;
    unsigned int count;

    if (XQueryTree(display, window, &root, &parent, &dummy, &count) > 0) {
	XFree(dummy);
	return parent;
    }
    return None;
}

static Window
GetWindowId(tkwin)
    Tk_Window tkwin;
{
    Window window;

    Tk_MakeWindowExist(tkwin);
    window = Tk_WindowId(tkwin);
#if (TK_MAJOR_VERSION > 4)
    if (Tk_IsTopLevel(tkwin)) {
	Window parent;

	parent = Blt_GetParent(Tk_Display(tkwin), window);
	if (parent != None) {
	    window = parent;
	}
	window = parent;
    }
#endif /* TK_MAJOR_VERSION > 4 */
    return window;
}

#endif /* WIN32 */

/*
 *----------------------------------------------------------------------
 *
 * 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)
    Tk_FakeWin *winPtr;		/* Window whose configuration was just
				 * changed. */
{
    XEvent event;

    event.type = ConfigureNotify;
    event.xconfigure.serial = LastKnownRequestProcessed(winPtr->display);
    event.xconfigure.send_event = False;
    event.xconfigure.display = winPtr->display;
    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;
    event.xconfigure.border_width = winPtr->changes.border_width;
    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);
}

/*
 *--------------------------------------------------------------
 *
 * Blt_MakeTransparentWindowExist --
 *
 *	Similar to Tk_MakeWindowExist but instead creates a
 *	transparent window to block for user events from sibling
 *	windows.
 *
 *	Differences from Tk_MakeWindowExist.
 *
 *	1. This is always a "busy" window. There's never a
 *	   platform-specific class procedure to execute instead.
 *	2. The window is transparent and never will contain children,
 *	   so colormap information is irrelevant.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	When the procedure returns, the internal window associated
 *	with tkwin is guaranteed to exist.  This may require the
 *	window's ancestors to be created too.
 *
 *--------------------------------------------------------------
 */
void
Blt_MakeTransparentWindowExist(tkwin, parent, isBusy)
    Tk_Window tkwin;		/* Token for window. */
    Window parent;		/* Parent window. */
    int isBusy;			/*  */
{
    TkWindow *winPtr = (TkWindow *) tkwin;
    TkWindow *winPtr2;
    Tcl_HashEntry *hPtr;
    int notUsed;
    TkDisplay *dispPtr;
#ifdef WIN32
    HWND hParent;
    int style;
    DWORD exStyle;
    HWND hWnd;
#else
    long int mask;
#endif /* WIN32 */

    if (winPtr->window != None) {
	return;			/* Window already exists. */
    }
#ifdef notdef			
    if ((winPtr->parentPtr == NULL) || (winPtr->flags & TK_TOP_LEVEL)) {
	parent = XRootWindow(winPtr->display, winPtr->screenNum);
	/* TODO: Make the entire screen busy */
    } else {
	if (Tk_WindowId(winPtr->parentPtr) == None) {
	    Tk_MakeWindowExist((Tk_Window)winPtr->parentPtr);
	}
    }
#endif

    /* Create a transparent window and put it on top.  */

#ifdef WIN32
    hParent = (HWND) parent;
    style = (WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
    exStyle = (WS_EX_TRANSPARENT | WS_EX_TOPMOST);
#define TK_WIN_CHILD_CLASS_NAME "TkChild"
    hWnd = CreateWindowEx(exStyle, TK_WIN_CHILD_CLASS_NAME, NULL, style,
	Tk_X(tkwin), Tk_Y(tkwin), Tk_Width(tkwin), Tk_Height(tkwin),
	hParent, NULL, (HINSTANCE) Tk_GetHINSTANCE(), NULL);
    winPtr->window = Tk_AttachHWND(tkwin, hWnd);
#else
    mask = (!isBusy) ? 0 : (CWDontPropagate | CWEventMask);
    /* Ignore the important events while the window is mapped.  */
#define USER_EVENTS  (EnterWindowMask | LeaveWindowMask | KeyPressMask | \
	KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask)
#define PROP_EVENTS  (KeyPressMask | KeyReleaseMask | ButtonPressMask | \
	ButtonReleaseMask | PointerMotionMask)

    winPtr->atts.do_not_propagate_mask = PROP_EVENTS;
    winPtr->atts.event_mask = USER_EVENTS;
    winPtr->changes.border_width = 0;
    winPtr->depth = 0; 

    winPtr->window = XCreateWindow(winPtr->display, parent,
	winPtr->changes.x, winPtr->changes.y,
	(unsigned)winPtr->changes.width,	/* width */
	(unsigned)winPtr->changes.height,	/* height */
	(unsigned)winPtr->changes.border_width,	/* border_width */
	winPtr->depth,		/* depth */
	InputOnly,		/* class */
	winPtr->visual,		/* visual */
        mask,			/* valuemask */
	&(winPtr->atts)		/* attributes */ );
#endif /* WIN32 */

    dispPtr = winPtr->dispPtr;
    hPtr = Tcl_CreateHashEntry(&(dispPtr->winTable), (char *)winPtr->window,
	&notUsed);
    Tcl_SetHashValue(hPtr, winPtr);
    winPtr->dirtyAtts = 0;
    winPtr->dirtyChanges = 0;
#ifdef TK_USE_INPUT_METHODS
    winPtr->inputContext = NULL;
#endif /* TK_USE_INPUT_METHODS */
    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)) {
		XWindowChanges changes;
		changes.sibling = winPtr2->window;
		changes.stack_mode = Below;
		XConfigureWindow(winPtr->display, winPtr->window,
		    CWSibling | CWStackMode, &changes);
		break;
	    }
	}
    }

    /*
     * Issue a ConfigureNotify event if there were deferred configuration
     * changes (but skip it if the window is being deleted;  the
     * ConfigureNotify event could cause problems if we're being called
     * from Tk_DestroyWindow under some conditions).
     */
    if ((winPtr->flags & TK_NEED_CONFIG_NOTIFY)
	&& !(winPtr->flags & TK_ALREADY_DEAD)) {
	winPtr->flags &= ~TK_NEED_CONFIG_NOTIFY;
	DoConfigureNotify((Tk_FakeWin *) tkwin);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Blt_FindChild --
 *
 *      Performs a linear search for the named child window in a given
 *	parent window.
 *
 *	This can be done via Tcl, but not through Tk's C API.  It's 
 *	simple enough, if you peek into the Tk_Window structure.
 *
 * Results:
 *      The child Tk_Window. If the named child can't be found, NULL
 *	is returned.
 *
 *----------------------------------------------------------------------
 */

/*LINTLIBRARY*/
Tk_Window
Blt_FindChild(parent, name)
    Tk_Window parent;
    char *name;
{
    register TkWindow *winPtr;
    TkWindow *parentPtr = (TkWindow *)parent;

    for (winPtr = parentPtr->childList; winPtr != NULL; 
	winPtr = winPtr->nextPtr) {
	if (strcmp(name, winPtr->nameUid) == 0) {
	    return (Tk_Window)winPtr;
	}
    }
    return NULL;
}

/*
 *----------------------------------------------------------------------
 *
 * Blt_FirstChildWindow --
 *
 *      Performs a linear search for the named child window in a given
 *	parent window.
 *
 *	This can be done via Tcl, but not through Tk's C API.  It's 
 *	simple enough, if you peek into the Tk_Window structure.
 *
 * Results:
 *      The child Tk_Window. If the named child can't be found, NULL
 *	is returned.
 *
 *----------------------------------------------------------------------
 */
/*LINTLIBRARY*/
Tk_Window
Blt_FirstChild(parent)
    Tk_Window parent;
{
    TkWindow *parentPtr = (TkWindow *)parent;
    return (Tk_Window)parentPtr->childList;
}

/*
 *----------------------------------------------------------------------
 *
 * Blt_FindChild --
 *
 *      Performs a linear search for the named child window in a given
 *	parent window.
 *
 *	This can be done via Tcl, but not through Tk's C API.  It's 
 *	simple enough, if you peek into the Tk_Window structure.
 *
 * Results:
 *      The child Tk_Window. If the named child can't be found, NULL
 *	is returned.
 *
 *----------------------------------------------------------------------
 */

/*LINTLIBRARY*/
Tk_Window
Blt_NextChild(tkwin)
    Tk_Window tkwin;
{
    TkWindow *winPtr = (TkWindow *)tkwin;

    if (winPtr == NULL) {
	return NULL;
    }
    return (Tk_Window)winPtr->nextPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * 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;

    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;
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Blt_RelinkWindow --
 *
 *	Relinks a window into a new parent.  The window is unlinked
 *	from its original parent's child list and added onto the end
 *	of the new parent's list.
 *
 *	FIXME:  If the window has focus, the focus should be moved
 *		to an ancestor.  Otherwise, Tk becomes confused 
 *		about which Toplevel turns on focus for the window. 
 *		Right now this is done at the Tcl layer.  For example,
 *		see blt::CreateTearoff in tabset.tcl.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The window is unlinked from its childList.
 *
 *----------------------------------------------------------------------
 */
void
Blt_RelinkWindow(tkwin, newParent, x, y)
    Tk_Window tkwin;		/* Child window to be linked. */
    Tk_Window newParent;
    int x, y;
{
    TkWindow *winPtr, *parentWinPtr;

    if (Blt_ReparentWindow(Tk_Display(tkwin), Tk_WindowId(tkwin),
	    Tk_WindowId(newParent), x, y) != TCL_OK) {
	return;
    }
    winPtr = (TkWindow *)tkwin;
    parentWinPtr = (TkWindow *)newParent;

    winPtr->flags &= ~TK_REPARENTED;
    UnlinkWindow(winPtr);	/* Remove the window from its parent's list */

    /* Append the window onto the end of the parent's list of children */
    winPtr->parentPtr = parentWinPtr;
    winPtr->nextPtr = NULL;
    if (parentWinPtr->childList == NULL) {
	parentWinPtr->childList = winPtr;
    } else {
	parentWinPtr->lastChildPtr->nextPtr = winPtr;
    }
    parentWinPtr->lastChildPtr = winPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * Blt_RelinkWindow --
 *
 *	Relinks a window into a new parent.  The window is unlinked
 *	from its original parent's child list and added onto the end
 *	of the new parent's list.
 *
 *	FIXME:  If the window has focus, the focus should be moved
 *		to an ancestor.  Otherwise, Tk becomes confused 
 *		about which Toplevel turns on focus for the window. 
 *		Right now this is done at the Tcl layer.  For example,
 *		see blt::CreateTearoff in tabset.tcl.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The window is unlinked from its childList.
 *
 *----------------------------------------------------------------------
 */
void
Blt_RelinkWindow2(tkwin, window, newParent, x, y)
    Tk_Window tkwin;		/* Child window to be linked. */
    Window window;
    Tk_Window newParent;
    int x, y;
{
#ifdef notdef
    TkWindow *winPtr, *parentWinPtr;
#endif
    if (Blt_ReparentWindow(Tk_Display(tkwin), window,
	    Tk_WindowId(newParent), x, y) != TCL_OK) {
	return;
    }
#ifdef notdef
    winPtr = (TkWindow *)tkwin;
    parentWinPtr = (TkWindow *)newParent;

    winPtr->flags &= ~TK_REPARENTED;
    UnlinkWindow(winPtr);	/* Remove the window from its parent's list */

    /* Append the window onto the end of the parent's list of children */
    winPtr->parentPtr = parentWinPtr;
    winPtr->nextPtr = NULL;
    if (parentWinPtr->childList == NULL) {
	parentWinPtr->childList = winPtr;
    } else {
	parentWinPtr->lastChildPtr->nextPtr = winPtr;
    }
    parentWinPtr->lastChildPtr = winPtr;
#endif
}

void
Blt_UnlinkWindow(tkwin)
    Tk_Window tkwin;		/* Child window to be linked. */
{
    TkWindow *winPtr;
    Window root;

    root = XRootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
    if (Blt_ReparentWindow(Tk_Display(tkwin), Tk_WindowId(tkwin),
	    root, 0, 0) != TCL_OK) {
	return;
    }
    winPtr = (TkWindow *)tkwin;
    winPtr->flags &= ~TK_REPARENTED;
#ifdef notdef
    UnlinkWindow(winPtr);	/* Remove the window from its parent's list */
#endif
}

/*
 *----------------------------------------------------------------------
 *
 * Blt_Toplevel --
 *
 *      Climbs up the widget hierarchy to find the top level window of
 *      the window given.
 *
 * Results:
 *      Returns the Tk_Window of the toplevel widget.
 *
 *----------------------------------------------------------------------
 */
Tk_Window
Blt_Toplevel(tkwin)
    register Tk_Window tkwin;
{
    while (!Tk_IsTopLevel(tkwin)) {
	tkwin = Tk_Parent(tkwin);
    }
    return tkwin;
}

void
Blt_RootCoordinates(tkwin, x, y, rootXPtr, rootYPtr)
    Tk_Window tkwin;
    int x, y;
    int *rootXPtr, *rootYPtr;
{
    int vx, vy, vw, vh;
    int rootX, rootY;

    Tk_GetRootCoords(tkwin, &rootX, &rootY);
    x += rootX;
    y += rootY;
    Tk_GetVRootGeometry(tkwin, &vx, &vy, &vw, &vh);
    x += vx;
    y += vy;
    *rootXPtr = x;
    *rootYPtr = y;
}


/* Find the toplevel then  */
int
Blt_RootX(tkwin)
    Tk_Window tkwin;
{
    int x;
    
    for (x = 0; tkwin != NULL;  tkwin = Tk_Parent(tkwin)) {
	x += Tk_X(tkwin) + Tk_Changes(tkwin)->border_width;
	if (Tk_IsTopLevel(tkwin)) {
	    break;
	}
    }
    return x;
}

int
Blt_RootY(tkwin)
    Tk_Window tkwin;
{
    int y;
    
    for (y = 0; tkwin != NULL;  tkwin = Tk_Parent(tkwin)) {
	y += Tk_Y(tkwin) + Tk_Changes(tkwin)->border_width;
	if (Tk_IsTopLevel(tkwin)) {
	    break;
	}
    }
    return y;
}

#ifdef WIN32
/*
 *----------------------------------------------------------------------
 *
 * Blt_GetRealWindowId --
 *
 *      Returns the XID for the Tk_Window given.  Starting in Tk 8.0,
 *      the toplevel widgets are wrapped by another window.
 *      Currently there's no way to get at that window, other than
 *      what is done here: query the X window hierarchy and grab the
 *      parent.
 *
 * Results:
 *      Returns the X Window ID of the widget.  If it's a toplevel, then
 *	the XID of the wrapper is returned.
 *
 *----------------------------------------------------------------------
 */
Window
Blt_GetRealWindowId(Tk_Window tkwin)
{
    return (Window) GetWindowHandle(tkwin);
}

/*
 *----------------------------------------------------------------------
 *
 * Blt_GetToplevel --
 *
 *      Retrieves the toplevel window which is the nearest ancestor of
 *      of the specified window.
 *
 * Results:
 *      Returns the toplevel window or NULL if the window has no
 *      ancestor which is a toplevel.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */
Tk_Window
Blt_GetToplevel(Tk_Window tkwin) /* Window for which the toplevel
				  * should be deterined. */
{
     while (!Tk_IsTopLevel(tkwin)) {
         tkwin = Tk_Parent(tkwin);
	 if (tkwin == NULL) {
             return NULL;
         }
     }
     return tkwin;
}

/*
 *----------------------------------------------------------------------
 *
 * Blt_RaiseToLevelWindow --
 *
 * Results:
 *	None.
 *
 *----------------------------------------------------------------------
 */
void
Blt_RaiseToplevel(Tk_Window tkwin)
{
    SetWindowPos(GetWindowHandle(tkwin), HWND_TOP, 0, 0, 0, 0,
	SWP_NOMOVE | SWP_NOSIZE);
}

/*
 *----------------------------------------------------------------------
 *
 * Blt_MapToplevel --
 *
 * Results:
 *	None.
 *
 *----------------------------------------------------------------------
 */
void
Blt_MapToplevel(Tk_Window tkwin)
{
    ShowWindow(GetWindowHandle(tkwin), SW_SHOWNORMAL);
}

/*
 *----------------------------------------------------------------------
 *
 * Blt_UnmapToplevel --
 *
 * Results:
 *	None.
 *
 *----------------------------------------------------------------------
 */
void
Blt_UnmapToplevel(Tk_Window tkwin)
{
    ShowWindow(GetWindowHandle(tkwin), SW_HIDE);
}

/*
 *----------------------------------------------------------------------
 *
 * Blt_MoveResizeToplevel --
 *
 * Results:
 *	None.
 *
 *----------------------------------------------------------------------
 */
void
Blt_MoveResizeToplevel(tkwin, x, y, width, height)
    Tk_Window tkwin;
    int x, y, width, height;
{
    SetWindowPos(GetWindowHandle(tkwin), HWND_TOP, x, y, width, height, 0);
}

int
Blt_ReparentWindow(
    Display *display,
    Window window, 
    Window newParent,
    int x, int y)
{
    XReparentWindow(display, window, newParent, x, y);
    return TCL_OK;
}

#else  /* WIN32 */

/*
 *----------------------------------------------------------------------
 *
 * Blt_GetRealWindowId --
 *
 *      Returns the XID for the Tk_Window given.  Starting in Tk 8.0,
 *      the toplevel widgets are wrapped by another window.
 *      Currently there's no way to get at that window, other than
 *      what is done here: query the X window hierarchy and grab the
 *      parent.
 *
 * Results:
 *      Returns the X Window ID of the widget.  If it's a toplevel, then
 *	the XID of the wrapper is returned.
 *
 *----------------------------------------------------------------------
 */
Window
Blt_GetRealWindowId(tkwin)
    Tk_Window tkwin;
{
    return GetWindowId(tkwin);
}

/*
 *----------------------------------------------------------------------
 *
 * Blt_RaiseToplevel --
 *
 * Results:
 *	None.
 *
 *----------------------------------------------------------------------
 */
void
Blt_RaiseToplevel(tkwin)
    Tk_Window tkwin;
{
    XRaiseWindow(Tk_Display(tkwin), GetWindowId(tkwin));
}

/*
 *----------------------------------------------------------------------
 *
 * Blt_LowerToplevel --
 *
 * Results:
 *	None.
 *
 *----------------------------------------------------------------------
 */
void
Blt_LowerToplevel(tkwin)
    Tk_Window tkwin;
{
    XLowerWindow(Tk_Display(tkwin), GetWindowId(tkwin));
}

/*
 *----------------------------------------------------------------------
 *
 * Blt_ResizeToplevel --
 *
 * Results:
 *	None.
 *
 *----------------------------------------------------------------------
 */
void
Blt_ResizeToplevel(tkwin, width, height)
    Tk_Window tkwin;
    int width, height;
{
    XResizeWindow(Tk_Display(tkwin), GetWindowId(tkwin), width, height);
}

/*
 *----------------------------------------------------------------------
 *
 * Blt_MoveResizeToplevel --
 *
 * Results:
 *	None.
 *
 *----------------------------------------------------------------------
 */
void
Blt_MoveResizeToplevel(tkwin, x, y, width, height)
    Tk_Window tkwin;
    int x, y, width, height;
{
    XMoveResizeWindow(Tk_Display(tkwin), GetWindowId(tkwin), x, y, 
	      width, height);
}

/*
 *----------------------------------------------------------------------
 *
 * Blt_ResizeToplevel --
 *
 * Results:
 *	None.
 *
 *----------------------------------------------------------------------
 */
void
Blt_MoveToplevel(tkwin, x, y)
    Tk_Window tkwin;
    int x, y;
{
    XMoveWindow(Tk_Display(tkwin), GetWindowId(tkwin), x, y);
}

/*
 *----------------------------------------------------------------------
 *
 * Blt_MapToplevel --
 *
 * Results:
 *	None.
 *
 *----------------------------------------------------------------------
 */
void
Blt_MapToplevel(tkwin)
    Tk_Window tkwin;
{
    XMapWindow(Tk_Display(tkwin), GetWindowId(tkwin));
}

/*
 *----------------------------------------------------------------------
 *
 * Blt_UnmapToplevel --
 *
 * Results:
 *	None.
 *
 *----------------------------------------------------------------------
 */
void
Blt_UnmapToplevel(tkwin)
    Tk_Window tkwin;
{
    XUnmapWindow(Tk_Display(tkwin), GetWindowId(tkwin));
}

/* ARGSUSED */
static int
XReparentWindowErrorProc(clientData, errEventPtr)
    ClientData clientData;
    XErrorEvent *errEventPtr;
{
    int *errorPtr = clientData;

    *errorPtr = TCL_ERROR;
    return 0;
}

int
Blt_ReparentWindow(display, window, newParent, x, y)
    Display *display;
    Window window, newParent;
    int x, y;
{
    Tk_ErrorHandler handler;
    int result;
    int any = -1;

    result = TCL_OK;
    handler = Tk_CreateErrorHandler(display, any, X_ReparentWindow, any,
	XReparentWindowErrorProc, &result);
    XReparentWindow(display, window, newParent, x, y);
    Tk_DeleteErrorHandler(handler);
    XSync(display, False);
    return result;
}

#endif /* WIN32 */

#if (TK_MAJOR_VERSION == 4)
#include <bltHash.h>
static int initialized = FALSE;
static Blt_HashTable windowTable;

void
Blt_SetWindowInstanceData(tkwin, instanceData)
    Tk_Window tkwin;
    ClientData instanceData;
{
    Blt_HashEntry *hPtr;
    int isNew;

    if (!initialized) {
	Blt_InitHashTable(&windowTable, BLT_ONE_WORD_KEYS);
	initialized = TRUE;
    }
    hPtr = Blt_CreateHashEntry(&windowTable, (char *)tkwin, &isNew);
    assert(isNew);
    Blt_SetHashValue(hPtr, instanceData);
}

ClientData
Blt_GetWindowInstanceData(tkwin)
    Tk_Window tkwin;
{
    Blt_HashEntry *hPtr;

    hPtr = Blt_FindHashEntry(&windowTable, (char *)tkwin);
    if (hPtr == NULL) {
	return NULL;
    }
    return Blt_GetHashValue(hPtr);
}

void
Blt_DeleteWindowInstanceData(tkwin)
    Tk_Window tkwin;
{
    Blt_HashEntry *hPtr;

    hPtr = Blt_FindHashEntry(&windowTable, (char *)tkwin);
    assert(hPtr);
    Blt_DeleteHashEntry(&windowTable, hPtr);
}

#else

void
Blt_SetWindowInstanceData(tkwin, instanceData)
    Tk_Window tkwin;
    ClientData instanceData;
{
    TkWindow *winPtr = (TkWindow *)tkwin;

    winPtr->instanceData = instanceData;
}

ClientData
Blt_GetWindowInstanceData(tkwin)
    Tk_Window tkwin;
{
    TkWindow *winPtr = (TkWindow *)tkwin;

    return winPtr->instanceData;
}

void
Blt_DeleteWindowInstanceData(tkwin)
    Tk_Window tkwin;
{
}

#endif

