1/*
2 * tkWinWm.c --
3 *
4 *	This module takes care of the interactions between a Tk-based
5 *	application and the window manager.  Among other things, it
6 *	implements the "wm" command and passes geometry information
7 *	to the window manager.
8 *
9 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
10 * Copyright (c) 1998-2000 by Scriptics Corporation.
11 *
12 * See the file "license.terms" for information on usage and redistribution
13 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14 *
15 * RCS: @(#) $Id: tkWinWm.c,v 1.54.2.28 2007/12/05 19:18:09 hobbs Exp $
16 */
17
18#include "tkWinInt.h"
19#include <shellapi.h>
20
21/*
22 * These next two defines are only valid on Win2K/XP+.
23 */
24#ifndef WS_EX_LAYERED
25#define WS_EX_LAYERED	0x00080000
26#endif
27#ifndef LWA_COLORKEY
28#define LWA_COLORKEY	0x00000001
29#endif
30#ifndef LWA_ALPHA
31#define LWA_ALPHA	0x00000002
32#endif
33
34/*
35 * Event structure for synthetic activation events.  These events are
36 * placed on the event queue whenever a toplevel gets a WM_MOUSEACTIVATE
37 * message.
38 */
39
40typedef struct ActivateEvent {
41    Tcl_Event ev;
42    TkWindow *winPtr;
43} ActivateEvent;
44
45/*
46 * A data structure of the following type holds information for
47 * each window manager protocol (such as WM_DELETE_WINDOW) for
48 * which a handler (i.e. a Tcl command) has been defined for a
49 * particular top-level window.
50 */
51
52typedef struct ProtocolHandler {
53    Atom protocol;		/* Identifies the protocol. */
54    struct ProtocolHandler *nextPtr;
55				/* Next in list of protocol handlers for
56				 * the same top-level window, or NULL for
57				 * end of list. */
58    Tcl_Interp *interp;		/* Interpreter in which to invoke command. */
59    char command[4];		/* Tcl command to invoke when a client
60				 * message for this protocol arrives.
61				 * The actual size of the structure varies
62				 * to accommodate the needs of the actual
63				 * command. THIS MUST BE THE LAST FIELD OF
64				 * THE STRUCTURE. */
65} ProtocolHandler;
66
67#define HANDLER_SIZE(cmdLength) \
68    ((unsigned) (sizeof(ProtocolHandler) - 3 + cmdLength))
69
70/*
71 * Helper type passed via lParam to TkWmStackorderToplevelEnumProc
72 */
73typedef struct TkWmStackorderToplevelPair {
74    Tcl_HashTable *table;
75    TkWindow **window_ptr;
76} TkWmStackorderToplevelPair;
77
78/*
79 * This structure represents the contents of a icon, in terms of its
80 * image.  The HICON is an internal Windows format.  Most of these
81 * icon-specific-structures originated with the Winico extension.
82 * We stripped out unused parts of that code, and integrated the
83 * code more naturally with Tcl.
84 */
85typedef struct {
86	UINT			Width, Height, Colors; /*  Width, Height and bpp */
87	LPBYTE			lpBits;                /*  ptr to DIB bits */
88	DWORD			dwNumBytes;            /*  how many bytes? */
89	LPBITMAPINFO	lpbi;                          /*  ptr to header */
90	LPBYTE			lpXOR;                 /*  ptr to XOR image bits */
91	LPBYTE			lpAND;                 /*  ptr to AND image bits */
92	HICON			hIcon;                 /*  DAS ICON */
93} ICONIMAGE, *LPICONIMAGE;
94/*
95 * This structure is how we represent a block of the above
96 * items.  We will reallocate these structures according to
97 * how many images they need to contain.
98 */
99typedef struct {
100	int		nNumImages;                      /*  How many images? */
101	ICONIMAGE	IconImages[1];                   /*  Image entries */
102} BlockOfIconImages, *BlockOfIconImagesPtr;
103/*
104 * These two structures are used to read in icons from an
105 * 'icon directory' (i.e. the contents of a .icr file, say).
106 * We only use these structures temporarily, since we copy
107 * the information we want into a BlockOfIconImages.
108 */
109typedef struct {
110	BYTE	bWidth;               /*  Width of the image */
111	BYTE	bHeight;              /*  Height of the image (times 2) */
112	BYTE	bColorCount;          /*  Number of colors in image (0 if >=8bpp) */
113	BYTE	bReserved;            /*  Reserved */
114	WORD	wPlanes;              /*  Color Planes */
115	WORD	wBitCount;            /*  Bits per pixel */
116	DWORD	dwBytesInRes;         /*  how many bytes in this resource? */
117	DWORD	dwImageOffset;        /*  where in the file is this image */
118} ICONDIRENTRY, *LPICONDIRENTRY;
119typedef struct {
120	WORD		idReserved;   /*  Reserved */
121	WORD		idType;       /*  resource type (1 for icons) */
122	WORD		idCount;      /*  how many images? */
123	ICONDIRENTRY	idEntries[1]; /*  the entries for each image */
124} ICONDIR, *LPICONDIR;
125
126/*
127 * A pointer to one of these strucutures is associated with each
128 * toplevel.  This allows us to free up all memory associated with icon
129 * resources when a window is deleted or if the window's icon is
130 * changed.  They are simply reference counted according to:
131 *
132 * (i) how many WmInfo structures point to this object
133 * (ii) whether the ThreadSpecificData defined in this file contains
134 * a pointer to this object.
135 *
136 * The former count is for windows whose icons are individually
137 * set, and the latter is for the global default icon choice.
138 *
139 * Icons loaded from .icr/.icr use the iconBlock field, icons
140 * loaded from .exe/.dll use the hIcon field.
141 */
142typedef struct WinIconInstance {
143    int refCount;		 /* Number of instances that share this
144				  * data structure. */
145    BlockOfIconImagesPtr iconBlock;
146                                 /* Pointer to icon resource data for
147                                  * image. */
148} WinIconInstance;
149
150typedef struct WinIconInstance *WinIconPtr;
151
152/*
153 * A data structure of the following type holds window-manager-related
154 * information for each top-level window in an application.
155 */
156
157typedef struct TkWmInfo {
158    TkWindow *winPtr;		/* Pointer to main Tk information for
159				 * this window. */
160    HWND wrapper;		/* This is the decorative frame window
161				 * created by the window manager to wrap
162				 * a toplevel window.  This window is
163				 * a direct child of the root window. */
164    char *title;		/* Title to display in window caption.  If
165				 * NULL, use name of widget.  Malloced. */
166    char *iconName;		/* Name to display in icon.  Malloced. */
167    XWMHints hints;		/* Various pieces of information for
168				 * window manager. */
169    char *leaderName;		/* Path name of leader of window group
170				 * (corresponds to hints.window_group).
171				 * Malloc-ed.  Note:  this field doesn't
172				 * get updated if leader is destroyed. */
173    TkWindow *masterPtr;	/* Master window for TRANSIENT_FOR property,
174				 * or NULL. */
175    Tk_Window icon;		/* Window to use as icon for this window,
176				 * or NULL. */
177    Tk_Window iconFor;		/* Window for which this window is icon, or
178				 * NULL if this isn't an icon for anyone. */
179
180    /*
181     * Information used to construct an XSizeHints structure for
182     * the window manager:
183     */
184
185    int defMinWidth, defMinHeight, defMaxWidth, defMaxHeight;
186				/* Default resize limits given by system. */
187    int sizeHintsFlags;		/* Flags word for XSizeHints structure.
188				 * If the PBaseSize flag is set then the
189				 * window is gridded;  otherwise it isn't
190				 * gridded. */
191    int minWidth, minHeight;	/* Minimum dimensions of window, in
192				 * pixels or grid units. */
193    int maxWidth, maxHeight;	/* Maximum dimensions of window, in
194				 * pixels or grid units. 0 to default. */
195    Tk_Window gridWin;		/* Identifies the window that controls
196				 * gridding for this top-level, or NULL if
197				 * the top-level isn't currently gridded. */
198    int widthInc, heightInc;	/* Increments for size changes (# pixels
199				 * per step). */
200    struct {
201	int x;	/* numerator */
202	int y;  /* denominator */
203    } minAspect, maxAspect;	/* Min/max aspect ratios for window. */
204    int reqGridWidth, reqGridHeight;
205				/* The dimensions of the window (in
206				 * grid units) requested through
207				 * the geometry manager. */
208    int gravity;		/* Desired window gravity. */
209
210    /*
211     * Information used to manage the size and location of a window.
212     */
213
214    int width, height;		/* Desired dimensions of window, specified
215				 * in pixels or grid units.  These values are
216				 * set by the "wm geometry" command and by
217				 * ConfigureNotify events (for when wm
218				 * resizes window).  -1 means user hasn't
219				 * requested dimensions. */
220    int x, y;			/* Desired X and Y coordinates for window.
221				 * These values are set by "wm geometry",
222				 * plus by ConfigureNotify events (when wm
223				 * moves window).  These numbers are
224				 * different than the numbers stored in
225				 * winPtr->changes because (a) they could be
226				 * measured from the right or bottom edge
227				 * of the screen (see WM_NEGATIVE_X and
228				 * WM_NEGATIVE_Y flags) and (b) if the window
229				 * has been reparented then they refer to the
230				 * parent rather than the window itself. */
231    int borderWidth, borderHeight;
232				/* Width and height of window dressing, in
233				 * pixels for the current style/exStyle.  This
234				 * includes the border on both sides of the
235				 * window. */
236    int configWidth, configHeight;
237				/* Dimensions passed to last request that we
238				 * issued to change geometry of window.  Used
239				 * to eliminate redundant resize operations. */
240    HMENU hMenu;		/* the hMenu associated with this menu */
241    DWORD style, exStyle;	/* Style flags for the wrapper window. */
242    LONG styleConfig;		/* Extra user requested style bits */
243    LONG exStyleConfig;		/* Extra user requested extended style bits */
244    Tcl_Obj *crefObj;		/* COLORREF object for transparent handling */
245    COLORREF colorref;		/* COLORREF for transparent handling */
246    double alpha;		/* Alpha transparency level
247				 * 0.0 (fully transparent) .. 1.0 (opaque) */
248
249    /*
250     * List of children of the toplevel which have private colormaps.
251     */
252
253    TkWindow **cmapList;	/* Array of window with private colormaps. */
254    int cmapCount;		/* Number of windows in array. */
255
256    /*
257     * Miscellaneous information.
258     */
259
260    ProtocolHandler *protPtr;	/* First in list of protocol handlers for
261				 * this window (NULL means none). */
262    int cmdArgc;		/* Number of elements in cmdArgv below. */
263    CONST char **cmdArgv;	/* Array of strings to store in the
264				 * WM_COMMAND property.  NULL means nothing
265				 * available. */
266    char *clientMachine;	/* String to store in WM_CLIENT_MACHINE
267				 * property, or NULL. */
268    int flags;			/* Miscellaneous flags, defined below. */
269    int numTransients;		/* number of transients on this window */
270    WinIconPtr iconPtr;         /* pointer to titlebar icon structure for
271                                 * this window, or NULL. */
272    struct TkWmInfo *nextPtr;	/* Next in list of all top-level windows. */
273} WmInfo;
274
275/*
276 * Flag values for WmInfo structures:
277 *
278 * WM_NEVER_MAPPED -		non-zero means window has never been
279 *				mapped;  need to update all info when
280 *				window is first mapped.
281 * WM_UPDATE_PENDING -		non-zero means a call to UpdateGeometryInfo
282 *				has already been scheduled for this
283 *				window;  no need to schedule another one.
284 * WM_NEGATIVE_X -		non-zero means x-coordinate is measured in
285 *				pixels from right edge of screen, rather
286 *				than from left edge.
287 * WM_NEGATIVE_Y -		non-zero means y-coordinate is measured in
288 *				pixels up from bottom of screen, rather than
289 *				down from top.
290 * WM_UPDATE_SIZE_HINTS -	non-zero means that new size hints need to be
291 *				propagated to window manager. Not used on Win.
292 * WM_SYNC_PENDING -		set to non-zero while waiting for the window
293 *				manager to respond to some state change.
294 * WM_MOVE_PENDING -		non-zero means the application has requested
295 *				a new position for the window, but it hasn't
296 *				been reflected through the window manager
297 *				yet.
298 * WM_COLORMAPS_EXPLICIT -	non-zero means the colormap windows were
299 *				set explicitly via "wm colormapwindows".
300 * WM_ADDED_TOPLEVEL_COLORMAP - non-zero means that when "wm colormapwindows"
301 *				was called the top-level itself wasn't
302 *				specified, so we added it implicitly at
303 *				the end of the list.
304 * WM_WIDTH_NOT_RESIZABLE -	non-zero means that we're not supposed to
305 *				allow the user to change the width of the
306 *				window (controlled by "wm resizable"
307 *				command).
308 * WM_HEIGHT_NOT_RESIZABLE -	non-zero means that we're not supposed to
309 *				allow the user to change the height of the
310 *				window (controlled by "wm resizable"
311 *				command).
312 * WM_WITHDRAWN -		non-zero means that this window has explicitly
313 *				been withdrawn. If it's a transient, it should
314 *				not mirror state changes in the master.
315 */
316
317#define WM_NEVER_MAPPED			(1<<0)
318#define WM_UPDATE_PENDING		(1<<1)
319#define WM_NEGATIVE_X			(1<<2)
320#define WM_NEGATIVE_Y			(1<<3)
321#define WM_UPDATE_SIZE_HINTS		(1<<4)
322#define WM_SYNC_PENDING			(1<<5)
323#define WM_CREATE_PENDING		(1<<6)
324#define WM_MOVE_PENDING			(1<<7)
325#define WM_COLORMAPS_EXPLICIT		(1<<8)
326#define WM_ADDED_TOPLEVEL_COLORMAP	(1<<9)
327#define WM_WIDTH_NOT_RESIZABLE		(1<<10)
328#define WM_HEIGHT_NOT_RESIZABLE		(1<<11)
329#define WM_WITHDRAWN			(1<<12)
330
331/*
332 * Window styles for various types of toplevel windows.
333 */
334
335#define WM_OVERRIDE_STYLE (WS_CLIPCHILDREN|WS_CLIPSIBLINGS|CS_DBLCLKS)
336#define EX_OVERRIDE_STYLE (WS_EX_TOOLWINDOW)
337
338#define WM_TOPLEVEL_STYLE (WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|CS_DBLCLKS)
339#define EX_TOPLEVEL_STYLE (0)
340
341#define WM_TRANSIENT_STYLE \
342		(WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_CLIPSIBLINGS|CS_DBLCLKS)
343#define EX_TRANSIENT_STYLE (WS_EX_DLGMODALFRAME)
344
345/*
346 * The following structure is the official type record for geometry
347 * management of top-level windows.
348 */
349
350static void		TopLevelReqProc(ClientData dummy, Tk_Window tkwin);
351
352static Tk_GeomMgr wmMgrType = {
353    "wm",				/* name */
354    TopLevelReqProc,			/* requestProc */
355    (Tk_GeomLostSlaveProc *) NULL,	/* lostSlaveProc */
356};
357
358typedef struct ThreadSpecificData {
359    HPALETTE systemPalette;      /* System palette; refers to the
360				  * currently installed foreground logical
361				  * palette. */
362    TkWindow *createWindow;      /* Window that is being constructed.  This
363				  * value is set immediately before a
364				  * call to CreateWindowEx, and is used
365				  * by SetLimits.  This is a gross hack
366				  * needed to work around Windows brain
367				  * damage where it sends the
368				  * WM_GETMINMAXINFO message before the
369				  * WM_CREATE window. */
370    int initialized;             /* Flag indicating whether thread-
371				  * specific elements of module have
372				  * been initialized. */
373    int firstWindow;             /* Flag, cleared when the first window
374				  * is mapped in a non-iconic state. */
375    WinIconPtr iconPtr;          /* IconPtr being used as default for all
376                                  * toplevels, or NULL. */
377} ThreadSpecificData;
378static Tcl_ThreadDataKey dataKey;
379
380/*
381 * The following variables cannot be placed in thread local storage
382 * because they must be shared across threads.
383 */
384
385static int initialized;        /* Flag indicating whether module has
386				* been initialized. */
387
388/*
389 * A pointer to a shell proc which allows us to extract icons from
390 * any file.  We just initialize this when we start up (if we can)
391 * and then it never changes
392 */
393DWORD* (WINAPI *shgetfileinfoProc) (LPCTSTR pszPath, DWORD dwFileAttributes,
394    SHFILEINFO* psfi, UINT cbFileInfo, UINT uFlags) = NULL;
395
396/*
397 * A pointer to SetLayeredWindowAttributes (user32.dll) which we
398 * retrieve dynamically because it is only valid on Win2K+.
399 */
400BOOL (WINAPI *setLayeredWindowAttributesProc) (HWND hwnd, COLORREF crKey,
401	BYTE bAlpha, DWORD dwFlags) = NULL;
402
403TCL_DECLARE_MUTEX(winWmMutex)
404
405/*
406 * Forward declarations for procedures defined in this file:
407 */
408
409static int		ActivateWindow _ANSI_ARGS_((Tcl_Event *evPtr,
410			    int flags));
411static void		ConfigureTopLevel _ANSI_ARGS_((WINDOWPOS *pos));
412static void		GenerateConfigureNotify _ANSI_ARGS_((
413			    TkWindow *winPtr));
414static void		GetMaxSize _ANSI_ARGS_((WmInfo *wmPtr,
415			    int *maxWidthPtr, int *maxHeightPtr));
416static void		GetMinSize _ANSI_ARGS_((WmInfo *wmPtr,
417			    int *minWidthPtr, int *minHeightPtr));
418static TkWindow *	GetTopLevel _ANSI_ARGS_((HWND hwnd));
419static void		InitWm _ANSI_ARGS_((void));
420static int		InstallColormaps _ANSI_ARGS_((HWND hwnd, int message,
421			    int isForemost));
422static void		InvalidateSubTree _ANSI_ARGS_((TkWindow *winPtr,
423			    Colormap colormap));
424static void		InvalidateSubTreeDepth _ANSI_ARGS_((TkWindow *winPtr));
425static int		ParseGeometry _ANSI_ARGS_((Tcl_Interp *interp,
426			    char *string, TkWindow *winPtr));
427static void		RefreshColormap _ANSI_ARGS_((Colormap colormap,
428	                    TkDisplay *dispPtr));
429static void		SetLimits _ANSI_ARGS_((HWND hwnd, MINMAXINFO *info));
430static void		TkWmStackorderToplevelWrapperMap _ANSI_ARGS_((
431			    TkWindow *winPtr,
432			    Display *display,
433			    Tcl_HashTable *table));
434static LRESULT CALLBACK	TopLevelProc _ANSI_ARGS_((HWND hwnd, UINT message,
435			    WPARAM wParam, LPARAM lParam));
436static void		TopLevelEventProc _ANSI_ARGS_((ClientData clientData,
437			    XEvent *eventPtr));
438static void		TopLevelReqProc _ANSI_ARGS_((ClientData dummy,
439			    Tk_Window tkwin));
440static void		UpdateGeometryInfo _ANSI_ARGS_((
441			    ClientData clientData));
442static void		UpdateWrapper _ANSI_ARGS_((TkWindow *winPtr));
443static LRESULT CALLBACK	WmProc _ANSI_ARGS_((HWND hwnd, UINT message,
444			    WPARAM wParam, LPARAM lParam));
445static void		WmWaitVisibilityOrMapProc _ANSI_ARGS_((
446			    ClientData clientData, XEvent *eventPtr));
447static BlockOfIconImagesPtr   ReadIconOrCursorFromFile _ANSI_ARGS_((
448			    Tcl_Interp *interp, Tcl_Obj* fileName, BOOL isIcon));
449static WinIconPtr       ReadIconFromFile _ANSI_ARGS_((
450			    Tcl_Interp *interp, Tcl_Obj *fileName));
451static WinIconPtr       GetIconFromPixmap _ANSI_ARGS_((Display *dsPtr,
452						       Pixmap pixmap));
453static int     		ReadICOHeader _ANSI_ARGS_((Tcl_Channel channel));
454static BOOL 		AdjustIconImagePointers _ANSI_ARGS_((LPICONIMAGE lpImage));
455static HICON 		MakeIconOrCursorFromResource
456                            _ANSI_ARGS_((LPICONIMAGE lpIcon, BOOL isIcon));
457static HICON 		GetIcon _ANSI_ARGS_((WinIconPtr titlebaricon,
458			    int icon_size));
459static int 		WinSetIcon _ANSI_ARGS_((Tcl_Interp *interp,
460			    WinIconPtr titlebaricon, Tk_Window tkw));
461static void 		FreeIconBlock _ANSI_ARGS_((BlockOfIconImagesPtr lpIR));
462static void  	 	DecrIconRefCount _ANSI_ARGS_((WinIconPtr titlebaricon));
463
464static int 		WmAspectCmd _ANSI_ARGS_((Tk_Window tkwin,
465			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
466			    Tcl_Obj *CONST objv[]));
467static int 		WmAttributesCmd _ANSI_ARGS_((Tk_Window tkwin,
468			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
469			    Tcl_Obj *CONST objv[]));
470static int 		WmClientCmd _ANSI_ARGS_((Tk_Window tkwin,
471			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
472			    Tcl_Obj *CONST objv[]));
473static int 		WmColormapwindowsCmd _ANSI_ARGS_((Tk_Window tkwin,
474			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
475			    Tcl_Obj *CONST objv[]));
476static int 		WmCommandCmd _ANSI_ARGS_((Tk_Window tkwin,
477			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
478			    Tcl_Obj *CONST objv[]));
479static int 		WmDeiconifyCmd _ANSI_ARGS_((Tk_Window tkwin,
480			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
481			    Tcl_Obj *CONST objv[]));
482static int 		WmFocusmodelCmd _ANSI_ARGS_((Tk_Window tkwin,
483			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
484			    Tcl_Obj *CONST objv[]));
485static int 		WmFrameCmd _ANSI_ARGS_((Tk_Window tkwin,
486			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
487			    Tcl_Obj *CONST objv[]));
488static int 		WmGeometryCmd _ANSI_ARGS_((Tk_Window tkwin,
489			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
490			    Tcl_Obj *CONST objv[]));
491static int 		WmGridCmd _ANSI_ARGS_((Tk_Window tkwin,
492			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
493			    Tcl_Obj *CONST objv[]));
494static int 		WmGroupCmd _ANSI_ARGS_((Tk_Window tkwin,
495			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
496			    Tcl_Obj *CONST objv[]));
497static int 		WmIconbitmapCmd _ANSI_ARGS_((Tk_Window tkwin,
498			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
499			    Tcl_Obj *CONST objv[]));
500static int 		WmIconifyCmd _ANSI_ARGS_((Tk_Window tkwin,
501			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
502			    Tcl_Obj *CONST objv[]));
503static int 		WmIconmaskCmd _ANSI_ARGS_((Tk_Window tkwin,
504			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
505			    Tcl_Obj *CONST objv[]));
506static int 		WmIconnameCmd _ANSI_ARGS_((Tk_Window tkwin,
507			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
508			    Tcl_Obj *CONST objv[]));
509static int 		WmIconphotoCmd _ANSI_ARGS_((Tk_Window tkwin,
510			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
511			    Tcl_Obj *CONST objv[]));
512static int 		WmIconpositionCmd _ANSI_ARGS_((Tk_Window tkwin,
513			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
514			    Tcl_Obj *CONST objv[]));
515static int 		WmIconwindowCmd _ANSI_ARGS_((Tk_Window tkwin,
516			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
517			    Tcl_Obj *CONST objv[]));
518static int 		WmMaxsizeCmd _ANSI_ARGS_((Tk_Window tkwin,
519			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
520			    Tcl_Obj *CONST objv[]));
521static int 		WmMinsizeCmd _ANSI_ARGS_((Tk_Window tkwin,
522			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
523			    Tcl_Obj *CONST objv[]));
524static int 		WmOverrideredirectCmd _ANSI_ARGS_((Tk_Window tkwin,
525			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
526			    Tcl_Obj *CONST objv[]));
527static int 		WmPositionfromCmd _ANSI_ARGS_((Tk_Window tkwin,
528			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
529			    Tcl_Obj *CONST objv[]));
530static int 		WmProtocolCmd _ANSI_ARGS_((Tk_Window tkwin,
531			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
532			    Tcl_Obj *CONST objv[]));
533static int 		WmResizableCmd _ANSI_ARGS_((Tk_Window tkwin,
534			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
535			    Tcl_Obj *CONST objv[]));
536static int 		WmSizefromCmd _ANSI_ARGS_((Tk_Window tkwin,
537			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
538			    Tcl_Obj *CONST objv[]));
539static int 		WmStackorderCmd _ANSI_ARGS_((Tk_Window tkwin,
540			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
541			    Tcl_Obj *CONST objv[]));
542static int 		WmStateCmd _ANSI_ARGS_((Tk_Window tkwin,
543			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
544			    Tcl_Obj *CONST objv[]));
545static int 		WmTitleCmd _ANSI_ARGS_((Tk_Window tkwin,
546			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
547			    Tcl_Obj *CONST objv[]));
548static int 		WmTransientCmd _ANSI_ARGS_((Tk_Window tkwin,
549			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
550			    Tcl_Obj *CONST objv[]));
551static int 		WmWithdrawCmd _ANSI_ARGS_((Tk_Window tkwin,
552			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
553			    Tcl_Obj *CONST objv[]));
554static void		WmUpdateGeom _ANSI_ARGS_((WmInfo *wmPtr,
555			    TkWindow *winPtr));
556
557/* Used in BytesPerLine */
558#define WIDTHBYTES(bits)      ((((bits) + 31)>>5)<<2)
559
560/*
561 *----------------------------------------------------------------------
562 *
563 * DIBNumColors --
564 *
565 *	Calculates the number of entries in the color table, given by
566 *	LPSTR lpbi - pointer to the CF_DIB memory block.  Used by
567 *	titlebar icon code.
568 *
569 * Results:
570 *
571 *      WORD - Number of entries in the color table.
572 *
573 * Side effects: None.
574 *
575 *
576 *----------------------------------------------------------------------
577 */
578static WORD
579DIBNumColors( LPSTR lpbi )
580{
581    WORD wBitCount;
582    DWORD dwClrUsed;
583
584    dwClrUsed = ((LPBITMAPINFOHEADER) lpbi)->biClrUsed;
585
586    if (dwClrUsed)
587	return (WORD) dwClrUsed;
588
589    wBitCount = ((LPBITMAPINFOHEADER) lpbi)->biBitCount;
590
591    switch (wBitCount)
592    {
593	case 1: return 2;
594	case 4: return 16;
595	case 8:	return 256;
596	default:return 0;
597    }
598}
599
600/*
601 *----------------------------------------------------------------------
602 *
603 * PaletteSize --
604 *
605 *	Calculates the number of bytes in the color table, as given by
606 *	LPSTR lpbi - pointer to the CF_DIB memory block.  Used by
607 *	titlebar icon code.
608 *
609 * Results:
610 *	number of bytes in the color table
611 *
612 * Side effects: None.
613 *
614 *
615 *----------------------------------------------------------------------
616 */
617static WORD
618PaletteSize( LPSTR lpbi )
619{
620    return ((WORD)( DIBNumColors( lpbi ) * sizeof( RGBQUAD )) );
621}
622
623/*
624 *----------------------------------------------------------------------
625 *
626 * FindDIBits --
627 *
628 *	Locate the image bits in a CF_DIB format DIB, as given by
629 *	LPSTR lpbi - pointer to the CF_DIB memory block.  Used by
630 *	titlebar icon code.
631 *
632 * Results:
633 *	pointer to the image bits
634 *
635 * Side effects: None
636 *
637 *
638 *----------------------------------------------------------------------
639 */
640static LPSTR
641FindDIBBits( LPSTR lpbi )
642{
643   return ( lpbi + *(LPDWORD)lpbi + PaletteSize( lpbi ) );
644}
645
646/*
647 *----------------------------------------------------------------------
648 *
649 * BytesPerLine --
650 *
651 *	Calculates the number of bytes in one scan line, as given by
652 *	LPBITMAPINFOHEADER lpBMIH - pointer to the BITMAPINFOHEADER
653 *	that begins the CF_DIB block.  Used by titlebar icon code.
654 *
655 * Results:
656 *	number of bytes in one scan line (DWORD aligned)
657 *
658 * Side effects: None
659 *
660 *
661 *----------------------------------------------------------------------
662 */
663static DWORD
664BytesPerLine( LPBITMAPINFOHEADER lpBMIH )
665{
666    return WIDTHBYTES(lpBMIH->biWidth * lpBMIH->biPlanes * lpBMIH->biBitCount);
667}
668
669/*
670 *----------------------------------------------------------------------
671 *
672 * AdjustIconImagePointers --
673 *
674 *	Adjusts internal pointers in icon resource struct, as given
675 *	by LPICONIMAGE lpImage - the resource to handle.  Used by
676 *	titlebar icon code.
677 *
678 * Results:
679 *	BOOL - TRUE for success, FALSE for failure
680 *
681 * Side effects:
682 *
683 *
684 *----------------------------------------------------------------------
685 */
686static BOOL
687AdjustIconImagePointers( LPICONIMAGE lpImage )
688{
689    /*  Sanity check */
690    if (lpImage==NULL)
691	return FALSE;
692    /*  BITMAPINFO is at beginning of bits */
693    lpImage->lpbi = (LPBITMAPINFO)lpImage->lpBits;
694    /*  Width - simple enough */
695    lpImage->Width = lpImage->lpbi->bmiHeader.biWidth;
696    /*
697     * Icons are stored in funky format where height is doubled
698     * so account for that
699     */
700    lpImage->Height = (lpImage->lpbi->bmiHeader.biHeight)/2;
701    /*  How many colors? */
702    lpImage->Colors = lpImage->lpbi->bmiHeader.biPlanes *
703                            lpImage->lpbi->bmiHeader.biBitCount;
704    /*  XOR bits follow the header and color table */
705    lpImage->lpXOR = (LPBYTE)FindDIBBits(((LPSTR)lpImage->lpbi));
706    /*  AND bits follow the XOR bits */
707    lpImage->lpAND = lpImage->lpXOR + (lpImage->Height*
708		BytesPerLine((LPBITMAPINFOHEADER)(lpImage->lpbi)));
709    return TRUE;
710}
711
712/*
713 *----------------------------------------------------------------------
714 *
715 * MakeIconOrCursorFromResource --
716 *
717 *	Construct an actual HICON structure from the information
718 *	in a resource.
719 *
720 * Results:
721 *
722 *
723 * Side effects:
724 *
725 *
726 *----------------------------------------------------------------------
727 */
728static HICON
729MakeIconOrCursorFromResource(LPICONIMAGE lpIcon, BOOL isIcon) {
730    HICON hIcon ;
731    static FARPROC pfnCreateIconFromResourceEx=NULL;
732    static int initinfo=0;
733    /*  Sanity Check */
734    if (lpIcon == NULL)
735	return NULL;
736    if (lpIcon->lpBits == NULL)
737	return NULL;
738    if (!initinfo) {
739	HMODULE hMod = GetModuleHandleA("USER32.DLL");
740	initinfo=1;
741	if (hMod){
742	    pfnCreateIconFromResourceEx =
743	      GetProcAddress(hMod, "CreateIconFromResourceEx");
744	}
745    }
746    /*  Let the OS do the real work :) */
747    if (pfnCreateIconFromResourceEx!=NULL) {
748	hIcon = (HICON) (pfnCreateIconFromResourceEx)
749	(lpIcon->lpBits, lpIcon->dwNumBytes, isIcon, 0x00030000,
750	 (*(LPBITMAPINFOHEADER)(lpIcon->lpBits)).biWidth,
751	 (*(LPBITMAPINFOHEADER)(lpIcon->lpBits)).biHeight/2, 0);
752    } else {
753	 hIcon = NULL;
754    }
755    /*  It failed, odds are good we're on NT so try the non-Ex way */
756    if (hIcon == NULL)    {
757	/*  We would break on NT if we try with a 16bpp image */
758	if (lpIcon->lpbi->bmiHeader.biBitCount != 16) {
759	    hIcon = CreateIconFromResource(lpIcon->lpBits, lpIcon->dwNumBytes,
760					   isIcon, 0x00030000);
761	}
762    }
763    return hIcon;
764}
765
766/*
767 *----------------------------------------------------------------------
768 *
769 * ReadICOHeader --
770 *
771 *	Reads the header from an ICO file, as specfied by channel.
772 *
773 * Results:
774 *	UINT - Number of images in file, -1 for failure.
775 *	If this succeeds, there is a decent chance this is a
776 *	valid icon file.
777 *
778 * Side effects:
779 *
780 *
781 *----------------------------------------------------------------------
782 */
783static int
784ReadICOHeader( Tcl_Channel channel )
785{
786    WORD    Input;
787    DWORD	dwBytesRead;
788
789    /*  Read the 'reserved' WORD */
790    dwBytesRead = Tcl_Read( channel, (char*)&Input, sizeof( WORD ));
791    /*  Did we get a WORD? */
792    if (dwBytesRead != sizeof( WORD ))
793	return -1;
794    /*  Was it 'reserved' ?   (ie 0) */
795    if (Input != 0)
796	return -1;
797    /*  Read the type WORD */
798    dwBytesRead = Tcl_Read( channel, (char*)&Input, sizeof( WORD ));
799    /*  Did we get a WORD? */
800    if (dwBytesRead != sizeof( WORD ))
801	return -1;
802    /*  Was it type 1? */
803    if (Input != 1)
804	return -1;
805    /*  Get the count of images */
806    dwBytesRead = Tcl_Read( channel, (char*)&Input, sizeof( WORD ));
807    /*  Did we get a WORD? */
808    if (dwBytesRead != sizeof( WORD ))
809	return -1;
810    /*  Return the count */
811    return (int)Input;
812}
813
814/*
815 *----------------------------------------------------------------------
816 *
817 * InitWindowClass --
818 *
819 *	This routine creates the Wm toplevel decorative frame class.
820 *
821 * Results:
822 *	None.
823 *
824 * Side effects:
825 *	Registers a new window class.
826 *
827 *----------------------------------------------------------------------
828 */
829static int
830InitWindowClass(WinIconPtr titlebaricon)
831{
832    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
833	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
834
835    if (! tsdPtr->initialized) {
836	tsdPtr->initialized = 1;
837	tsdPtr->firstWindow = 1;
838	tsdPtr->iconPtr = NULL;
839    }
840    if (! initialized) {
841	Tcl_MutexLock(&winWmMutex);
842	if (! initialized) {
843	    Tcl_DString classString;
844	    WNDCLASS class;
845	    initialized = 1;
846
847	    if (shgetfileinfoProc == NULL) {
848		HINSTANCE hInstance = LoadLibraryA("shell32");
849		if (hInstance != NULL) {
850		    shgetfileinfoProc =
851		    (DWORD* (WINAPI *) (LPCTSTR pszPath, DWORD dwFileAttributes,
852    SHFILEINFO* psfi, UINT cbFileInfo, UINT uFlags)) GetProcAddress(hInstance,
853		      "SHGetFileInfo");
854		    FreeLibrary(hInstance);
855		}
856	    }
857	    if (setLayeredWindowAttributesProc == NULL) {
858		HINSTANCE hInstance = LoadLibraryA("user32");
859		if (hInstance != NULL) {
860		    setLayeredWindowAttributesProc =
861			(BOOL (WINAPI *) (HWND hwnd, COLORREF crKey,
862				BYTE bAlpha, DWORD dwFlags))
863			GetProcAddress(hInstance,
864				"SetLayeredWindowAttributes");
865		    FreeLibrary(hInstance);
866		}
867	    }
868	    /*
869	     * The only difference between WNDCLASSW and WNDCLASSA are
870	     * in pointers, so we can use the generic structure WNDCLASS.
871	     */
872	    ZeroMemory(&class, sizeof(WNDCLASS));
873
874	    class.style = CS_HREDRAW | CS_VREDRAW;
875	    class.hInstance = Tk_GetHINSTANCE();
876	    Tcl_WinUtfToTChar(TK_WIN_TOPLEVEL_CLASS_NAME, -1, &classString);
877	    class.lpszClassName = (LPCTSTR) Tcl_DStringValue(&classString);
878	    class.lpfnWndProc = WmProc;
879	    if (titlebaricon == NULL) {
880		class.hIcon = LoadIcon(Tk_GetHINSTANCE(), "tk");
881	    } else {
882		class.hIcon = GetIcon(titlebaricon, ICON_BIG);
883		if (class.hIcon == NULL) {
884		    return TCL_ERROR;
885		}
886		/*
887		 * Store pointer to default icon so we know when
888		 * we need to free that information
889		 */
890		tsdPtr->iconPtr = titlebaricon;
891	    }
892	    class.hCursor = LoadCursor(NULL, IDC_ARROW);
893
894	    if (!(*tkWinProcs->registerClass)(&class)) {
895		Tcl_Panic("Unable to register TkTopLevel class");
896	    }
897
898	    Tcl_DStringFree(&classString);
899	}
900	Tcl_MutexUnlock(&winWmMutex);
901    }
902    return TCL_OK;
903}
904
905/*
906 *----------------------------------------------------------------------
907 *
908 * InitWm --
909 *
910 *	This initialises the window manager
911 *
912 * Results:
913 *	None.
914 *
915 * Side effects:
916 *	Registers a new window class.
917 *
918 *----------------------------------------------------------------------
919 */
920static void
921InitWm(void)
922{
923    /* Ignore return result */
924    (void) InitWindowClass(NULL);
925}
926
927/*
928 *----------------------------------------------------------------------
929 *
930 * WinSetIcon --
931 *
932 *	Sets either the default toplevel titlebar icon, or the icon
933 *	for a specific toplevel (if tkw is given, then only that
934 *	window is used).
935 *
936 *	The ref-count of the titlebaricon is NOT changed.  If this
937 *	function returns successfully, the caller should assume
938 *	the icon was used (and therefore the ref-count should
939 *	be adjusted to reflect that fact).  If the function returned
940 *	an error, the caller should assume the icon was not used
941 *	(and may wish to free the memory associated with it).
942 *
943 * Results:
944 *	A standard Tcl return code.
945 *
946 * Side effects:
947 *	One or all windows may have their icon changed.
948 *	The Tcl result may be modified.
949 *	The window-manager will be initialised if it wasn't already.
950 *	The given window will be forced into existence.
951 *
952 *----------------------------------------------------------------------
953 */
954static int
955WinSetIcon(interp, titlebaricon, tkw)
956    Tcl_Interp *interp;
957    WinIconPtr titlebaricon;
958    Tk_Window tkw;
959{
960    WmInfo *wmPtr;
961    HWND hwnd;
962    int application = 0;
963
964    if (tkw == NULL) {
965        tkw = Tk_MainWindow(interp);
966	application = 1;
967    }
968
969    if (!(Tk_IsTopLevel(tkw))) {
970	Tcl_AppendResult(interp, "window \"", Tk_PathName(tkw),
971		"\" isn't a top-level window", (char *) NULL);
972	return TCL_ERROR;
973    }
974    if (Tk_WindowId(tkw) == None) {
975	Tk_MakeWindowExist(tkw);
976    }
977    /* We must get the window's wrapper, not the window itself */
978    wmPtr = ((TkWindow*)tkw)->wmInfoPtr;
979    hwnd = wmPtr->wrapper;
980
981    if (application) {
982	if (hwnd == NULL) {
983	    /*
984	     * I don't actually think this is ever the correct thing, unless
985	     * perhaps the window doesn't have a wrapper.  But I believe all
986	     * windows have wrappers.
987	     */
988	    hwnd = Tk_GetHWND(Tk_WindowId(tkw));
989	}
990	/*
991	 * If we aren't initialised, then just initialise with the user's
992	 * icon.  Otherwise our icon choice will be ignored moments later
993	 * when Tk finishes initialising.
994	 */
995	if (!initialized) {
996	    if (InitWindowClass(titlebaricon) != TCL_OK) {
997		Tcl_AppendResult(interp,"Unable to set icon", (char*)NULL);
998		return TCL_ERROR;
999	    }
1000	} else {
1001	    ThreadSpecificData *tsdPtr;
1002	    if (
1003#ifdef _WIN64
1004		!SetClassLongPtr(hwnd, GCLP_HICONSM,
1005			(LPARAM)GetIcon(titlebaricon, ICON_SMALL))
1006#else
1007		!SetClassLong(hwnd, GCL_HICONSM,
1008			(LPARAM)GetIcon(titlebaricon, ICON_SMALL))
1009#endif
1010		) {
1011		/*
1012		 * For some reason this triggers, even though it seems
1013		 * to be successful This is probably related to the
1014		 * WNDCLASS vs WNDCLASSEX difference.  Anyway it seems
1015		 * we have to ignore errors returned here.
1016		 */
1017
1018		/*
1019		 * Tcl_AppendResult(interp,"Unable to set new small icon", (char*)NULL);
1020		 * return TCL_ERROR;
1021		 */
1022	    }
1023	    if (
1024#ifdef _WIN64
1025		!SetClassLongPtr(hwnd, GCLP_HICON,
1026			(LPARAM)GetIcon(titlebaricon, ICON_BIG))
1027#else
1028		!SetClassLong(hwnd, GCL_HICON,
1029			(LPARAM)GetIcon(titlebaricon, ICON_BIG))
1030#endif
1031		) {
1032		Tcl_AppendResult(interp,"Unable to set new icon", (char*)NULL);
1033		return TCL_ERROR;
1034	    }
1035	    tsdPtr = (ThreadSpecificData *)
1036		    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1037	    if (tsdPtr->iconPtr != NULL) {
1038		DecrIconRefCount(tsdPtr->iconPtr);
1039	    }
1040	    tsdPtr->iconPtr = titlebaricon;
1041	}
1042    } else {
1043	if (!initialized) {
1044	    /*
1045	     * Need to initialise the wm otherwise we will fail on
1046	     * code which tries to set a toplevel's icon before that
1047	     * happens.  Ignore return result.
1048	     */
1049	    (void)InitWindowClass(NULL);
1050	}
1051	/*
1052	 * The following code is exercised if you do
1053	 *
1054	 *   toplevel .t ; wm titlebaricon .t foo.icr
1055	 *
1056	 * i.e. the wm hasn't had time to properly create
1057	 * the '.t' window before you set the icon.
1058	 */
1059	if (hwnd == NULL) {
1060	    /*
1061	     * This little snippet is copied from the 'Map' function,
1062	     * and should probably be placed in one proper location
1063	     */
1064	    UpdateWrapper(wmPtr->winPtr);
1065	    wmPtr = ((TkWindow*)tkw)->wmInfoPtr;
1066	    hwnd = wmPtr->wrapper;
1067	    if (hwnd == NULL) {
1068		Tcl_AppendResult(interp,
1069			"Can't set icon; window has no wrapper.", (char*)NULL);
1070		return TCL_ERROR;
1071	    }
1072	}
1073	SendMessage(hwnd, WM_SETICON, ICON_SMALL,
1074		(LPARAM) GetIcon(titlebaricon, ICON_SMALL));
1075	SendMessage(hwnd, WM_SETICON, ICON_BIG,
1076		(LPARAM) GetIcon(titlebaricon, ICON_BIG));
1077
1078	/* Update the iconPtr we keep for each WmInfo structure. */
1079	if (wmPtr->iconPtr != NULL) {
1080	    /* Free any old icon ptr which is associated with this window. */
1081	    DecrIconRefCount(wmPtr->iconPtr);
1082	}
1083	/*
1084	 * We do not need to increment the ref count for the
1085	 * titlebaricon, because it was already incremented when we
1086	 * retrieved it.
1087	 */
1088	wmPtr->iconPtr = titlebaricon;
1089    }
1090    return TCL_OK;
1091}
1092
1093/*
1094 *----------------------------------------------------------------------
1095 *
1096 * TkWinGetIcon --
1097 *
1098 *	Gets either the default toplevel titlebar icon, or the icon
1099 *	for a specific toplevel (ICON_SMALL or ICON_BIG).
1100 *
1101 * Results:
1102 *	A Windows HICON.
1103 *
1104 * Side effects:
1105 *	The given window will be forced into existence.
1106 *
1107 *----------------------------------------------------------------------
1108 */
1109HICON
1110TkWinGetIcon(Tk_Window tkwin, DWORD iconsize)
1111{
1112    WmInfo *wmPtr;
1113    HICON icon;
1114    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1115	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1116
1117    if (tsdPtr->iconPtr != NULL) {
1118	/*
1119	 * return default toplevel icon
1120	 */
1121	return GetIcon(tsdPtr->iconPtr, iconsize);
1122    }
1123
1124    /* ensure we operate on the toplevel, that has the icon refs */
1125    while (!Tk_IsTopLevel(tkwin)) {
1126	tkwin = Tk_Parent(tkwin);
1127	if (tkwin == NULL) {
1128	    return NULL;
1129	}
1130    }
1131
1132    if (Tk_WindowId(tkwin) == None) {
1133	Tk_MakeWindowExist(tkwin);
1134    }
1135
1136    wmPtr = ((TkWindow *) tkwin)->wmInfoPtr;
1137    if (wmPtr->iconPtr != NULL) {
1138	/*
1139	 * return window toplevel icon
1140	 */
1141	return GetIcon(wmPtr->iconPtr, iconsize);
1142    }
1143
1144    /*
1145     * Find the icon otherwise associated with the toplevel, or
1146     * finally with the window class.
1147     */
1148    icon = (HICON) SendMessage(wmPtr->wrapper, WM_GETICON, iconsize,
1149	    (LPARAM) NULL);
1150    if (icon == (HICON) NULL) {
1151#ifdef _WIN64
1152	icon = (HICON) GetClassLongPtr(wmPtr->wrapper,
1153		(iconsize == ICON_BIG) ? GCLP_HICON : GCLP_HICONSM);
1154#else
1155	icon = (HICON) GetClassLong(wmPtr->wrapper,
1156		(iconsize == ICON_BIG) ? GCL_HICON : GCL_HICONSM);
1157#endif
1158    }
1159    return icon;
1160}
1161
1162/*
1163 *----------------------------------------------------------------------
1164 *
1165 * ReadIconFromFile --
1166 *
1167 *	Read the contents of a file (usually .ico, .icr) and extract an
1168 *	icon resource, if possible, otherwise check if the shell has an
1169 *	icon assigned to the given file and use that.  If both of those
1170 *	fail, then NULL is returned, and an error message will already be
1171 *	in the interpreter.
1172 *
1173 * Results:
1174 *	A WinIconPtr structure containing the icons in the file, with
1175 *	its ref count already incremented. The calling procedure should
1176 *	either place this structure inside a WmInfo structure, or it should
1177 *	pass it on to DecrIconRefCount() to ensure no memory leaks occur.
1178 *
1179 *	If the given fileName did not contain a valid icon structure,
1180 *	return NULL.
1181 *
1182 * Side effects:
1183 *	Memory is allocated for the returned structure and the icons
1184 *	it contains.  If the structure is not wanted, it should be
1185 *	passed to DecrIconRefCount, and in any case a valid ref count
1186 *	should be ensured to avoid memory leaks.
1187 *
1188 *	Currently icon resources are not shared, so the ref count of
1189 *	one of these structures will always be 0 or 1.  However all we
1190 *	need do is implement some sort of lookup function between
1191 *	filenames and WinIconPtr structures and no other code will need
1192 *	to be changed.  The pseudo-code for this is implemented below
1193 *	in the 'if (0)' branch.  It did not seem necessary to implement
1194 *	this optimisation here, since moving to icon<->image
1195 *	conversions will probably make it obsolete.
1196 *
1197 *----------------------------------------------------------------------
1198 */
1199static WinIconPtr
1200ReadIconFromFile(interp, fileName)
1201    Tcl_Interp *interp;
1202    Tcl_Obj *fileName;
1203{
1204    WinIconPtr titlebaricon = NULL;
1205
1206    if (0 /* If we already have an icon for this filename */) {
1207	titlebaricon = NULL; /* Get the real value from a lookup */
1208	titlebaricon->refCount++;
1209	return titlebaricon;
1210    } else {
1211	/* First check if it is a .ico file */
1212	BlockOfIconImagesPtr lpIR;
1213	lpIR = ReadIconOrCursorFromFile(interp, fileName, TRUE);
1214
1215	/*
1216	 * Then see if we can ask the shell for the icon for this file.
1217	 * We want both the regular and small icons so that the Alt-Tab
1218	 * (task-switching) display uses the right icon.
1219	 */
1220	if (lpIR == NULL && shgetfileinfoProc != NULL) {
1221	    SHFILEINFO sfiSM;
1222	    Tcl_DString ds, ds2;
1223	    DWORD *res;
1224	    CONST char *file;
1225
1226	    file = Tcl_TranslateFileName(interp, Tcl_GetString(fileName), &ds);
1227	    if (file == NULL) { return NULL; }
1228	    Tcl_UtfToExternalDString(NULL, file, -1, &ds2);
1229	    Tcl_DStringFree(&ds);
1230	    res = (*shgetfileinfoProc)(Tcl_DStringValue(&ds2), 0, &sfiSM,
1231		    sizeof(SHFILEINFO), SHGFI_SMALLICON|SHGFI_ICON);
1232
1233	    if (res != 0) {
1234		SHFILEINFO sfi;
1235		int size;
1236
1237		Tcl_ResetResult(interp);
1238		res = (*shgetfileinfoProc)(Tcl_DStringValue(&ds2), 0, &sfi,
1239			sizeof(SHFILEINFO), SHGFI_ICON);
1240
1241		/* Account for extra icon, if necessary */
1242		size = sizeof(BlockOfIconImages) +
1243		    ((res != 0) ? sizeof(ICONIMAGE) : 0);
1244		lpIR = (BlockOfIconImagesPtr) ckalloc(size);
1245		if (lpIR == NULL) {
1246		    if (res != 0) {
1247			DestroyIcon(sfi.hIcon);
1248		    }
1249		    DestroyIcon(sfiSM.hIcon);
1250		    Tcl_DStringFree(&ds2);
1251		    return NULL;
1252		}
1253		ZeroMemory(lpIR, size);
1254
1255		lpIR->nNumImages               = ((res != 0) ? 2 : 1);
1256		lpIR->IconImages[0].Width      = 16;
1257		lpIR->IconImages[0].Height     = 16;
1258		lpIR->IconImages[0].Colors     = 4;
1259		lpIR->IconImages[0].hIcon      = sfiSM.hIcon;
1260		/* All other IconImages fields are ignored */
1261
1262		if (res != 0) {
1263		    lpIR->IconImages[1].Width  = 32;
1264		    lpIR->IconImages[1].Height = 32;
1265		    lpIR->IconImages[1].Colors = 4;
1266		    lpIR->IconImages[1].hIcon  = sfi.hIcon;
1267		}
1268	    }
1269	    Tcl_DStringFree(&ds2);
1270	}
1271	if (lpIR != NULL) {
1272	    titlebaricon = (WinIconPtr) ckalloc(sizeof(WinIconInstance));
1273	    titlebaricon->iconBlock = lpIR;
1274	    titlebaricon->refCount = 1;
1275	}
1276	return titlebaricon;
1277    }
1278}
1279
1280/*
1281 *----------------------------------------------------------------------
1282 *
1283 * GetIconFromPixmap --
1284 *
1285 *	Turn a Tk Pixmap (i.e. a bitmap) into an icon resource, if
1286 *	possible, otherwise NULL is returned.
1287 *
1288 * Results:
1289 *	A WinIconPtr structure containing a conversion of the given
1290 *	bitmap into an icon, with its ref count already incremented.  The
1291 *	calling procedure should either place this structure inside a
1292 *	WmInfo structure, or it should pass it on to DecrIconRefCount()
1293 *	to ensure no memory leaks occur.
1294 *
1295 *	If the given pixmap did not contain a valid icon structure,
1296 *	return NULL.
1297 *
1298 * Side effects:
1299 *	Memory is allocated for the returned structure and the icons
1300 *	it contains.  If the structure is not wanted, it should be
1301 *	passed to DecrIconRefCount, and in any case a valid ref count
1302 *	should be ensured to avoid memory leaks.
1303 *
1304 *	Currently icon resources are not shared, so the ref count of
1305 *	one of these structures will always be 0 or 1.  However all we
1306 *	need do is implement some sort of lookup function between
1307 *	pixmaps and WinIconPtr structures and no other code will need
1308 *	to be changed.
1309 *
1310 *----------------------------------------------------------------------
1311 */
1312static WinIconPtr
1313GetIconFromPixmap(dsPtr, pixmap)
1314    Display *dsPtr;
1315    Pixmap pixmap;
1316{
1317    WinIconPtr titlebaricon = NULL;
1318    TkWinDrawable* twdPtr = (TkWinDrawable*) pixmap;
1319
1320    if (twdPtr == NULL) {
1321        return NULL;
1322    }
1323
1324    if (0 /* If we already have an icon for this pixmap */) {
1325	titlebaricon = NULL; /* Get the real value from a lookup */
1326	titlebaricon->refCount++;
1327	return titlebaricon;
1328    } else {
1329	BlockOfIconImagesPtr lpIR;
1330	ICONINFO icon;
1331	HICON hIcon;
1332	int width, height;
1333
1334	Tk_SizeOfBitmap(dsPtr, pixmap, &width, &height);
1335
1336	icon.fIcon = TRUE;
1337	icon.xHotspot = 0;
1338	icon.yHotspot = 0;
1339	icon.hbmMask = twdPtr->bitmap.handle;
1340	icon.hbmColor = twdPtr->bitmap.handle;
1341
1342	hIcon = CreateIconIndirect(&icon);
1343	if (hIcon == NULL) {
1344	    return NULL;
1345	}
1346
1347	lpIR = (BlockOfIconImagesPtr) ckalloc(sizeof(BlockOfIconImages));
1348	if (lpIR == NULL) {
1349	    DestroyIcon(hIcon);
1350	    return NULL;
1351	}
1352
1353	lpIR->nNumImages = 1;
1354	lpIR->IconImages[0].Width = width;
1355	lpIR->IconImages[0].Height = height;
1356	lpIR->IconImages[0].Colors = 1 << twdPtr->bitmap.depth;
1357	lpIR->IconImages[0].hIcon = hIcon;
1358	/* These fields are ignored */
1359	lpIR->IconImages[0].lpBits = 0;
1360	lpIR->IconImages[0].dwNumBytes = 0;
1361	lpIR->IconImages[0].lpXOR = 0;
1362	lpIR->IconImages[0].lpAND = 0;
1363
1364	titlebaricon = (WinIconPtr) ckalloc(sizeof(WinIconInstance));
1365	titlebaricon->iconBlock = lpIR;
1366	titlebaricon->refCount = 1;
1367	return titlebaricon;
1368    }
1369}
1370
1371/*
1372 *----------------------------------------------------------------------
1373 *
1374 * DecrIconRefCount --
1375 *
1376 *	Reduces the reference count.
1377 *
1378 * Results:
1379 *	None.
1380 *
1381 * Side effects:
1382 *	If the ref count falls to zero, free the memory associated
1383 *	with the icon resource structures.  In this case the pointer
1384 *	passed into this function is no longer valid.
1385 *
1386 *----------------------------------------------------------------------
1387 */
1388static void
1389DecrIconRefCount(WinIconPtr titlebaricon) {
1390    titlebaricon->refCount--;
1391
1392    if (titlebaricon->refCount <= 0) {
1393	if (titlebaricon->iconBlock != NULL) {
1394	    FreeIconBlock(titlebaricon->iconBlock);
1395	}
1396	titlebaricon->iconBlock = NULL;
1397
1398	ckfree((char*)titlebaricon);
1399    }
1400}
1401
1402/*
1403 *----------------------------------------------------------------------
1404 *
1405 * FreeIconBlock --
1406 *
1407 *	Frees all memory associated with a previously loaded
1408 *	titlebaricon.  The icon block pointer is no longer
1409 *	valid once this function returns.
1410 *
1411 * Results:
1412 *	None.
1413 *
1414 * Side effects:
1415 *
1416 *
1417 *----------------------------------------------------------------------
1418 */
1419static void
1420FreeIconBlock(BlockOfIconImagesPtr lpIR)
1421{
1422    int i;
1423
1424    /* Free all the bits */
1425    for (i=0; i< lpIR->nNumImages; i++) {
1426	if (lpIR->IconImages[i].lpBits != NULL) {
1427	    ckfree((char*)lpIR->IconImages[i].lpBits);
1428	}
1429	if (lpIR->IconImages[i].hIcon != NULL) {
1430	    DestroyIcon(lpIR->IconImages[i].hIcon);
1431	}
1432    }
1433    ckfree ((char*)lpIR);
1434}
1435
1436/*
1437 *----------------------------------------------------------------------
1438 *
1439 * GetIcon --
1440 *
1441 *	Extracts an icon of a given size from an icon resource
1442 *
1443 * Results:
1444 *	Returns the icon, if found, else NULL.
1445 *
1446 * Side effects:
1447 *
1448 *
1449 *----------------------------------------------------------------------
1450 */
1451static HICON
1452GetIcon(WinIconPtr titlebaricon, int icon_size)
1453{
1454    BlockOfIconImagesPtr lpIR;
1455
1456    if (titlebaricon == NULL) {
1457        return NULL;
1458    }
1459
1460    lpIR = titlebaricon->iconBlock;
1461    if (lpIR == NULL) {
1462	return NULL;
1463    } else {
1464	unsigned int size = (icon_size == 0 ? 16 : 32);
1465	int i;
1466
1467	for (i = 0; i < lpIR->nNumImages; i++) {
1468	    /* Take the first or a 32x32 16 color icon*/
1469	    if ((lpIR->IconImages[i].Height == size)
1470	       && (lpIR->IconImages[i].Width == size)
1471	       && (lpIR->IconImages[i].Colors >= 4)) {
1472		return lpIR->IconImages[i].hIcon;
1473	    }
1474	}
1475
1476	/*
1477	 * If we get here, then just return the first one,
1478	 * it will have to do!
1479	 */
1480	if (lpIR->nNumImages >= 1) {
1481	    return lpIR->IconImages[0].hIcon;
1482	}
1483    }
1484    return NULL;
1485}
1486
1487static HCURSOR
1488TclWinReadCursorFromFile(Tcl_Interp* interp, Tcl_Obj* fileName)
1489{
1490    BlockOfIconImagesPtr lpIR;
1491    HICON res = NULL;
1492
1493    lpIR = ReadIconOrCursorFromFile(interp, fileName, FALSE);
1494    if (lpIR == NULL) {
1495        return NULL;
1496    }
1497    if (lpIR->nNumImages >= 1) {
1498	res = CopyImage(lpIR->IconImages[0].hIcon, IMAGE_CURSOR,0,0,0);
1499    }
1500    FreeIconBlock(lpIR);
1501    return res;
1502}
1503
1504/*
1505 *----------------------------------------------------------------------
1506 *
1507 * ReadIconOrCursorFromFile --
1508 *
1509 *	Reads an Icon Resource from an ICO file, as given by
1510 *	char* fileName - Name of the ICO file. This name should
1511 *	be in Utf format.
1512 *
1513 * Results:
1514 *	Returns an icon resource, if found, else NULL.
1515 *
1516 * Side effects:
1517 *      May leave error messages in the Tcl interpreter.
1518 *
1519 *----------------------------------------------------------------------
1520 */
1521static BlockOfIconImagesPtr
1522ReadIconOrCursorFromFile(Tcl_Interp* interp, Tcl_Obj* fileName, BOOL isIcon)
1523{
1524    BlockOfIconImagesPtr lpIR, lpNew;
1525    Tcl_Channel          channel;
1526    int                  i;
1527    DWORD            	 dwBytesRead;
1528    LPICONDIRENTRY    	 lpIDE;
1529
1530    /*  Open the file */
1531    channel = Tcl_FSOpenFileChannel(interp, fileName, "r", 0);
1532    if (channel == NULL) {
1533	Tcl_AppendResult(interp,"Error opening file \"",
1534			 Tcl_GetString(fileName),
1535	                 "\" for reading",(char*)NULL);
1536	return NULL;
1537    }
1538    if (Tcl_SetChannelOption(interp, channel, "-translation", "binary")
1539	    != TCL_OK) {
1540	Tcl_Close(NULL, channel);
1541	return NULL;
1542    }
1543    if (Tcl_SetChannelOption(interp, channel, "-encoding", "binary")
1544	    != TCL_OK) {
1545	Tcl_Close(NULL, channel);
1546	return NULL;
1547    }
1548    /*  Allocate memory for the resource structure */
1549    lpIR = (BlockOfIconImagesPtr) ckalloc(sizeof(BlockOfIconImages));
1550    if (lpIR == NULL)    {
1551	Tcl_AppendResult(interp,"Error allocating memory",(char*)NULL);
1552	Tcl_Close(NULL, channel);
1553	return NULL;
1554    }
1555    /*  Read in the header */
1556    if ((lpIR->nNumImages = ReadICOHeader( channel )) == -1)    {
1557	Tcl_AppendResult(interp,"Invalid file header",(char*)NULL);
1558	Tcl_Close(NULL, channel);
1559	ckfree((char*) lpIR );
1560	return NULL;
1561    }
1562    /*  Adjust the size of the struct to account for the images */
1563    lpNew = (BlockOfIconImagesPtr) ckrealloc((char*)lpIR,
1564	sizeof(BlockOfIconImages) + ((lpIR->nNumImages-1) * sizeof(ICONIMAGE)));
1565    if (lpNew == NULL) {
1566	Tcl_AppendResult(interp,"Error allocating memory",(char*)NULL);
1567	Tcl_Close(NULL, channel);
1568	ckfree( (char*)lpIR );
1569	return NULL;
1570    }
1571    lpIR = lpNew;
1572    /*  Allocate enough memory for the icon directory entries */
1573    lpIDE = (LPICONDIRENTRY) ckalloc(lpIR->nNumImages * sizeof(ICONDIRENTRY));
1574    if (lpIDE == NULL) {
1575	Tcl_AppendResult(interp,"Error allocating memory",(char*)NULL);
1576	Tcl_Close(NULL, channel);
1577	ckfree( (char*)lpIR );
1578	return NULL;
1579    }
1580    /*  Read in the icon directory entries */
1581    dwBytesRead = Tcl_Read(channel, (char*)lpIDE,
1582			   lpIR->nNumImages * sizeof( ICONDIRENTRY ));
1583    if (dwBytesRead != lpIR->nNumImages * sizeof( ICONDIRENTRY ))    {
1584	Tcl_AppendResult(interp,"Error reading file",(char*)NULL);
1585	Tcl_Close(NULL, channel);
1586	ckfree( (char*)lpIR );
1587	return NULL;
1588    }
1589    /* NULL-out everything to make memory management easier */
1590    for( i = 0; i < lpIR->nNumImages; i++ ) {
1591	lpIR->IconImages[i].lpBits = NULL;
1592    }
1593    /*  Loop through and read in each image */
1594    for( i = 0; i < lpIR->nNumImages; i++ )    {
1595	/*  Allocate memory for the resource */
1596	lpIR->IconImages[i].lpBits = (LPBYTE) ckalloc(lpIDE[i].dwBytesInRes);
1597	if (lpIR->IconImages[i].lpBits == NULL) {
1598	    Tcl_AppendResult(interp,"Error allocating memory",(char*)NULL);
1599	    goto readError;
1600	}
1601	lpIR->IconImages[i].dwNumBytes = lpIDE[i].dwBytesInRes;
1602	/*  Seek to beginning of this image */
1603	if (Tcl_Seek(channel, lpIDE[i].dwImageOffset, FILE_BEGIN) == -1) {
1604	    Tcl_AppendResult(interp,"Error seeking in file",(char*)NULL);
1605	    goto readError;
1606	}
1607	/*  Read it in */
1608	dwBytesRead = Tcl_Read( channel, lpIR->IconImages[i].lpBits,
1609			       lpIDE[i].dwBytesInRes);
1610	if (dwBytesRead != lpIDE[i].dwBytesInRes) {
1611	    Tcl_AppendResult(interp,"Error reading file",(char*)NULL);
1612	    goto readError;
1613	}
1614	/*  Set the internal pointers appropriately */
1615	if (!AdjustIconImagePointers( &(lpIR->IconImages[i]))) {
1616	    Tcl_AppendResult(interp,"Error converting to internal format",
1617			     (char*)NULL);
1618	    goto readError;
1619	}
1620	lpIR->IconImages[i].hIcon =
1621	    MakeIconOrCursorFromResource(&(lpIR->IconImages[i]), isIcon);
1622    }
1623    /*  Clean up */
1624    ckfree((char*)lpIDE);
1625    Tcl_Close(NULL, channel);
1626    if (lpIR == NULL){
1627	Tcl_AppendResult(interp,"Reading of ", Tcl_GetString(fileName),
1628			 " failed!",(char*)NULL);
1629	return NULL;
1630    }
1631    return lpIR;
1632  readError:
1633    Tcl_Close(NULL, channel);
1634    for( i = 0; i < lpIR->nNumImages; i++ )    {
1635    	if (lpIR->IconImages[i].lpBits != NULL) {
1636	    ckfree((char*)lpIR->IconImages[i].lpBits);
1637	}
1638    }
1639    ckfree((char*)lpIDE );
1640    ckfree((char*)lpIR );
1641    return NULL;
1642}
1643
1644/*
1645 *----------------------------------------------------------------------
1646 *
1647 * GetTopLevel --
1648 *
1649 *	This function retrieves the TkWindow associated with the
1650 *	given HWND.
1651 *
1652 * Results:
1653 *	Returns the matching TkWindow.
1654 *
1655 * Side effects:
1656 *	None.
1657 *
1658 *----------------------------------------------------------------------
1659 */
1660static TkWindow *
1661GetTopLevel(hwnd)
1662    HWND hwnd;
1663{
1664    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1665            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1666
1667    /*
1668     * If this function is called before the CreateWindowEx call
1669     * has completed, then the user data slot will not have been
1670     * set yet, so we use the global createWindow variable.
1671     */
1672
1673    if (tsdPtr->createWindow) {
1674	return tsdPtr->createWindow;
1675    }
1676#ifdef _WIN64
1677    return (TkWindow *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1678#else
1679    return (TkWindow *) GetWindowLong(hwnd, GWL_USERDATA);
1680#endif
1681}
1682
1683/*
1684 *----------------------------------------------------------------------
1685 *
1686 * SetLimits --
1687 *
1688 *	Updates the minimum and maximum window size constraints.
1689 *
1690 * Results:
1691 *	None.
1692 *
1693 * Side effects:
1694 *	Changes the values of the info pointer to reflect the current
1695 *	minimum and maximum size values.
1696 *
1697 *----------------------------------------------------------------------
1698 */
1699
1700static void
1701SetLimits(hwnd, info)
1702    HWND hwnd;
1703    MINMAXINFO *info;
1704{
1705    register WmInfo *wmPtr;
1706    int maxWidth, maxHeight;
1707    int minWidth, minHeight;
1708    int base;
1709    TkWindow *winPtr = GetTopLevel(hwnd);
1710
1711    if (winPtr == NULL) {
1712	return;
1713    }
1714
1715    wmPtr = winPtr->wmInfoPtr;
1716
1717    /*
1718     * Copy latest constraint info.
1719     */
1720
1721    wmPtr->defMinWidth = info->ptMinTrackSize.x;
1722    wmPtr->defMinHeight = info->ptMinTrackSize.y;
1723    wmPtr->defMaxWidth = info->ptMaxTrackSize.x;
1724    wmPtr->defMaxHeight = info->ptMaxTrackSize.y;
1725
1726    GetMaxSize(wmPtr, &maxWidth, &maxHeight);
1727    GetMinSize(wmPtr, &minWidth, &minHeight);
1728
1729    if (wmPtr->gridWin != NULL) {
1730	base = winPtr->reqWidth - (wmPtr->reqGridWidth * wmPtr->widthInc);
1731	if (base < 0) {
1732	    base = 0;
1733	}
1734	base += wmPtr->borderWidth;
1735	info->ptMinTrackSize.x = base + (minWidth * wmPtr->widthInc);
1736	info->ptMaxTrackSize.x = base + (maxWidth * wmPtr->widthInc);
1737
1738	base = winPtr->reqHeight - (wmPtr->reqGridHeight * wmPtr->heightInc);
1739	if (base < 0) {
1740	    base = 0;
1741	}
1742	base += wmPtr->borderHeight;
1743	info->ptMinTrackSize.y = base + (minHeight * wmPtr->heightInc);
1744	info->ptMaxTrackSize.y = base + (maxHeight * wmPtr->heightInc);
1745    } else {
1746	info->ptMaxTrackSize.x = maxWidth + wmPtr->borderWidth;
1747	info->ptMaxTrackSize.y = maxHeight + wmPtr->borderHeight;
1748	info->ptMinTrackSize.x = minWidth + wmPtr->borderWidth;
1749	info->ptMinTrackSize.y = minHeight + wmPtr->borderHeight;
1750    }
1751
1752    /*
1753     * If the window isn't supposed to be resizable, then set the
1754     * minimum and maximum dimensions to be the same as the current size.
1755     */
1756
1757    if (!(wmPtr->flags & WM_SYNC_PENDING)) {
1758	if (wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) {
1759	    info->ptMinTrackSize.x = winPtr->changes.width
1760		+ wmPtr->borderWidth;
1761	    info->ptMaxTrackSize.x = info->ptMinTrackSize.x;
1762	}
1763	if (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE) {
1764	    info->ptMinTrackSize.y = winPtr->changes.height
1765		+ wmPtr->borderHeight;
1766	    info->ptMaxTrackSize.y = info->ptMinTrackSize.y;
1767	}
1768    }
1769}
1770
1771/*
1772 *----------------------------------------------------------------------
1773 *
1774 * TkWinWmCleanup --
1775 *
1776 *	Unregisters classes registered by the window manager. This is
1777 *	called from the DLL main entry point when the DLL is unloaded.
1778 *
1779 * Results:
1780 *	None.
1781 *
1782 * Side effects:
1783 *	The window classes are discarded.
1784 *
1785 *----------------------------------------------------------------------
1786 */
1787
1788void
1789TkWinWmCleanup(hInstance)
1790    HINSTANCE hInstance;
1791{
1792    ThreadSpecificData *tsdPtr;
1793
1794    /*
1795     * If we're using stubs to access the Tcl library, and they
1796     * haven't been initialized, we can't call Tcl_GetThreadData.
1797     */
1798
1799#ifdef USE_TCL_STUBS
1800    if (tclStubsPtr == NULL) {
1801        return;
1802    }
1803#endif
1804
1805    if (!initialized) {
1806	return;
1807    }
1808    initialized = 0;
1809
1810    tsdPtr = (ThreadSpecificData *)
1811            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1812
1813    if (!tsdPtr->initialized) {
1814        return;
1815    }
1816    tsdPtr->initialized = 0;
1817
1818    UnregisterClass(TK_WIN_TOPLEVEL_CLASS_NAME, hInstance);
1819}
1820
1821/*
1822 *--------------------------------------------------------------
1823 *
1824 * TkWmNewWindow --
1825 *
1826 *	This procedure is invoked whenever a new top-level
1827 *	window is created.  Its job is to initialize the WmInfo
1828 *	structure for the window.
1829 *
1830 * Results:
1831 *	None.
1832 *
1833 * Side effects:
1834 *	A WmInfo structure gets allocated and initialized.
1835 *
1836 *--------------------------------------------------------------
1837 */
1838
1839void
1840TkWmNewWindow(winPtr)
1841    TkWindow *winPtr;		/* Newly-created top-level window. */
1842{
1843    register WmInfo *wmPtr;
1844
1845    wmPtr = (WmInfo *) ckalloc(sizeof(WmInfo));
1846
1847    /*
1848     * Initialize full structure, then set what isn't NULL
1849     */
1850    ZeroMemory(wmPtr, sizeof(WmInfo));
1851    winPtr->wmInfoPtr = wmPtr;
1852    wmPtr->winPtr = winPtr;
1853    wmPtr->hints.flags = InputHint | StateHint;
1854    wmPtr->hints.input = True;
1855    wmPtr->hints.initial_state = NormalState;
1856    wmPtr->hints.icon_pixmap = None;
1857    wmPtr->hints.icon_window = None;
1858    wmPtr->hints.icon_x = wmPtr->hints.icon_y = 0;
1859    wmPtr->hints.icon_mask = None;
1860    wmPtr->hints.window_group = None;
1861
1862    /*
1863     * Default the maximum dimensions to the size of the display.
1864     */
1865
1866    wmPtr->defMinWidth = wmPtr->defMinHeight = 0;
1867    wmPtr->defMaxWidth = DisplayWidth(winPtr->display, winPtr->screenNum);
1868    wmPtr->defMaxHeight = DisplayHeight(winPtr->display, winPtr->screenNum);
1869    wmPtr->minWidth = wmPtr->minHeight = 1;
1870    wmPtr->maxWidth = wmPtr->maxHeight = 0;
1871    wmPtr->widthInc = wmPtr->heightInc = 1;
1872    wmPtr->minAspect.x = wmPtr->minAspect.y = 1;
1873    wmPtr->maxAspect.x = wmPtr->maxAspect.y = 1;
1874    wmPtr->reqGridWidth = wmPtr->reqGridHeight = -1;
1875    wmPtr->gravity = NorthWestGravity;
1876    wmPtr->width = -1;
1877    wmPtr->height = -1;
1878    wmPtr->x = winPtr->changes.x;
1879    wmPtr->y = winPtr->changes.y;
1880    wmPtr->crefObj = NULL;
1881    wmPtr->colorref = (COLORREF) NULL;
1882    wmPtr->alpha = 1.0;
1883
1884    wmPtr->configWidth = -1;
1885    wmPtr->configHeight = -1;
1886    wmPtr->flags = WM_NEVER_MAPPED;
1887    wmPtr->nextPtr = winPtr->dispPtr->firstWmPtr;
1888    winPtr->dispPtr->firstWmPtr = wmPtr;
1889
1890    /*
1891     * Tk must monitor structure events for top-level windows, in order
1892     * to detect size and position changes caused by window managers.
1893     */
1894
1895    Tk_CreateEventHandler((Tk_Window) winPtr, StructureNotifyMask,
1896	    TopLevelEventProc, (ClientData) winPtr);
1897
1898    /*
1899     * Arrange for geometry requests to be reflected from the window
1900     * to the window manager.
1901     */
1902
1903    Tk_ManageGeometry((Tk_Window) winPtr, &wmMgrType, (ClientData) 0);
1904}
1905
1906/*
1907 *----------------------------------------------------------------------
1908 *
1909 * UpdateWrapper --
1910 *
1911 *	This function creates the wrapper window that contains the
1912 *	window decorations and menus for a toplevel.  This function
1913 *	may be called after a window is mapped to change the window
1914 *	style.
1915 *
1916 * Results:
1917 *	None.
1918 *
1919 * Side effects:
1920 *	Destroys any old wrapper window and replaces it with a newly
1921 *	created wrapper.
1922 *
1923 *----------------------------------------------------------------------
1924 */
1925
1926static void
1927UpdateWrapper(winPtr)
1928    TkWindow *winPtr;		/* Top-level window to redecorate. */
1929{
1930    register WmInfo *wmPtr = winPtr->wmInfoPtr;
1931    HWND parentHWND, oldWrapper = wmPtr->wrapper;
1932    HWND child, nextHWND, focusHWND;
1933    int x, y, width, height, state;
1934    WINDOWPLACEMENT place;
1935    HICON hSmallIcon = NULL;
1936    HICON hBigIcon = NULL;
1937    Tcl_DString titleString, classString;
1938    int *childStateInfo = NULL;
1939    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1940            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1941
1942    if (winPtr->window == None) {
1943	/*
1944	 * Ensure existence of the window to update the wrapper for.
1945	 */
1946	Tk_MakeWindowExist((Tk_Window) winPtr);
1947    }
1948
1949    child = TkWinGetHWND(winPtr->window);
1950    parentHWND = NULL;
1951    /*
1952     * nextHWND will help us maintain Z order.
1953     * focusHWND will help us maintain focus, if we had it.
1954     */
1955    nextHWND = NULL;
1956    focusHWND = GetFocus();
1957    if ((oldWrapper == NULL) || (oldWrapper != GetForegroundWindow())) {
1958	focusHWND = NULL;
1959    }
1960
1961    if (winPtr->flags & TK_EMBEDDED) {
1962	wmPtr->wrapper = (HWND) winPtr->privatePtr;
1963	if (wmPtr->wrapper == NULL) {
1964	    Tcl_Panic("UpdateWrapper: Cannot find container window");
1965	}
1966	if (!IsWindow(wmPtr->wrapper)) {
1967	    Tcl_Panic("UpdateWrapper: Container was destroyed");
1968	}
1969
1970    } else {
1971	/*
1972	 * Pick the decorative frame style.  Override redirect windows get
1973	 * created as undecorated popups if they have no transient parent,
1974	 * otherwise they are children.  This allows splash screens to operate
1975	 * as an independent window, while having dropdows (like for a
1976	 * combobox) not grab focus away from their parent.  Transient windows
1977	 * get a modal dialog frame.  Neither override, nor transient windows
1978	 * appear in the Windows taskbar.  Note that a transient window does
1979	 * not resize by default, so we need to explicitly add the
1980	 * WS_THICKFRAME style if we want it to be resizeable.
1981	 */
1982
1983	if (winPtr->atts.override_redirect) {
1984	    wmPtr->style = WM_OVERRIDE_STYLE;
1985	    wmPtr->exStyle = EX_OVERRIDE_STYLE;
1986	    /* parent must be desktop even if we have a transient parent */
1987	    parentHWND = GetDesktopWindow();
1988	    if (wmPtr->masterPtr) {
1989		wmPtr->style |= WS_CHILD;
1990	    } else {
1991		wmPtr->style |= WS_POPUP;
1992	    }
1993	} else if (wmPtr->masterPtr) {
1994	    wmPtr->style = WM_TRANSIENT_STYLE;
1995	    wmPtr->exStyle = EX_TRANSIENT_STYLE;
1996	    parentHWND = Tk_GetHWND(Tk_WindowId(wmPtr->masterPtr));
1997	    if (! ((wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) &&
1998		    (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE))) {
1999		wmPtr->style |= WS_THICKFRAME;
2000	    }
2001	} else {
2002	    wmPtr->style = WM_TOPLEVEL_STYLE;
2003	    wmPtr->exStyle = EX_TOPLEVEL_STYLE;
2004	}
2005
2006	wmPtr->style   |= wmPtr->styleConfig;
2007	wmPtr->exStyle |= wmPtr->exStyleConfig;
2008
2009	if ((wmPtr->flags & WM_WIDTH_NOT_RESIZABLE)
2010		&& (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE)) {
2011	    wmPtr->style &= ~ (WS_MAXIMIZEBOX | WS_SIZEBOX);
2012	}
2013
2014	/*
2015	 * Compute the geometry of the parent and child windows.
2016	 */
2017
2018	if ((wmPtr->flags & WM_UPDATE_PENDING)) {
2019	    Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
2020	}
2021
2022	wmPtr->flags |= WM_CREATE_PENDING|WM_MOVE_PENDING;
2023	UpdateGeometryInfo((ClientData)winPtr);
2024	wmPtr->flags &= ~(WM_CREATE_PENDING|WM_MOVE_PENDING);
2025
2026	width = wmPtr->borderWidth + winPtr->changes.width;
2027	height = wmPtr->borderHeight + winPtr->changes.height;
2028
2029	/*
2030	 * Set the initial position from the user or program specified
2031	 * location.  If nothing has been specified, then let the system
2032	 * pick a location.
2033	 */
2034
2035	if (!(wmPtr->sizeHintsFlags & (USPosition | PPosition))
2036		&& (wmPtr->flags & WM_NEVER_MAPPED)) {
2037	    x = CW_USEDEFAULT;
2038	    y = CW_USEDEFAULT;
2039	} else {
2040	    x = winPtr->changes.x;
2041	    y = winPtr->changes.y;
2042	}
2043
2044	/*
2045	 * Create the containing window, and set the user data to point
2046	 * to the TkWindow.
2047	 */
2048
2049	tsdPtr->createWindow = winPtr;
2050	Tcl_WinUtfToTChar(((wmPtr->title != NULL) ?
2051                           wmPtr->title : winPtr->nameUid), -1, &titleString);
2052	Tcl_WinUtfToTChar(TK_WIN_TOPLEVEL_CLASS_NAME, -1, &classString);
2053	wmPtr->wrapper = (*tkWinProcs->createWindowEx)(wmPtr->exStyle,
2054		(LPCTSTR) Tcl_DStringValue(&classString),
2055		(LPCTSTR) Tcl_DStringValue(&titleString),
2056		wmPtr->style, x, y, width, height,
2057		parentHWND, NULL, Tk_GetHINSTANCE(), NULL);
2058	Tcl_DStringFree(&classString);
2059	Tcl_DStringFree(&titleString);
2060#ifdef _WIN64
2061	SetWindowLongPtr(wmPtr->wrapper, GWLP_USERDATA, (LONG_PTR) winPtr);
2062#else
2063	SetWindowLong(wmPtr->wrapper, GWL_USERDATA, (LONG) winPtr);
2064#endif
2065	tsdPtr->createWindow = NULL;
2066
2067	if ((wmPtr->exStyleConfig & WS_EX_LAYERED)
2068		&& setLayeredWindowAttributesProc != NULL) {
2069	    /*
2070	     * The user supplies a double from [0..1], but Windows wants an
2071	     * int (transparent) 0..255 (opaque), so do the translation.
2072	     * Add the 0.5 to round the value.
2073	     */
2074	    setLayeredWindowAttributesProc((HWND) wmPtr->wrapper,
2075		    wmPtr->colorref, (BYTE) (wmPtr->alpha * 255 + 0.5),
2076		    LWA_ALPHA | (wmPtr->crefObj ? LWA_COLORKEY : 0));
2077	} else {
2078	    /*
2079	     * Layering not used or supported.
2080	     */
2081	    wmPtr->alpha = 1.0;
2082	    if (wmPtr->crefObj) {
2083		Tcl_DecrRefCount(wmPtr->crefObj);
2084		wmPtr->crefObj = NULL;
2085	    }
2086	}
2087
2088	place.length = sizeof(WINDOWPLACEMENT);
2089	GetWindowPlacement(wmPtr->wrapper, &place);
2090	wmPtr->x = place.rcNormalPosition.left;
2091	wmPtr->y = place.rcNormalPosition.top;
2092
2093	if (!(winPtr->flags & TK_ALREADY_DEAD)) {
2094	    TkInstallFrameMenu((Tk_Window) winPtr);
2095	}
2096
2097	if (oldWrapper && (oldWrapper != wmPtr->wrapper)
2098		&& !(wmPtr->exStyle & WS_EX_TOPMOST)) {
2099	    /*
2100	     * We will adjust wrapper to have the same Z order as oldWrapper
2101	     * if it isn't a TOPMOST window.
2102	     */
2103	    nextHWND = GetNextWindow(oldWrapper, GW_HWNDPREV);
2104	}
2105    }
2106
2107    /*
2108     * Now we need to reparent the contained window and set its
2109     * style appropriately.  Be sure to update the style first so that
2110     * Windows doesn't try to set the focus to the child window.
2111     */
2112
2113#ifdef _WIN64
2114    SetWindowLongPtr(child, GWL_STYLE,
2115	    WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
2116#else
2117    SetWindowLong(child, GWL_STYLE,
2118	    WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
2119#endif
2120    if (winPtr->flags & TK_EMBEDDED) {
2121#ifdef _WIN64
2122	SetWindowLongPtr(child, GWLP_WNDPROC, (LONG_PTR) TopLevelProc);
2123#else
2124	SetWindowLong(child, GWL_WNDPROC, (LONG) TopLevelProc);
2125#endif
2126    }
2127    SetParent(child, wmPtr->wrapper);
2128    if (oldWrapper) {
2129	hSmallIcon = (HICON) SendMessage(oldWrapper, WM_GETICON, ICON_SMALL,
2130		(LPARAM) NULL);
2131	hBigIcon = (HICON) SendMessage(oldWrapper, WM_GETICON, ICON_BIG,
2132		(LPARAM) NULL);
2133    }
2134
2135    if (oldWrapper && (oldWrapper != wmPtr->wrapper)
2136	    && (oldWrapper != GetDesktopWindow())) {
2137#ifdef _WIN64
2138	SetWindowLongPtr(oldWrapper, GWLP_USERDATA, (LONG) NULL);
2139#else
2140	SetWindowLong(oldWrapper, GWL_USERDATA, (LONG) NULL);
2141#endif
2142
2143	if (wmPtr->numTransients > 0) {
2144	    /*
2145	     * Unset the current wrapper as the parent for all transient
2146	     * children for whom this is the master
2147	     */
2148	    WmInfo *wmPtr2;
2149
2150	    childStateInfo = (int *)ckalloc((unsigned) wmPtr->numTransients
2151		* sizeof(int));
2152	    state = 0;
2153	    for (wmPtr2 = winPtr->dispPtr->firstWmPtr; wmPtr2 != NULL;
2154		 wmPtr2 = wmPtr2->nextPtr) {
2155		if (wmPtr2->masterPtr == winPtr) {
2156		    if (!(wmPtr2->flags & WM_NEVER_MAPPED)) {
2157			childStateInfo[state++] = wmPtr2->hints.initial_state;
2158			SetParent(TkWinGetHWND(wmPtr2->winPtr->window), NULL);
2159		    }
2160		}
2161	    }
2162	}
2163	/*
2164	 * Remove the menubar before destroying the window so the menubar
2165	 * isn't destroyed.
2166	 */
2167
2168	SetMenu(oldWrapper, NULL);
2169	DestroyWindow(oldWrapper);
2170    }
2171
2172    wmPtr->flags &= ~WM_NEVER_MAPPED;
2173    SendMessage(wmPtr->wrapper, TK_ATTACHWINDOW, (WPARAM) child, 0);
2174
2175    /*
2176     * Force an initial transition from withdrawn to the real
2177     * initial state.  Set the Z order based on previous wrapper
2178     * before we set the state.
2179     */
2180
2181    state = wmPtr->hints.initial_state;
2182    wmPtr->hints.initial_state = WithdrawnState;
2183    if (nextHWND) {
2184	SetWindowPos(wmPtr->wrapper, nextHWND, 0, 0, 0, 0,
2185		SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_NOSENDCHANGING
2186		|SWP_NOOWNERZORDER);
2187    }
2188    TkpWmSetState(winPtr, state);
2189
2190    if (hSmallIcon != NULL) {
2191	SendMessage(wmPtr->wrapper,WM_SETICON,ICON_SMALL,(LPARAM)hSmallIcon);
2192    }
2193    if (hBigIcon != NULL) {
2194	SendMessage(wmPtr->wrapper,WM_SETICON,ICON_BIG,(LPARAM)hBigIcon);
2195    }
2196
2197    /*
2198     * If we are embedded then force a mapping of the window now,
2199     * because we do not necessarily own the wrapper and may not
2200     * get another opportunity to map ourselves. We should not be
2201     * in either iconified or zoomed states when we get here, so
2202     * it is safe to just check for TK_EMBEDDED without checking
2203     * what state we are supposed to be in (default to NormalState).
2204     */
2205
2206    if (winPtr->flags & TK_EMBEDDED) {
2207	XMapWindow(winPtr->display, winPtr->window);
2208    }
2209
2210    /*
2211     * Set up menus on the wrapper if required.
2212     */
2213
2214    if (wmPtr->hMenu != NULL) {
2215	wmPtr->flags = WM_SYNC_PENDING;
2216	SetMenu(wmPtr->wrapper, wmPtr->hMenu);
2217	wmPtr->flags &= ~WM_SYNC_PENDING;
2218    }
2219
2220    if (childStateInfo) {
2221	if (wmPtr->numTransients > 0) {
2222	    /*
2223	     * Reset all transient children for whom this is the master
2224	     */
2225	    WmInfo *wmPtr2;
2226
2227	    state = 0;
2228	    for (wmPtr2 = winPtr->dispPtr->firstWmPtr; wmPtr2 != NULL;
2229		 wmPtr2 = wmPtr2->nextPtr) {
2230		if (wmPtr2->masterPtr == winPtr) {
2231		    if (!(wmPtr2->flags & WM_NEVER_MAPPED)) {
2232			UpdateWrapper(wmPtr2->winPtr);
2233			TkpWmSetState(wmPtr2->winPtr, childStateInfo[state++]);
2234		    }
2235		}
2236	    }
2237	}
2238
2239	ckfree((char *) childStateInfo);
2240    }
2241
2242    /*
2243     * If this is the first window created by the application, then
2244     * we should activate the initial window.  Otherwise, if this had
2245     * the focus, we need to restore that.
2246     * XXX: Rewrapping generates a <FocusOut> and <FocusIn> that would
2247     * XXX: best be avoided, if we could safely mask them.
2248     */
2249
2250    if (tsdPtr->firstWindow) {
2251	tsdPtr->firstWindow = 0;
2252	SetActiveWindow(wmPtr->wrapper);
2253    } else if (focusHWND) {
2254	SetFocus(focusHWND);
2255    }
2256}
2257
2258/*
2259 *--------------------------------------------------------------
2260 *
2261 * TkWmMapWindow --
2262 *
2263 *	This procedure is invoked to map a top-level window.  This
2264 *	module gets a chance to update all window-manager-related
2265 *	information in properties before the window manager sees
2266 *	the map event and checks the properties.  It also gets to
2267 *	decide whether or not to even map the window after all.
2268 *
2269 * Results:
2270 *	None.
2271 *
2272 * Side effects:
2273 *	Properties of winPtr may get updated to provide up-to-date
2274 *	information to the window manager.  The window may also get
2275 *	mapped, but it may not be if this procedure decides that
2276 *	isn't appropriate (e.g. because the window is withdrawn).
2277 *
2278 *--------------------------------------------------------------
2279 */
2280
2281void
2282TkWmMapWindow(winPtr)
2283    TkWindow *winPtr;		/* Top-level window that's about to
2284				 * be mapped. */
2285{
2286    register WmInfo *wmPtr = winPtr->wmInfoPtr;
2287    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
2288            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
2289
2290    if (!tsdPtr->initialized) {
2291	InitWm();
2292    }
2293
2294    if (wmPtr->flags & WM_NEVER_MAPPED) {
2295	/*
2296	 * Don't map a transient if the master is not mapped.
2297	 */
2298
2299	if (wmPtr->masterPtr != NULL &&
2300	        !Tk_IsMapped(wmPtr->masterPtr)) {
2301	    wmPtr->hints.initial_state = WithdrawnState;
2302	    return;
2303	}
2304    } else {
2305	if (wmPtr->hints.initial_state == WithdrawnState) {
2306	    return;
2307	}
2308
2309	/*
2310	 * Map the window in either the iconified or normal state.  Note that
2311	 * we only send a map event if the window is in the normal state.
2312	 */
2313
2314	TkpWmSetState(winPtr, wmPtr->hints.initial_state);
2315    }
2316
2317    /*
2318     * This is the first time this window has ever been mapped.
2319     * Store all the window-manager-related information for the
2320     * window.
2321     */
2322
2323    UpdateWrapper(winPtr);
2324}
2325
2326/*
2327 *--------------------------------------------------------------
2328 *
2329 * TkWmUnmapWindow --
2330 *
2331 *	This procedure is invoked to unmap a top-level window.  The
2332 *	only thing it does special is unmap the decorative frame before
2333 *	unmapping the toplevel window.
2334 *
2335 * Results:
2336 *	None.
2337 *
2338 * Side effects:
2339 *	Unmaps the decorative frame and the window.
2340 *
2341 *--------------------------------------------------------------
2342 */
2343
2344void
2345TkWmUnmapWindow(winPtr)
2346    TkWindow *winPtr;		/* Top-level window that's about to
2347				 * be unmapped. */
2348{
2349    TkpWmSetState(winPtr, WithdrawnState);
2350}
2351
2352/*
2353 *----------------------------------------------------------------------
2354 *
2355 * TkpWmSetState --
2356 *
2357 *	Sets the window manager state for the wrapper window of a
2358 *	given toplevel window.
2359 *
2360 * Results:
2361 *	None.
2362 *
2363 * Side effects:
2364 *	May maximize, minimize, restore, or withdraw a window.
2365 *
2366 *----------------------------------------------------------------------
2367 */
2368
2369void
2370TkpWmSetState(winPtr, state)
2371     TkWindow *winPtr;		/* Toplevel window to operate on. */
2372     int state;			/* One of IconicState, ZoomState, NormalState,
2373				 * or WithdrawnState. */
2374{
2375    WmInfo *wmPtr = winPtr->wmInfoPtr;
2376    int cmd;
2377
2378    if (wmPtr->flags & WM_NEVER_MAPPED) {
2379	wmPtr->hints.initial_state = state;
2380	return;
2381    }
2382
2383    wmPtr->flags |= WM_SYNC_PENDING;
2384    if (state == WithdrawnState) {
2385	cmd = SW_HIDE;
2386    } else if (state == IconicState) {
2387	cmd = SW_SHOWMINNOACTIVE;
2388    } else if (state == NormalState) {
2389	cmd = SW_SHOWNOACTIVATE;
2390    } else if (state == ZoomState) {
2391	cmd = SW_SHOWMAXIMIZED;
2392    }
2393
2394    ShowWindow(wmPtr->wrapper, cmd);
2395    wmPtr->flags &= ~WM_SYNC_PENDING;
2396}
2397
2398/*
2399 *--------------------------------------------------------------
2400 *
2401 * TkWmDeadWindow --
2402 *
2403 *	This procedure is invoked when a top-level window is
2404 *	about to be deleted.  It cleans up the wm-related data
2405 *	structures for the window.
2406 *
2407 * Results:
2408 *	None.
2409 *
2410 * Side effects:
2411 *	The WmInfo structure for winPtr gets freed up.
2412 *
2413 *--------------------------------------------------------------
2414 */
2415
2416void
2417TkWmDeadWindow(winPtr)
2418    TkWindow *winPtr;		/* Top-level window that's being deleted. */
2419{
2420    register WmInfo *wmPtr = winPtr->wmInfoPtr;
2421    WmInfo *wmPtr2;
2422
2423    if (wmPtr == NULL) {
2424	return;
2425    }
2426
2427    /*
2428     * Clean up event related window info.
2429     */
2430
2431    if (winPtr->dispPtr->firstWmPtr == wmPtr) {
2432	winPtr->dispPtr->firstWmPtr = wmPtr->nextPtr;
2433    } else {
2434	register WmInfo *prevPtr;
2435	for (prevPtr = winPtr->dispPtr->firstWmPtr; ;
2436	     prevPtr = prevPtr->nextPtr) {
2437	    if (prevPtr == NULL) {
2438		Tcl_Panic("couldn't unlink window in TkWmDeadWindow");
2439	    }
2440	    if (prevPtr->nextPtr == wmPtr) {
2441		prevPtr->nextPtr = wmPtr->nextPtr;
2442		break;
2443	    }
2444	}
2445    }
2446
2447    /*
2448     * Reset all transient windows whose master is the dead window.
2449     */
2450
2451    for (wmPtr2 = winPtr->dispPtr->firstWmPtr; wmPtr2 != NULL;
2452	 wmPtr2 = wmPtr2->nextPtr) {
2453	if (wmPtr2->masterPtr == winPtr) {
2454	    wmPtr->numTransients--;
2455	    Tk_DeleteEventHandler((Tk_Window) wmPtr2->masterPtr,
2456	            VisibilityChangeMask|StructureNotifyMask,
2457	            WmWaitVisibilityOrMapProc, (ClientData) wmPtr2->winPtr);
2458	    wmPtr2->masterPtr = NULL;
2459	    if ((wmPtr2->wrapper != None)
2460		    && !(wmPtr2->flags & (WM_NEVER_MAPPED))) {
2461		UpdateWrapper(wmPtr2->winPtr);
2462	    }
2463	}
2464    }
2465    if (wmPtr->numTransients != 0)
2466        Tcl_Panic("numTransients should be 0");
2467
2468    if (wmPtr->title != NULL) {
2469	ckfree(wmPtr->title);
2470    }
2471    if (wmPtr->iconName != NULL) {
2472	ckfree(wmPtr->iconName);
2473    }
2474    if (wmPtr->hints.flags & IconPixmapHint) {
2475	Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap);
2476    }
2477    if (wmPtr->hints.flags & IconMaskHint) {
2478	Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_mask);
2479    }
2480    if (wmPtr->leaderName != NULL) {
2481	ckfree(wmPtr->leaderName);
2482    }
2483    if (wmPtr->icon != NULL) {
2484	wmPtr2 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
2485	wmPtr2->iconFor = NULL;
2486    }
2487    if (wmPtr->iconFor != NULL) {
2488	wmPtr2 = ((TkWindow *) wmPtr->iconFor)->wmInfoPtr;
2489	wmPtr2->icon = NULL;
2490	wmPtr2->hints.flags &= ~IconWindowHint;
2491    }
2492    while (wmPtr->protPtr != NULL) {
2493	ProtocolHandler *protPtr;
2494
2495	protPtr = wmPtr->protPtr;
2496	wmPtr->protPtr = protPtr->nextPtr;
2497	Tcl_EventuallyFree((ClientData) protPtr, TCL_DYNAMIC);
2498    }
2499    if (wmPtr->cmdArgv != NULL) {
2500	ckfree((char *) wmPtr->cmdArgv);
2501    }
2502    if (wmPtr->clientMachine != NULL) {
2503	ckfree((char *) wmPtr->clientMachine);
2504    }
2505    if (wmPtr->flags & WM_UPDATE_PENDING) {
2506	Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
2507    }
2508    if (wmPtr->masterPtr != NULL) {
2509	wmPtr2 = wmPtr->masterPtr->wmInfoPtr;
2510	/*
2511	 * If we had a master, tell them that we aren't tied
2512	 * to them anymore
2513	 */
2514	if (wmPtr2 != NULL) {
2515	    wmPtr2->numTransients--;
2516	}
2517	Tk_DeleteEventHandler((Tk_Window) wmPtr->masterPtr,
2518		VisibilityChangeMask|StructureNotifyMask,
2519		WmWaitVisibilityOrMapProc, (ClientData) winPtr);
2520	wmPtr->masterPtr = NULL;
2521    }
2522    if (wmPtr->crefObj != NULL) {
2523	Tcl_DecrRefCount(wmPtr->crefObj);
2524	wmPtr->crefObj = NULL;
2525    }
2526
2527    /*
2528     * Destroy the decorative frame window.
2529     */
2530
2531    if (!(winPtr->flags & TK_EMBEDDED)) {
2532	if (wmPtr->wrapper != NULL) {
2533	    DestroyWindow(wmPtr->wrapper);
2534	} else {
2535	    DestroyWindow(Tk_GetHWND(winPtr->window));
2536	}
2537    }
2538    if (wmPtr->iconPtr != NULL) {
2539	/*
2540	 * This may delete the icon resource data.  I believe we
2541	 * should do this after destroying the decorative frame,
2542	 * because the decorative frame is using this icon.
2543	 */
2544        DecrIconRefCount(wmPtr->iconPtr);
2545    }
2546
2547    ckfree((char *) wmPtr);
2548    winPtr->wmInfoPtr = NULL;
2549}
2550
2551/*
2552 *--------------------------------------------------------------
2553 *
2554 * TkWmSetClass --
2555 *
2556 *	This procedure is invoked whenever a top-level window's
2557 *	class is changed.  If the window has been mapped then this
2558 *	procedure updates the window manager property for the
2559 *	class.  If the window hasn't been mapped, the update is
2560 *	deferred until just before the first mapping.
2561 *
2562 * Results:
2563 *	None.
2564 *
2565 * Side effects:
2566 *	A window property may get updated.
2567 *
2568 *--------------------------------------------------------------
2569 */
2570
2571void
2572TkWmSetClass(winPtr)
2573    TkWindow *winPtr;		/* Newly-created top-level window. */
2574{
2575    return;
2576}
2577
2578/*
2579 *----------------------------------------------------------------------
2580 *
2581 * Tk_WmObjCmd --
2582 *
2583 *	This procedure is invoked to process the "wm" Tcl command.
2584 *	See the user documentation for details on what it does.
2585 *
2586 * Results:
2587 *	A standard Tcl result.
2588 *
2589 * Side effects:
2590 *	See the user documentation.
2591 *
2592 *----------------------------------------------------------------------
2593 */
2594
2595	/* ARGSUSED */
2596int
2597Tk_WmObjCmd(clientData, interp, objc, objv)
2598    ClientData clientData;	/* Main window associated with
2599				 * interpreter. */
2600    Tcl_Interp *interp;		/* Current interpreter. */
2601    int objc;			/* Number of arguments. */
2602    Tcl_Obj *CONST objv[];	/* Argument objects. */
2603{
2604    Tk_Window tkwin = (Tk_Window) clientData;
2605    static CONST char *optionStrings[] = {
2606	"aspect", "attributes", "client", "colormapwindows",
2607	"command", "deiconify", "focusmodel", "frame",
2608	"geometry", "grid", "group", "iconbitmap",
2609	"iconify", "iconmask", "iconname",
2610	"iconphoto", "iconposition",
2611	"iconwindow", "maxsize", "minsize", "overrideredirect",
2612        "positionfrom", "protocol", "resizable", "sizefrom",
2613        "stackorder", "state", "title", "transient",
2614	"withdraw", (char *) NULL };
2615    enum options {
2616        WMOPT_ASPECT, WMOPT_ATTRIBUTES, WMOPT_CLIENT, WMOPT_COLORMAPWINDOWS,
2617	WMOPT_COMMAND, WMOPT_DEICONIFY, WMOPT_FOCUSMODEL, WMOPT_FRAME,
2618	WMOPT_GEOMETRY, WMOPT_GRID, WMOPT_GROUP, WMOPT_ICONBITMAP,
2619	WMOPT_ICONIFY, WMOPT_ICONMASK, WMOPT_ICONNAME,
2620	WMOPT_ICONPHOTO, WMOPT_ICONPOSITION,
2621	WMOPT_ICONWINDOW, WMOPT_MAXSIZE, WMOPT_MINSIZE, WMOPT_OVERRIDEREDIRECT,
2622        WMOPT_POSITIONFROM, WMOPT_PROTOCOL, WMOPT_RESIZABLE, WMOPT_SIZEFROM,
2623        WMOPT_STACKORDER, WMOPT_STATE, WMOPT_TITLE, WMOPT_TRANSIENT,
2624	WMOPT_WITHDRAW };
2625    int index, length;
2626    char *argv1;
2627    TkWindow *winPtr;
2628    TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
2629
2630    if (objc < 2) {
2631	wrongNumArgs:
2632	Tcl_WrongNumArgs(interp, 1, objv, "option window ?arg ...?");
2633	return TCL_ERROR;
2634    }
2635
2636    argv1 = Tcl_GetStringFromObj(objv[1], &length);
2637    if ((argv1[0] == 't') && (strncmp(argv1, "tracing", length) == 0)
2638	    && (length >= 3)) {
2639	int wmTracing;
2640	if ((objc != 2) && (objc != 3)) {
2641	    Tcl_WrongNumArgs(interp, 2, objv, "?boolean?");
2642	    return TCL_ERROR;
2643	}
2644	if (objc == 2) {
2645	    Tcl_SetResult(interp,
2646		    ((dispPtr->flags & TK_DISPLAY_WM_TRACING) ? "on" : "off"),
2647		    TCL_STATIC);
2648	    return TCL_OK;
2649	}
2650	if (Tcl_GetBooleanFromObj(interp, objv[2], &wmTracing) != TCL_OK) {
2651	    return TCL_ERROR;
2652	}
2653	if (wmTracing) {
2654	    dispPtr->flags |= TK_DISPLAY_WM_TRACING;
2655	} else {
2656	    dispPtr->flags &= ~TK_DISPLAY_WM_TRACING;
2657	}
2658	return TCL_OK;
2659    }
2660
2661    if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0,
2662	    &index) != TCL_OK) {
2663	return TCL_ERROR;
2664    }
2665
2666    if (objc < 3) {
2667	goto wrongNumArgs;
2668    }
2669
2670    if (TkGetWindowFromObj(interp, tkwin, objv[2], (Tk_Window *) &winPtr)
2671	    != TCL_OK) {
2672	return TCL_ERROR;
2673    }
2674    if (!Tk_IsTopLevel(winPtr)) {
2675	Tcl_AppendResult(interp, "window \"", winPtr->pathName,
2676		"\" isn't a top-level window", (char *) NULL);
2677	return TCL_ERROR;
2678    }
2679
2680    switch ((enum options) index) {
2681      case WMOPT_ASPECT:
2682	return WmAspectCmd(tkwin, winPtr, interp, objc, objv);
2683      case WMOPT_ATTRIBUTES:
2684	return WmAttributesCmd(tkwin, winPtr, interp, objc, objv);
2685      case WMOPT_CLIENT:
2686	return WmClientCmd(tkwin, winPtr, interp, objc, objv);
2687      case WMOPT_COLORMAPWINDOWS:
2688	return WmColormapwindowsCmd(tkwin, winPtr, interp, objc, objv);
2689      case WMOPT_COMMAND:
2690	return WmCommandCmd(tkwin, winPtr, interp, objc, objv);
2691      case WMOPT_DEICONIFY:
2692	return WmDeiconifyCmd(tkwin, winPtr, interp, objc, objv);
2693      case WMOPT_FOCUSMODEL:
2694	return WmFocusmodelCmd(tkwin, winPtr, interp, objc, objv);
2695      case WMOPT_FRAME:
2696	return WmFrameCmd(tkwin, winPtr, interp, objc, objv);
2697      case WMOPT_GEOMETRY:
2698	return WmGeometryCmd(tkwin, winPtr, interp, objc, objv);
2699      case WMOPT_GRID:
2700	return WmGridCmd(tkwin, winPtr, interp, objc, objv);
2701      case WMOPT_GROUP:
2702	return WmGroupCmd(tkwin, winPtr, interp, objc, objv);
2703      case WMOPT_ICONBITMAP:
2704	return WmIconbitmapCmd(tkwin, winPtr, interp, objc, objv);
2705      case WMOPT_ICONIFY:
2706	return WmIconifyCmd(tkwin, winPtr, interp, objc, objv);
2707      case WMOPT_ICONMASK:
2708	return WmIconmaskCmd(tkwin, winPtr, interp, objc, objv);
2709      case WMOPT_ICONNAME:
2710	return WmIconnameCmd(tkwin, winPtr, interp, objc, objv);
2711      case WMOPT_ICONPHOTO:
2712        return WmIconphotoCmd(tkwin, winPtr, interp, objc, objv);
2713      case WMOPT_ICONPOSITION:
2714	return WmIconpositionCmd(tkwin, winPtr, interp, objc, objv);
2715      case WMOPT_ICONWINDOW:
2716	return WmIconwindowCmd(tkwin, winPtr, interp, objc, objv);
2717      case WMOPT_MAXSIZE:
2718	return WmMaxsizeCmd(tkwin, winPtr, interp, objc, objv);
2719      case WMOPT_MINSIZE:
2720	return WmMinsizeCmd(tkwin, winPtr, interp, objc, objv);
2721      case WMOPT_OVERRIDEREDIRECT:
2722	return WmOverrideredirectCmd(tkwin, winPtr, interp, objc, objv);
2723      case WMOPT_POSITIONFROM:
2724	return WmPositionfromCmd(tkwin, winPtr, interp, objc, objv);
2725      case WMOPT_PROTOCOL:
2726	return WmProtocolCmd(tkwin, winPtr, interp, objc, objv);
2727      case WMOPT_RESIZABLE:
2728	return WmResizableCmd(tkwin, winPtr, interp, objc, objv);
2729      case WMOPT_SIZEFROM:
2730	return WmSizefromCmd(tkwin, winPtr, interp, objc, objv);
2731      case WMOPT_STACKORDER:
2732	return WmStackorderCmd(tkwin, winPtr, interp, objc, objv);
2733      case WMOPT_STATE:
2734	return WmStateCmd(tkwin, winPtr, interp, objc, objv);
2735      case WMOPT_TITLE:
2736	return WmTitleCmd(tkwin, winPtr, interp, objc, objv);
2737      case WMOPT_TRANSIENT:
2738	return WmTransientCmd(tkwin, winPtr, interp, objc, objv);
2739      case WMOPT_WITHDRAW:
2740	return WmWithdrawCmd(tkwin, winPtr, interp, objc, objv);
2741    }
2742
2743    /* This should not happen */
2744    return TCL_ERROR;
2745}
2746
2747/*
2748 *----------------------------------------------------------------------
2749 *
2750 * WmAspectCmd --
2751 *
2752 *	This procedure is invoked to process the "wm aspect" Tcl command.
2753 *	See the user documentation for details on what it does.
2754 *
2755 * Results:
2756 *	A standard Tcl result.
2757 *
2758 * Side effects:
2759 *	See the user documentation.
2760 *
2761 *----------------------------------------------------------------------
2762 */
2763
2764static int
2765WmAspectCmd(tkwin, winPtr, interp, objc, objv)
2766    Tk_Window tkwin;		/* Main window of the application. */
2767    TkWindow *winPtr;           /* Toplevel to work with */
2768    Tcl_Interp *interp;		/* Current interpreter. */
2769    int objc;			/* Number of arguments. */
2770    Tcl_Obj *CONST objv[];	/* Argument objects. */
2771{
2772    register WmInfo *wmPtr = winPtr->wmInfoPtr;
2773    int numer1, denom1, numer2, denom2;
2774
2775    if ((objc != 3) && (objc != 7)) {
2776	Tcl_WrongNumArgs(interp, 2, objv,
2777		"window ?minNumer minDenom maxNumer maxDenom?");
2778	return TCL_ERROR;
2779    }
2780    if (objc == 3) {
2781	if (wmPtr->sizeHintsFlags & PAspect) {
2782	    char buf[TCL_INTEGER_SPACE * 4];
2783
2784	    sprintf(buf, "%d %d %d %d", wmPtr->minAspect.x,
2785		    wmPtr->minAspect.y, wmPtr->maxAspect.x,
2786		    wmPtr->maxAspect.y);
2787	    Tcl_SetResult(interp, buf, TCL_VOLATILE);
2788	}
2789	return TCL_OK;
2790    }
2791    if (*Tcl_GetString(objv[3]) == '\0') {
2792	wmPtr->sizeHintsFlags &= ~PAspect;
2793    } else {
2794	if ((Tcl_GetIntFromObj(interp, objv[3], &numer1) != TCL_OK)
2795		|| (Tcl_GetIntFromObj(interp, objv[4], &denom1) != TCL_OK)
2796		|| (Tcl_GetIntFromObj(interp, objv[5], &numer2) != TCL_OK)
2797		|| (Tcl_GetIntFromObj(interp, objv[6], &denom2) != TCL_OK)) {
2798	    return TCL_ERROR;
2799	}
2800	if ((numer1 <= 0) || (denom1 <= 0) || (numer2 <= 0) ||
2801		(denom2 <= 0)) {
2802	    Tcl_SetResult(interp, "aspect number can't be <= 0",
2803		    TCL_STATIC);
2804	    return TCL_ERROR;
2805	}
2806	wmPtr->minAspect.x = numer1;
2807	wmPtr->minAspect.y = denom1;
2808	wmPtr->maxAspect.x = numer2;
2809	wmPtr->maxAspect.y = denom2;
2810	wmPtr->sizeHintsFlags |= PAspect;
2811    }
2812    WmUpdateGeom(wmPtr, winPtr);
2813    return TCL_OK;
2814}
2815
2816/*
2817 *----------------------------------------------------------------------
2818 *
2819 * WmAttributesCmd --
2820 *
2821 *	This procedure is invoked to process the "wm attributes" Tcl command.
2822 *	See the user documentation for details on what it does.
2823 *
2824 * Results:
2825 *	A standard Tcl result.
2826 *
2827 * Side effects:
2828 *	See the user documentation.
2829 *
2830 *----------------------------------------------------------------------
2831 */
2832
2833static int
2834WmAttributesCmd(tkwin, winPtr, interp, objc, objv)
2835    Tk_Window tkwin;		/* Main window of the application. */
2836    TkWindow *winPtr;           /* Toplevel to work with */
2837    Tcl_Interp *interp;		/* Current interpreter. */
2838    int objc;			/* Number of arguments. */
2839    Tcl_Obj *CONST objv[];	/* Argument objects. */
2840{
2841    register WmInfo *wmPtr = winPtr->wmInfoPtr;
2842    LONG style, exStyle, styleBit, *stylePtr;
2843    char *string;
2844    int i, boolean, length, updatewrapper = 0;
2845
2846    if ((objc < 3) || ((objc > 5) && ((objc%2) == 0))) {
2847        configArgs:
2848	Tcl_WrongNumArgs(interp, 2, objv,
2849		"window"
2850		" ?-alpha ?double??"
2851		" ?-transparentcolor ?color??"
2852		" ?-disabled ?bool??"
2853		" ?-toolwindow ?bool??"
2854		" ?-topmost ?bool??");
2855	return TCL_ERROR;
2856    }
2857    exStyle = wmPtr->exStyleConfig;
2858    style   = wmPtr->styleConfig;
2859    if (objc == 3) {
2860	Tcl_Obj *objPtr = Tcl_NewObj();
2861	Tcl_ListObjAppendElement(NULL, objPtr,
2862		Tcl_NewStringObj("-alpha", -1));
2863	Tcl_ListObjAppendElement(NULL, objPtr, Tcl_NewDoubleObj(wmPtr->alpha));
2864	Tcl_ListObjAppendElement(NULL, objPtr,
2865		Tcl_NewStringObj("-transparentcolor", -1));
2866	Tcl_ListObjAppendElement(NULL, objPtr,
2867		wmPtr->crefObj ? wmPtr->crefObj : Tcl_NewObj());
2868	Tcl_ListObjAppendElement(NULL, objPtr,
2869		Tcl_NewStringObj("-disabled", -1));
2870	Tcl_ListObjAppendElement(NULL, objPtr,
2871		Tcl_NewBooleanObj((style & WS_DISABLED)));
2872	Tcl_ListObjAppendElement(NULL, objPtr,
2873		Tcl_NewStringObj("-toolwindow", -1));
2874	Tcl_ListObjAppendElement(NULL, objPtr,
2875		Tcl_NewBooleanObj((exStyle & WS_EX_TOOLWINDOW)));
2876	Tcl_ListObjAppendElement(NULL, objPtr,
2877		Tcl_NewStringObj("-topmost", -1));
2878	Tcl_ListObjAppendElement(NULL, objPtr,
2879		Tcl_NewBooleanObj((exStyle & WS_EX_TOPMOST)));
2880	Tcl_SetObjResult(interp, objPtr);
2881	return TCL_OK;
2882    }
2883    for (i = 3; i < objc; i += 2) {
2884	string = Tcl_GetStringFromObj(objv[i], &length);
2885	if ((length < 2) || (string[0] != '-')) {
2886	    goto configArgs;
2887	}
2888	if (strncmp(string, "-disabled", length) == 0) {
2889	    stylePtr = &style;
2890	    styleBit = WS_DISABLED;
2891	} else if ((strncmp(string, "-alpha", length) == 0)
2892		|| ((length > 2) && (strncmp(string, "-transparentcolor",
2893					     length) == 0))) {
2894	    stylePtr = &exStyle;
2895	    styleBit = WS_EX_LAYERED;
2896	} else if ((length > 3)
2897		   && (strncmp(string, "-toolwindow", length) == 0)) {
2898	    stylePtr = &exStyle;
2899	    styleBit = WS_EX_TOOLWINDOW;
2900	    if (objc != 4) {
2901		/*
2902		 * Changes to toolwindow style require an update
2903		 */
2904		updatewrapper = 1;
2905	    }
2906	} else if ((length > 3)
2907		   && (strncmp(string, "-topmost", length) == 0)) {
2908	    stylePtr = &exStyle;
2909	    styleBit = WS_EX_TOPMOST;
2910	    if ((i < objc-1) && (winPtr->flags & TK_EMBEDDED)) {
2911		Tcl_AppendResult(interp, "can't set topmost flag on ",
2912			winPtr->pathName, ": it is an embedded window",
2913			(char *) NULL);
2914		return TCL_ERROR;
2915	    }
2916	} else {
2917	    goto configArgs;
2918	}
2919	if (styleBit == WS_EX_LAYERED) {
2920	    if (objc == 4) {
2921		if (string[1] == 'a') {		/* -alpha */
2922		    Tcl_SetObjResult(interp, Tcl_NewDoubleObj(wmPtr->alpha));
2923		} else {			/* -transparentcolor */
2924		    Tcl_SetObjResult(interp,
2925			    wmPtr->crefObj ? wmPtr->crefObj : Tcl_NewObj());
2926		}
2927	    } else {
2928		if (string[1] == 'a') {		/* -alpha */
2929		    double dval;
2930
2931		    if (Tcl_GetDoubleFromObj(interp, objv[i+1], &dval)
2932			    != TCL_OK) {
2933			return TCL_ERROR;
2934		    }
2935
2936		    /*
2937		     * The user should give (transparent) 0 .. 1.0 (opaque),
2938		     * but we ignore the setting of this (it will always be 1)
2939		     * in the case that the API is not available.
2940		     */
2941		    if (dval < 0.0) {
2942			dval = 0;
2943		    } else if (dval > 1.0) {
2944			dval = 1;
2945		    }
2946		    wmPtr->alpha = dval;
2947		} else {			/* -transparentcolor */
2948		    char *crefstr = Tcl_GetStringFromObj(objv[i+1], &length);
2949
2950		    if (length == 0) {
2951			/* reset to no transparent color */
2952			if (wmPtr->crefObj) {
2953			    Tcl_DecrRefCount(wmPtr->crefObj);
2954			    wmPtr->crefObj = NULL;
2955			}
2956		    } else {
2957			XColor *cPtr =
2958			    Tk_GetColor(interp, tkwin, Tk_GetUid(crefstr));
2959			if (cPtr == NULL) {
2960			    return TCL_ERROR;
2961			}
2962
2963			if (wmPtr->crefObj) {
2964			    Tcl_DecrRefCount(wmPtr->crefObj);
2965			}
2966			wmPtr->crefObj = objv[i+1];
2967			Tcl_IncrRefCount(wmPtr->crefObj);
2968			wmPtr->colorref = RGB((BYTE) (cPtr->red >> 8),
2969				(BYTE) (cPtr->green >> 8),
2970				(BYTE) (cPtr->blue >> 8));
2971			Tk_FreeColor(cPtr);
2972		    }
2973		}
2974
2975		/*
2976		 * Only ever add the WS_EX_LAYERED bit, as it can cause
2977		 * flashing to change this window style.  This allows things
2978		 * like fading tooltips to avoid flash ugliness without
2979		 * forcing all window to be layered.
2980		 */
2981		if ((wmPtr->alpha < 1.0) || (wmPtr->crefObj != NULL)) {
2982		    *stylePtr |= styleBit;
2983		}
2984		if ((setLayeredWindowAttributesProc != NULL)
2985			&& (wmPtr->wrapper != NULL)) {
2986		    /*
2987		     * Set the window directly regardless of UpdateWrapper.
2988		     * The user supplies a double from [0..1], but Windows
2989		     * wants an int (transparent) 0..255 (opaque), so do the
2990		     * translation.  Add the 0.5 to round the value.
2991		     */
2992		    if (!(wmPtr->exStyleConfig & WS_EX_LAYERED)) {
2993#ifdef _WIN64
2994			SetWindowLongPtr(wmPtr->wrapper, GWL_EXSTYLE,
2995				*stylePtr);
2996#else
2997			SetWindowLong(wmPtr->wrapper, GWL_EXSTYLE,
2998				*stylePtr);
2999#endif
3000		    }
3001		    setLayeredWindowAttributesProc((HWND) wmPtr->wrapper,
3002			    wmPtr->colorref, (BYTE) (wmPtr->alpha * 255 + 0.5),
3003			    LWA_ALPHA | (wmPtr->crefObj ? LWA_COLORKEY : 0));
3004		}
3005	    }
3006	} else {
3007	    if ((i < objc-1) &&
3008		    (Tcl_GetBooleanFromObj(interp, objv[i+1], &boolean)
3009			    != TCL_OK)) {
3010		return TCL_ERROR;
3011	    }
3012	    if (objc == 4) {
3013		Tcl_SetIntObj(Tcl_GetObjResult(interp),
3014			((*stylePtr & styleBit) != 0));
3015	    } else if (boolean) {
3016		*stylePtr |= styleBit;
3017	    } else {
3018		*stylePtr &= ~styleBit;
3019	    }
3020	}
3021	if ((styleBit == WS_EX_TOPMOST) && (wmPtr->wrapper != NULL)) {
3022	    /*
3023	     * Force the topmost position aspect to ensure that switching
3024	     * between (no)topmost reflects properly when rewrapped.
3025	     */
3026	    SetWindowPos(wmPtr->wrapper,
3027		    ((exStyle & WS_EX_TOPMOST) ?
3028			    HWND_TOPMOST : HWND_NOTOPMOST), 0, 0, 0, 0,
3029		    SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_NOSENDCHANGING
3030		    |SWP_NOOWNERZORDER);
3031	}
3032    }
3033    if (wmPtr->styleConfig != style) {
3034	/*
3035	 * Currently this means only WS_DISABLED changed, which we can
3036	 * effect with EnableWindow.
3037	 */
3038	wmPtr->styleConfig = style;
3039	if ((wmPtr->exStyleConfig == exStyle)
3040		&& !(wmPtr->flags & WM_NEVER_MAPPED)) {
3041	    EnableWindow(wmPtr->wrapper, (style & WS_DISABLED) ? 0 : 1);
3042	}
3043    }
3044    if (wmPtr->exStyleConfig != exStyle) {
3045	wmPtr->exStyleConfig = exStyle;
3046	if (updatewrapper) {
3047	    /*
3048	     * UpdateWrapper ensure that all effects are properly handled,
3049	     * such as TOOLWINDOW disappearing from the taskbar.
3050	     */
3051	    if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
3052		UpdateWrapper(winPtr);
3053	    }
3054	}
3055    }
3056    return TCL_OK;
3057}
3058
3059/*
3060 *----------------------------------------------------------------------
3061 *
3062 * WmClientCmd --
3063 *
3064 *	This procedure is invoked to process the "wm client" Tcl command.
3065 *	See the user documentation for details on what it does.
3066 *
3067 * Results:
3068 *	A standard Tcl result.
3069 *
3070 * Side effects:
3071 *	See the user documentation.
3072 *
3073 *----------------------------------------------------------------------
3074 */
3075
3076static int
3077WmClientCmd(tkwin, winPtr, interp, objc, objv)
3078    Tk_Window tkwin;		/* Main window of the application. */
3079    TkWindow *winPtr;           /* Toplevel to work with */
3080    Tcl_Interp *interp;		/* Current interpreter. */
3081    int objc;			/* Number of arguments. */
3082    Tcl_Obj *CONST objv[];	/* Argument objects. */
3083{
3084    register WmInfo *wmPtr = winPtr->wmInfoPtr;
3085    char *argv3;
3086    int length;
3087
3088    if ((objc != 3) && (objc != 4)) {
3089	Tcl_WrongNumArgs(interp, 2, objv, "window ?name?");
3090	return TCL_ERROR;
3091    }
3092    if (objc == 3) {
3093	if (wmPtr->clientMachine != NULL) {
3094	    Tcl_SetResult(interp, wmPtr->clientMachine, TCL_STATIC);
3095	}
3096	return TCL_OK;
3097    }
3098    argv3 = Tcl_GetStringFromObj(objv[3], &length);
3099    if (argv3[0] == 0) {
3100	if (wmPtr->clientMachine != NULL) {
3101	    ckfree((char *) wmPtr->clientMachine);
3102	    wmPtr->clientMachine = NULL;
3103	    if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
3104		XDeleteProperty(winPtr->display, winPtr->window,
3105			Tk_InternAtom((Tk_Window) winPtr,
3106				"WM_CLIENT_MACHINE"));
3107	    }
3108	}
3109	return TCL_OK;
3110    }
3111    if (wmPtr->clientMachine != NULL) {
3112	ckfree((char *) wmPtr->clientMachine);
3113    }
3114    wmPtr->clientMachine = (char *)
3115	    ckalloc((unsigned) (length + 1));
3116    strcpy(wmPtr->clientMachine, argv3);
3117    if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
3118	XTextProperty textProp;
3119	if (XStringListToTextProperty(&wmPtr->clientMachine, 1, &textProp)
3120		!= 0) {
3121	    XSetWMClientMachine(winPtr->display, winPtr->window,
3122		    &textProp);
3123	    XFree((char *) textProp.value);
3124	}
3125    }
3126    return TCL_OK;
3127}
3128
3129/*
3130 *----------------------------------------------------------------------
3131 *
3132 * WmColormapwindowsCmd --
3133 *
3134 *	This procedure is invoked to process the "wm colormapwindows"
3135 *	Tcl command.
3136 *	See the user documentation for details on what it does.
3137 *
3138 * Results:
3139 *	A standard Tcl result.
3140 *
3141 * Side effects:
3142 *	See the user documentation.
3143 *
3144 *----------------------------------------------------------------------
3145 */
3146
3147static int
3148WmColormapwindowsCmd(tkwin, winPtr, interp, objc, objv)
3149    Tk_Window tkwin;		/* Main window of the application. */
3150    TkWindow *winPtr;           /* Toplevel to work with */
3151    Tcl_Interp *interp;		/* Current interpreter. */
3152    int objc;			/* Number of arguments. */
3153    Tcl_Obj *CONST objv[];	/* Argument objects. */
3154{
3155    register WmInfo *wmPtr = winPtr->wmInfoPtr;
3156    TkWindow **cmapList;
3157    TkWindow *winPtr2;
3158    int i, windowObjc, gotToplevel;
3159    Tcl_Obj **windowObjv;
3160
3161    if ((objc != 3) && (objc != 4)) {
3162	Tcl_WrongNumArgs(interp, 2, objv, "window ?windowList?");
3163	return TCL_ERROR;
3164    }
3165    if (objc == 3) {
3166	Tk_MakeWindowExist((Tk_Window) winPtr);
3167	for (i = 0; i < wmPtr->cmapCount; i++) {
3168	    if ((i == (wmPtr->cmapCount-1))
3169		    && (wmPtr->flags & WM_ADDED_TOPLEVEL_COLORMAP)) {
3170		break;
3171	    }
3172	    Tcl_AppendElement(interp, wmPtr->cmapList[i]->pathName);
3173	}
3174	return TCL_OK;
3175    }
3176    if (Tcl_ListObjGetElements(interp, objv[3], &windowObjc, &windowObjv)
3177	    != TCL_OK) {
3178	return TCL_ERROR;
3179    }
3180    cmapList = (TkWindow **) ckalloc((unsigned)
3181	    ((windowObjc+1)*sizeof(TkWindow*)));
3182    gotToplevel = 0;
3183    for (i = 0; i < windowObjc; i++) {
3184	if (TkGetWindowFromObj(interp, tkwin, windowObjv[i],
3185		(Tk_Window *) &winPtr2) != TCL_OK)
3186	{
3187	    ckfree((char *) cmapList);
3188	    return TCL_ERROR;
3189	}
3190	if (winPtr2 == winPtr) {
3191	    gotToplevel = 1;
3192	}
3193	if (winPtr2->window == None) {
3194	    Tk_MakeWindowExist((Tk_Window) winPtr2);
3195	}
3196	cmapList[i] = winPtr2;
3197    }
3198    if (!gotToplevel) {
3199	wmPtr->flags |= WM_ADDED_TOPLEVEL_COLORMAP;
3200	cmapList[windowObjc] = winPtr;
3201	windowObjc++;
3202    } else {
3203	wmPtr->flags &= ~WM_ADDED_TOPLEVEL_COLORMAP;
3204    }
3205    wmPtr->flags |= WM_COLORMAPS_EXPLICIT;
3206    if (wmPtr->cmapList != NULL) {
3207	ckfree((char *)wmPtr->cmapList);
3208    }
3209    wmPtr->cmapList = cmapList;
3210    wmPtr->cmapCount = windowObjc;
3211
3212    /*
3213     * Now we need to force the updated colormaps to be installed.
3214     */
3215
3216    if (wmPtr == winPtr->dispPtr->foregroundWmPtr) {
3217	InstallColormaps(wmPtr->wrapper, WM_QUERYNEWPALETTE, 1);
3218    } else {
3219	InstallColormaps(wmPtr->wrapper, WM_PALETTECHANGED, 0);
3220    }
3221    return TCL_OK;
3222}
3223
3224/*
3225 *----------------------------------------------------------------------
3226 *
3227 * WmCommandCmd --
3228 *
3229 *	This procedure is invoked to process the "wm command" Tcl command.
3230 *	See the user documentation for details on what it does.
3231 *
3232 * Results:
3233 *	A standard Tcl result.
3234 *
3235 * Side effects:
3236 *	See the user documentation.
3237 *
3238 *----------------------------------------------------------------------
3239 */
3240
3241static int
3242WmCommandCmd(tkwin, winPtr, interp, objc, objv)
3243    Tk_Window tkwin;		/* Main window of the application. */
3244    TkWindow *winPtr;           /* Toplevel to work with */
3245    Tcl_Interp *interp;		/* Current interpreter. */
3246    int objc;			/* Number of arguments. */
3247    Tcl_Obj *CONST objv[];	/* Argument objects. */
3248{
3249    register WmInfo *wmPtr = winPtr->wmInfoPtr;
3250    char *argv3;
3251    int cmdArgc;
3252    CONST char **cmdArgv;
3253
3254    if ((objc != 3) && (objc != 4)) {
3255	Tcl_WrongNumArgs(interp, 2, objv, "window ?value?");
3256	return TCL_ERROR;
3257    }
3258    if (objc == 3) {
3259	if (wmPtr->cmdArgv != NULL) {
3260	    Tcl_SetResult(interp,
3261		    Tcl_Merge(wmPtr->cmdArgc, wmPtr->cmdArgv),
3262		    TCL_DYNAMIC);
3263	}
3264	return TCL_OK;
3265    }
3266    argv3 = Tcl_GetString(objv[3]);
3267    if (argv3[0] == 0) {
3268	if (wmPtr->cmdArgv != NULL) {
3269	    ckfree((char *) wmPtr->cmdArgv);
3270	    wmPtr->cmdArgv = NULL;
3271	    if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
3272		XDeleteProperty(winPtr->display, winPtr->window,
3273			Tk_InternAtom((Tk_Window) winPtr, "WM_COMMAND"));
3274	    }
3275	}
3276	return TCL_OK;
3277    }
3278    if (Tcl_SplitList(interp, argv3, &cmdArgc, &cmdArgv) != TCL_OK) {
3279	return TCL_ERROR;
3280    }
3281    if (wmPtr->cmdArgv != NULL) {
3282	ckfree((char *) wmPtr->cmdArgv);
3283    }
3284    wmPtr->cmdArgc = cmdArgc;
3285    wmPtr->cmdArgv = cmdArgv;
3286    if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
3287	XSetCommand(winPtr->display, winPtr->window, cmdArgv, cmdArgc);
3288    }
3289    return TCL_OK;
3290}
3291
3292/*
3293 *----------------------------------------------------------------------
3294 *
3295 * WmDeiconifyCmd --
3296 *
3297 *	This procedure is invoked to process the "wm deiconify" Tcl command.
3298 *	See the user documentation for details on what it does.
3299 *
3300 * Results:
3301 *	A standard Tcl result.
3302 *
3303 * Side effects:
3304 *	See the user documentation.
3305 *
3306 *----------------------------------------------------------------------
3307 */
3308
3309static int
3310WmDeiconifyCmd(tkwin, winPtr, interp, objc, objv)
3311    Tk_Window tkwin;		/* Main window of the application. */
3312    TkWindow *winPtr;           /* Toplevel to work with */
3313    Tcl_Interp *interp;		/* Current interpreter. */
3314    int objc;			/* Number of arguments. */
3315    Tcl_Obj *CONST objv[];	/* Argument objects. */
3316{
3317    register WmInfo *wmPtr = winPtr->wmInfoPtr;
3318
3319    if (objc != 3) {
3320	Tcl_WrongNumArgs(interp, 2, objv, "window");
3321	return TCL_ERROR;
3322    }
3323    if (wmPtr->iconFor != NULL) {
3324	Tcl_AppendResult(interp, "can't deiconify ", Tcl_GetString(objv[2]),
3325		": it is an icon for ", Tk_PathName(wmPtr->iconFor),
3326		(char *) NULL);
3327	return TCL_ERROR;
3328    }
3329    if (winPtr->flags & TK_EMBEDDED) {
3330	Tcl_AppendResult(interp, "can't deiconify ", winPtr->pathName,
3331		": it is an embedded window", (char *) NULL);
3332	return TCL_ERROR;
3333    }
3334
3335    wmPtr->flags &= ~WM_WITHDRAWN;
3336
3337    /*
3338     * If WM_UPDATE_PENDING is true, a pending UpdateGeometryInfo may
3339     * need to be called first to update a withdrawn toplevel's geometry
3340     * before it is deiconified by TkpWmSetState.
3341     * Don't bother if we've never been mapped.
3342     */
3343    if ((wmPtr->flags & WM_UPDATE_PENDING) &&
3344	    !(wmPtr->flags & WM_NEVER_MAPPED)) {
3345	Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
3346	UpdateGeometryInfo((ClientData) winPtr);
3347    }
3348
3349    /*
3350     * If we were in the ZoomState (maximized), 'wm deiconify'
3351     * should not cause the window to shrink
3352     */
3353    if (wmPtr->hints.initial_state == ZoomState) {
3354	TkpWmSetState(winPtr, ZoomState);
3355    } else {
3356	TkpWmSetState(winPtr, NormalState);
3357    }
3358
3359    /*
3360     * An unmapped window will be mapped at idle time
3361     * by a call to MapFrame. That calls CreateWrapper
3362     * which sets the focus and raises the window.
3363     */
3364    if (wmPtr->flags & WM_NEVER_MAPPED) {
3365	return TCL_OK;
3366    }
3367
3368    /*
3369     * Follow Windows-like style here, raising the window to the top.
3370     */
3371    TkWmRestackToplevel(winPtr, Above, NULL);
3372    if (!(Tk_Attributes((Tk_Window) winPtr)->override_redirect)) {
3373	TkSetFocusWin(winPtr, 1);
3374    }
3375    return TCL_OK;
3376}
3377
3378/*
3379 *----------------------------------------------------------------------
3380 *
3381 * WmFocusmodelCmd --
3382 *
3383 *	This procedure is invoked to process the "wm focusmodel" Tcl command.
3384 *	See the user documentation for details on what it does.
3385 *
3386 * Results:
3387 *	A standard Tcl result.
3388 *
3389 * Side effects:
3390 *	See the user documentation.
3391 *
3392 *----------------------------------------------------------------------
3393 */
3394
3395static int
3396WmFocusmodelCmd(tkwin, winPtr, interp, objc, objv)
3397    Tk_Window tkwin;		/* Main window of the application. */
3398    TkWindow *winPtr;           /* Toplevel to work with */
3399    Tcl_Interp *interp;		/* Current interpreter. */
3400    int objc;			/* Number of arguments. */
3401    Tcl_Obj *CONST objv[];	/* Argument objects. */
3402{
3403    register WmInfo *wmPtr = winPtr->wmInfoPtr;
3404    static CONST char *optionStrings[] = {
3405	"active", "passive", (char *) NULL };
3406    enum options {
3407	OPT_ACTIVE, OPT_PASSIVE };
3408    int index;
3409
3410    if ((objc != 3) && (objc != 4)) {
3411	Tcl_WrongNumArgs(interp, 2, objv, "window ?active|passive?");
3412	return TCL_ERROR;
3413    }
3414    if (objc == 3) {
3415	Tcl_SetResult(interp, (wmPtr->hints.input ? "passive" : "active"),
3416		TCL_STATIC);
3417	return TCL_OK;
3418    }
3419
3420    if (Tcl_GetIndexFromObj(interp, objv[3], optionStrings, "argument", 0,
3421	    &index) != TCL_OK) {
3422	return TCL_ERROR;
3423    }
3424    if (index == OPT_ACTIVE) {
3425	wmPtr->hints.input = False;
3426    } else { /* OPT_PASSIVE */
3427	wmPtr->hints.input = True;
3428    }
3429    return TCL_OK;
3430}
3431
3432/*
3433 *----------------------------------------------------------------------
3434 *
3435 * WmFrameCmd --
3436 *
3437 *	This procedure is invoked to process the "wm frame" Tcl command.
3438 *	See the user documentation for details on what it does.
3439 *
3440 * Results:
3441 *	A standard Tcl result.
3442 *
3443 * Side effects:
3444 *	See the user documentation.
3445 *
3446 *----------------------------------------------------------------------
3447 */
3448
3449static int
3450WmFrameCmd(tkwin, winPtr, interp, objc, objv)
3451    Tk_Window tkwin;		/* Main window of the application. */
3452    TkWindow *winPtr;           /* Toplevel to work with */
3453    Tcl_Interp *interp;		/* Current interpreter. */
3454    int objc;			/* Number of arguments. */
3455    Tcl_Obj *CONST objv[];	/* Argument objects. */
3456{
3457    register WmInfo *wmPtr = winPtr->wmInfoPtr;
3458    HWND hwnd;
3459    char buf[TCL_INTEGER_SPACE];
3460
3461    if (objc != 3) {
3462	Tcl_WrongNumArgs(interp, 2, objv, "window");
3463	return TCL_ERROR;
3464    }
3465    if (Tk_WindowId((Tk_Window) winPtr) == None) {
3466	Tk_MakeWindowExist((Tk_Window) winPtr);
3467    }
3468    hwnd = wmPtr->wrapper;
3469    if (hwnd == NULL) {
3470	hwnd = Tk_GetHWND(Tk_WindowId((Tk_Window) winPtr));
3471    }
3472    sprintf(buf, "0x%x", (unsigned int) hwnd);
3473    Tcl_SetResult(interp, buf, TCL_VOLATILE);
3474    return TCL_OK;
3475}
3476
3477/*
3478 *----------------------------------------------------------------------
3479 *
3480 * WmGeometryCmd --
3481 *
3482 *	This procedure is invoked to process the "wm geometry" Tcl command.
3483 *	See the user documentation for details on what it does.
3484 *
3485 * Results:
3486 *	A standard Tcl result.
3487 *
3488 * Side effects:
3489 *	See the user documentation.
3490 *
3491 *----------------------------------------------------------------------
3492 */
3493
3494static int
3495WmGeometryCmd(tkwin, winPtr, interp, objc, objv)
3496    Tk_Window tkwin;		/* Main window of the application. */
3497    TkWindow *winPtr;           /* Toplevel to work with */
3498    Tcl_Interp *interp;		/* Current interpreter. */
3499    int objc;			/* Number of arguments. */
3500    Tcl_Obj *CONST objv[];	/* Argument objects. */
3501{
3502    register WmInfo *wmPtr = winPtr->wmInfoPtr;
3503    char xSign, ySign;
3504    int width, height;
3505    char *argv3;
3506
3507    if ((objc != 3) && (objc != 4)) {
3508	Tcl_WrongNumArgs(interp, 2, objv, "window ?newGeometry?");
3509	return TCL_ERROR;
3510    }
3511    if (objc == 3) {
3512	char buf[16 + TCL_INTEGER_SPACE * 4];
3513
3514	xSign = (wmPtr->flags & WM_NEGATIVE_X) ? '-' : '+';
3515	ySign = (wmPtr->flags & WM_NEGATIVE_Y) ? '-' : '+';
3516	if (wmPtr->gridWin != NULL) {
3517	    width = wmPtr->reqGridWidth + (winPtr->changes.width
3518		    - winPtr->reqWidth)/wmPtr->widthInc;
3519	    height = wmPtr->reqGridHeight + (winPtr->changes.height
3520		    - winPtr->reqHeight)/wmPtr->heightInc;
3521	} else {
3522	    width = winPtr->changes.width;
3523	    height = winPtr->changes.height;
3524	}
3525	sprintf(buf, "%dx%d%c%d%c%d", width, height, xSign, wmPtr->x,
3526		ySign, wmPtr->y);
3527	Tcl_SetResult(interp, buf, TCL_VOLATILE);
3528	return TCL_OK;
3529    }
3530    argv3 = Tcl_GetString(objv[3]);
3531    if (*argv3 == '\0') {
3532	wmPtr->width = -1;
3533	wmPtr->height = -1;
3534	WmUpdateGeom(wmPtr, winPtr);
3535	return TCL_OK;
3536    }
3537    return ParseGeometry(interp, argv3, winPtr);
3538}
3539
3540/*
3541 *----------------------------------------------------------------------
3542 *
3543 * WmGridCmd --
3544 *
3545 *	This procedure is invoked to process the "wm grid" Tcl command.
3546 *	See the user documentation for details on what it does.
3547 *
3548 * Results:
3549 *	A standard Tcl result.
3550 *
3551 * Side effects:
3552 *	See the user documentation.
3553 *
3554 *----------------------------------------------------------------------
3555 */
3556
3557static int
3558WmGridCmd(tkwin, winPtr, interp, objc, objv)
3559    Tk_Window tkwin;		/* Main window of the application. */
3560    TkWindow *winPtr;           /* Toplevel to work with */
3561    Tcl_Interp *interp;		/* Current interpreter. */
3562    int objc;			/* Number of arguments. */
3563    Tcl_Obj *CONST objv[];	/* Argument objects. */
3564{
3565    register WmInfo *wmPtr = winPtr->wmInfoPtr;
3566    int reqWidth, reqHeight, widthInc, heightInc;
3567
3568    if ((objc != 3) && (objc != 7)) {
3569	Tcl_WrongNumArgs(interp, 2, objv,
3570		"window ?baseWidth baseHeight widthInc heightInc?");
3571	return TCL_ERROR;
3572    }
3573    if (objc == 3) {
3574	if (wmPtr->sizeHintsFlags & PBaseSize) {
3575	    char buf[TCL_INTEGER_SPACE * 4];
3576
3577	    sprintf(buf, "%d %d %d %d", wmPtr->reqGridWidth,
3578		    wmPtr->reqGridHeight, wmPtr->widthInc,
3579		    wmPtr->heightInc);
3580	    Tcl_SetResult(interp, buf, TCL_VOLATILE);
3581	}
3582	return TCL_OK;
3583    }
3584    if (*Tcl_GetString(objv[3]) == '\0') {
3585	/*
3586	 * Turn off gridding and reset the width and height
3587	 * to make sense as ungridded numbers.
3588	 */
3589
3590	wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc);
3591	if (wmPtr->width != -1) {
3592	    wmPtr->width = winPtr->reqWidth + (wmPtr->width
3593		    - wmPtr->reqGridWidth)*wmPtr->widthInc;
3594	    wmPtr->height = winPtr->reqHeight + (wmPtr->height
3595		    - wmPtr->reqGridHeight)*wmPtr->heightInc;
3596	}
3597	wmPtr->widthInc = 1;
3598	wmPtr->heightInc = 1;
3599    } else {
3600	if ((Tcl_GetIntFromObj(interp, objv[3], &reqWidth) != TCL_OK)
3601		|| (Tcl_GetIntFromObj(interp, objv[4], &reqHeight) != TCL_OK)
3602		|| (Tcl_GetIntFromObj(interp, objv[5], &widthInc) != TCL_OK)
3603		|| (Tcl_GetIntFromObj(interp, objv[6], &heightInc) != TCL_OK)) {
3604	    return TCL_ERROR;
3605	}
3606	if (reqWidth < 0) {
3607	    Tcl_SetResult(interp, "baseWidth can't be < 0", TCL_STATIC);
3608	    return TCL_ERROR;
3609	}
3610	if (reqHeight < 0) {
3611	    Tcl_SetResult(interp, "baseHeight can't be < 0", TCL_STATIC);
3612	    return TCL_ERROR;
3613	}
3614	if (widthInc <= 0) {
3615	    Tcl_SetResult(interp, "widthInc can't be <= 0", TCL_STATIC);
3616	    return TCL_ERROR;
3617	}
3618	if (heightInc <= 0) {
3619	    Tcl_SetResult(interp, "heightInc can't be <= 0", TCL_STATIC);
3620	    return TCL_ERROR;
3621	}
3622	Tk_SetGrid((Tk_Window) winPtr, reqWidth, reqHeight, widthInc,
3623		heightInc);
3624    }
3625    WmUpdateGeom(wmPtr, winPtr);
3626    return TCL_OK;
3627}
3628
3629/*
3630 *----------------------------------------------------------------------
3631 *
3632 * WmGroupCmd --
3633 *
3634 *	This procedure is invoked to process the "wm group" Tcl command.
3635 *	See the user documentation for details on what it does.
3636 *
3637 * Results:
3638 *	A standard Tcl result.
3639 *
3640 * Side effects:
3641 *	See the user documentation.
3642 *
3643 *----------------------------------------------------------------------
3644 */
3645
3646static int
3647WmGroupCmd(tkwin, winPtr, interp, objc, objv)
3648    Tk_Window tkwin;		/* Main window of the application. */
3649    TkWindow *winPtr;           /* Toplevel to work with */
3650    Tcl_Interp *interp;		/* Current interpreter. */
3651    int objc;			/* Number of arguments. */
3652    Tcl_Obj *CONST objv[];	/* Argument objects. */
3653{
3654    register WmInfo *wmPtr = winPtr->wmInfoPtr;
3655    Tk_Window tkwin2;
3656    char *argv3;
3657    int length;
3658
3659    if ((objc != 3) && (objc != 4)) {
3660	Tcl_WrongNumArgs(interp, 2, objv, "window ?pathName?");
3661	return TCL_ERROR;
3662    }
3663    if (objc == 3) {
3664	if (wmPtr->hints.flags & WindowGroupHint) {
3665	    Tcl_SetResult(interp, wmPtr->leaderName, TCL_STATIC);
3666	}
3667	return TCL_OK;
3668    }
3669    argv3 = Tcl_GetStringFromObj(objv[3], &length);
3670    if (*argv3 == '\0') {
3671	wmPtr->hints.flags &= ~WindowGroupHint;
3672	if (wmPtr->leaderName != NULL) {
3673	    ckfree(wmPtr->leaderName);
3674	}
3675	wmPtr->leaderName = NULL;
3676    } else {
3677	if (TkGetWindowFromObj(interp, tkwin, objv[3], &tkwin2) != TCL_OK) {
3678	    return TCL_ERROR;
3679	}
3680	Tk_MakeWindowExist(tkwin2);
3681	if (wmPtr->leaderName != NULL) {
3682	    ckfree(wmPtr->leaderName);
3683	}
3684	wmPtr->hints.window_group = Tk_WindowId(tkwin2);
3685	wmPtr->hints.flags |= WindowGroupHint;
3686	wmPtr->leaderName = ckalloc((unsigned) (length + 1));
3687	strcpy(wmPtr->leaderName, argv3);
3688    }
3689    return TCL_OK;
3690}
3691
3692/*
3693 *----------------------------------------------------------------------
3694 *
3695 * WmIconbitmapCmd --
3696 *
3697 *	This procedure is invoked to process the "wm iconbitmap" Tcl command.
3698 *	See the user documentation for details on what it does.
3699 *
3700 * Results:
3701 *	A standard Tcl result.
3702 *
3703 * Side effects:
3704 *	See the user documentation.
3705 *
3706 *----------------------------------------------------------------------
3707 */
3708
3709static int
3710WmIconbitmapCmd(tkwin, winPtr, interp, objc, objv)
3711    Tk_Window tkwin;		/* Main window of the application. */
3712    TkWindow *winPtr;           /* Toplevel to work with */
3713    Tcl_Interp *interp;		/* Current interpreter. */
3714    int objc;			/* Number of arguments. */
3715    Tcl_Obj *CONST objv[];	/* Argument objects. */
3716{
3717    register WmInfo *wmPtr = winPtr->wmInfoPtr;
3718    TkWindow *useWinPtr = winPtr; /* window to apply to (NULL if -default) */
3719    char *string;
3720
3721    if ((objc < 3) || (objc > 5)) {
3722	Tcl_WrongNumArgs(interp, 2, objv, "window ?-default? ?image?");
3723	return TCL_ERROR;
3724    } else if (objc == 5) {
3725	/* If we have 5 arguments, we must have a '-default' flag */
3726	char *argv3 = Tcl_GetString(objv[3]);
3727	if (strcmp(argv3, "-default")) {
3728	    Tcl_AppendResult(interp, "illegal option \"",
3729		    argv3, "\" must be \"-default\"",
3730		    (char *) NULL);
3731	    return TCL_ERROR;
3732	}
3733	useWinPtr = NULL;
3734    } else if (objc == 3) {
3735	/* No arguments were given */
3736	if (wmPtr->hints.flags & IconPixmapHint) {
3737	    Tcl_SetResult(interp, (char *)
3738		    Tk_NameOfBitmap(winPtr->display, wmPtr->hints.icon_pixmap),
3739		    TCL_STATIC);
3740	}
3741	return TCL_OK;
3742    }
3743
3744    string = Tcl_GetString(objv[objc-1]);
3745    if (*string == '\0') {
3746	if (wmPtr->hints.icon_pixmap != None) {
3747	    Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap);
3748	    wmPtr->hints.icon_pixmap = None;
3749	}
3750	wmPtr->hints.flags &= ~IconPixmapHint;
3751	if (WinSetIcon(interp, NULL, (Tk_Window) useWinPtr) != TCL_OK) {
3752	    return TCL_ERROR;
3753	}
3754    } else {
3755	/*
3756	 * In the future this block of code will use Tk's 'image'
3757	 * functionality to allow all supported image formats.
3758	 * However, this will require a change to the way icons are
3759	 * handled.  We will need to add icon<->image conversions
3760	 * routines.
3761	 *
3762	 * Until that happens we simply try to find an icon in the
3763	 * given argument, and if that fails, we use the older
3764	 * bitmap code.  We do things this way round (icon then
3765	 * bitmap), because the bitmap code actually seems to have
3766	 * no visible effect, so we want to give the icon code the
3767	 * first try at doing something.
3768	 */
3769
3770	/*
3771	 * Either return NULL, or return a valid titlebaricon with its
3772	 * ref count already incremented.
3773	 */
3774	WinIconPtr titlebaricon = ReadIconFromFile(interp, objv[objc-1]);
3775	if (titlebaricon != NULL) {
3776	    /*
3777	     * Try to set the icon for the window.  If it is a '-default'
3778	     * icon, we must pass in NULL
3779	     */
3780	    if (WinSetIcon(interp, titlebaricon, (Tk_Window) useWinPtr)
3781		    != TCL_OK) {
3782		/* We didn't use the titlebaricon after all */
3783		DecrIconRefCount(titlebaricon);
3784		titlebaricon = NULL;
3785	    }
3786	}
3787	if (titlebaricon == NULL) {
3788	    /*
3789	     * We didn't manage to handle the argument as a valid
3790	     * icon.  Try as a bitmap.  First we must clear the
3791	     * error message which was placed in the interpreter
3792	     */
3793	    Pixmap pixmap;
3794	    Tcl_ResetResult(interp);
3795	    pixmap = Tk_GetBitmap(interp, (Tk_Window) winPtr, string);
3796	    if (pixmap == None) {
3797		return TCL_ERROR;
3798	    }
3799	    wmPtr->hints.icon_pixmap = pixmap;
3800	    wmPtr->hints.flags |= IconPixmapHint;
3801	    titlebaricon = GetIconFromPixmap(Tk_Display(winPtr), pixmap);
3802	    if (titlebaricon != NULL) {
3803		if (WinSetIcon(interp, titlebaricon, (Tk_Window) useWinPtr)
3804			!= TCL_OK) {
3805		    /* We didn't use the titlebaricon after all */
3806		    DecrIconRefCount(titlebaricon);
3807		    titlebaricon = NULL;
3808		}
3809	    }
3810	}
3811    }
3812    return TCL_OK;
3813}
3814
3815/*
3816 *----------------------------------------------------------------------
3817 *
3818 * WmIconifyCmd --
3819 *
3820 *	This procedure is invoked to process the "wm iconify" Tcl command.
3821 *	See the user documentation for details on what it does.
3822 *
3823 * Results:
3824 *	A standard Tcl result.
3825 *
3826 * Side effects:
3827 *	See the user documentation.
3828 *
3829 *----------------------------------------------------------------------
3830 */
3831
3832static int
3833WmIconifyCmd(tkwin, winPtr, interp, objc, objv)
3834    Tk_Window tkwin;		/* Main window of the application. */
3835    TkWindow *winPtr;           /* Toplevel to work with */
3836    Tcl_Interp *interp;		/* Current interpreter. */
3837    int objc;			/* Number of arguments. */
3838    Tcl_Obj *CONST objv[];	/* Argument objects. */
3839{
3840    register WmInfo *wmPtr = winPtr->wmInfoPtr;
3841    if (objc != 3) {
3842	Tcl_WrongNumArgs(interp, 2, objv, "window");
3843	return TCL_ERROR;
3844    }
3845    if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) {
3846	Tcl_AppendResult(interp, "can't iconify \"", winPtr->pathName,
3847		"\": override-redirect flag is set", (char *) NULL);
3848	return TCL_ERROR;
3849    }
3850    if (wmPtr->masterPtr != NULL) {
3851	Tcl_AppendResult(interp, "can't iconify \"", winPtr->pathName,
3852		"\": it is a transient", (char *) NULL);
3853	return TCL_ERROR;
3854    }
3855    if (wmPtr->iconFor != NULL) {
3856	Tcl_AppendResult(interp, "can't iconify ", winPtr->pathName,
3857		": it is an icon for ", Tk_PathName(wmPtr->iconFor),
3858		(char *) NULL);
3859	return TCL_ERROR;
3860    }
3861    if (winPtr->flags & TK_EMBEDDED) {
3862	Tcl_AppendResult(interp, "can't iconify ", winPtr->pathName,
3863		": it is an embedded window", (char *) NULL);
3864	return TCL_ERROR;
3865    }
3866    TkpWmSetState(winPtr, IconicState);
3867    return TCL_OK;
3868}
3869
3870/*
3871 *----------------------------------------------------------------------
3872 *
3873 * WmIconmaskCmd --
3874 *
3875 *	This procedure is invoked to process the "wm iconmask" Tcl command.
3876 *	See the user documentation for details on what it does.
3877 *
3878 * Results:
3879 *	A standard Tcl result.
3880 *
3881 * Side effects:
3882 *	See the user documentation.
3883 *
3884 *----------------------------------------------------------------------
3885 */
3886
3887static int
3888WmIconmaskCmd(tkwin, winPtr, interp, objc, objv)
3889    Tk_Window tkwin;		/* Main window of the application. */
3890    TkWindow *winPtr;           /* Toplevel to work with */
3891    Tcl_Interp *interp;		/* Current interpreter. */
3892    int objc;			/* Number of arguments. */
3893    Tcl_Obj *CONST objv[];	/* Argument objects. */
3894{
3895    register WmInfo *wmPtr = winPtr->wmInfoPtr;
3896    Pixmap pixmap;
3897    char *argv3;
3898
3899    if ((objc != 3) && (objc != 4)) {
3900	Tcl_WrongNumArgs(interp, 2, objv, "window ?bitmap?");
3901	return TCL_ERROR;
3902    }
3903    if (objc == 3) {
3904	if (wmPtr->hints.flags & IconMaskHint) {
3905	    Tcl_SetResult(interp, (char *)
3906		    Tk_NameOfBitmap(winPtr->display, wmPtr->hints.icon_mask),
3907		    TCL_STATIC);
3908	}
3909	return TCL_OK;
3910    }
3911    argv3 = Tcl_GetString(objv[3]);
3912    if (*argv3 == '\0') {
3913	if (wmPtr->hints.icon_mask != None) {
3914	    Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_mask);
3915	}
3916	wmPtr->hints.flags &= ~IconMaskHint;
3917    } else {
3918	pixmap = Tk_GetBitmap(interp, tkwin, argv3);
3919	if (pixmap == None) {
3920	    return TCL_ERROR;
3921	}
3922	wmPtr->hints.icon_mask = pixmap;
3923	wmPtr->hints.flags |= IconMaskHint;
3924    }
3925    return TCL_OK;
3926}
3927
3928/*
3929 *----------------------------------------------------------------------
3930 *
3931 * WmIconnameCmd --
3932 *
3933 *	This procedure is invoked to process the "wm iconname" Tcl command.
3934 *	See the user documentation for details on what it does.
3935 *
3936 * Results:
3937 *	A standard Tcl result.
3938 *
3939 * Side effects:
3940 *	See the user documentation.
3941 *
3942 *----------------------------------------------------------------------
3943 */
3944
3945static int
3946WmIconnameCmd(tkwin, winPtr, interp, objc, objv)
3947    Tk_Window tkwin;		/* Main window of the application. */
3948    TkWindow *winPtr;           /* Toplevel to work with */
3949    Tcl_Interp *interp;		/* Current interpreter. */
3950    int objc;			/* Number of arguments. */
3951    Tcl_Obj *CONST objv[];	/* Argument objects. */
3952{
3953    register WmInfo *wmPtr = winPtr->wmInfoPtr;
3954    char *argv3;
3955    int length;
3956
3957    if (objc > 4) {
3958	Tcl_WrongNumArgs(interp, 2, objv, "window ?newName?");
3959	return TCL_ERROR;
3960    }
3961    if (objc == 3) {
3962	Tcl_SetResult(interp,
3963		((wmPtr->iconName != NULL) ? wmPtr->iconName : ""),
3964		TCL_STATIC);
3965	return TCL_OK;
3966    } else {
3967	if (wmPtr->iconName != NULL) {
3968	    ckfree((char *) wmPtr->iconName);
3969	}
3970	argv3 = Tcl_GetStringFromObj(objv[3], &length);
3971	wmPtr->iconName = ckalloc((unsigned) (length + 1));
3972	strcpy(wmPtr->iconName, argv3);
3973	if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
3974	    XSetIconName(winPtr->display, winPtr->window, wmPtr->iconName);
3975	}
3976    }
3977    return TCL_OK;
3978}
3979
3980/*
3981 *----------------------------------------------------------------------
3982 *
3983 * WmIconphotoCmd --
3984 *
3985 *	This procedure is invoked to process the "wm iconphoto"
3986 *	Tcl command.
3987 *	See the user documentation for details on what it does.
3988 *
3989 * Results:
3990 *	A standard Tcl result.
3991 *
3992 * Side effects:
3993 *	See the user documentation.
3994 *
3995 *----------------------------------------------------------------------
3996 */
3997
3998static int
3999WmIconphotoCmd(tkwin, winPtr, interp, objc, objv)
4000    Tk_Window tkwin;		/* Main window of the application. */
4001    TkWindow *winPtr;           /* Toplevel to work with */
4002    Tcl_Interp *interp;		/* Current interpreter. */
4003    int objc;			/* Number of arguments. */
4004    Tcl_Obj *CONST objv[];	/* Argument objects. */
4005{
4006    register WmInfo *wmPtr = winPtr->wmInfoPtr;
4007    TkWindow *useWinPtr = winPtr; /* window to apply to (NULL if -default) */
4008    Tk_PhotoHandle photo;
4009    Tk_PhotoImageBlock block;
4010    int i, size, width, height, idx, bufferSize, startObj = 3;
4011    unsigned char *bgraPixelPtr;
4012    BlockOfIconImagesPtr lpIR;
4013    WinIconPtr titlebaricon = NULL;
4014    HICON hIcon;
4015
4016    if (objc < 4) {
4017	Tcl_WrongNumArgs(interp, 2, objv,
4018		"window ?-default? image1 ?image2 ...?");
4019	return TCL_ERROR;
4020    }
4021    /*
4022     * Iterate over all images to validate their existence.
4023     */
4024    if (strcmp(Tcl_GetString(objv[3]), "-default") == 0) {
4025	useWinPtr = NULL;
4026	startObj = 4;
4027	if (objc == 4) {
4028	    Tcl_WrongNumArgs(interp, 2, objv,
4029		    "window ?-default? image1 ?image2 ...?");
4030	    return TCL_ERROR;
4031	}
4032    }
4033    for (i = startObj; i < objc; i++) {
4034	photo = Tk_FindPhoto(interp, Tcl_GetString(objv[i]));
4035	if (photo == NULL) {
4036	    Tcl_AppendResult(interp, "can't use \"", Tcl_GetString(objv[i]),
4037		    "\" as iconphoto: not a photo image", (char *) NULL);
4038	    return TCL_ERROR;
4039	}
4040    }
4041    /* We have calculated the size of the data. Try to allocate the needed
4042     * memory space. */
4043    size = sizeof(BlockOfIconImages)
4044	+ (sizeof(ICONIMAGE) * (objc - (startObj+1)));
4045    lpIR = (BlockOfIconImagesPtr) Tcl_AttemptAlloc(size);
4046    if (lpIR == NULL) {
4047	return TCL_ERROR;
4048    }
4049    ZeroMemory(lpIR, size);
4050
4051    lpIR->nNumImages = objc - startObj;
4052    for (i = startObj; i < objc; i++) {
4053	photo = Tk_FindPhoto(interp, Tcl_GetString(objv[i]));
4054	Tk_PhotoGetSize(photo, &width, &height);
4055	Tk_PhotoGetImage(photo, &block);
4056
4057	/*
4058	 * Convert the image data into BGRA format (RGBQUAD) and then
4059	 * encode the image data into an HICON.
4060	 */
4061	bufferSize = height * width * block.pixelSize;
4062	bgraPixelPtr = ckalloc(bufferSize);
4063	for (idx = 0 ; idx < bufferSize ; idx += 4) {
4064	    bgraPixelPtr[idx] = block.pixelPtr[idx+2];
4065	    bgraPixelPtr[idx+1] = block.pixelPtr[idx+1];
4066	    bgraPixelPtr[idx+2] = block.pixelPtr[idx+0];
4067	    bgraPixelPtr[idx+3] = block.pixelPtr[idx+3];
4068	}
4069	hIcon = CreateIcon(Tk_GetHINSTANCE(), width, height, 1, 32,
4070		NULL, (BYTE *) bgraPixelPtr);
4071	ckfree(bgraPixelPtr);
4072	if (hIcon == NULL) {
4073	    /* XXX should free up created icons */
4074	    Tcl_Free((char *) lpIR);
4075	    Tcl_AppendResult(interp, "failed to create icon for \"",
4076		    Tcl_GetString(objv[i]), "\"", (char *) NULL);
4077	    return TCL_ERROR;
4078	}
4079	lpIR->IconImages[i-startObj].Width  = width;
4080	lpIR->IconImages[i-startObj].Height = height;
4081	lpIR->IconImages[i-startObj].Colors = 4;
4082	lpIR->IconImages[i-startObj].hIcon  = hIcon;
4083    }
4084    titlebaricon = (WinIconPtr) ckalloc(sizeof(WinIconInstance));
4085    titlebaricon->iconBlock = lpIR;
4086    titlebaricon->refCount = 1;
4087    if (WinSetIcon(interp, titlebaricon, (Tk_Window) useWinPtr) != TCL_OK) {
4088	/* We didn't use the titlebaricon after all */
4089	DecrIconRefCount(titlebaricon);
4090	return TCL_ERROR;
4091    }
4092    return TCL_OK;
4093}
4094
4095/*
4096 *----------------------------------------------------------------------
4097 *
4098 * WmIconpositionCmd --
4099 *
4100 *	This procedure is invoked to process the "wm iconposition"
4101 *	Tcl command.
4102 *	See the user documentation for details on what it does.
4103 *
4104 * Results:
4105 *	A standard Tcl result.
4106 *
4107 * Side effects:
4108 *	See the user documentation.
4109 *
4110 *----------------------------------------------------------------------
4111 */
4112
4113static int
4114WmIconpositionCmd(tkwin, winPtr, interp, objc, objv)
4115    Tk_Window tkwin;		/* Main window of the application. */
4116    TkWindow *winPtr;           /* Toplevel to work with */
4117    Tcl_Interp *interp;		/* Current interpreter. */
4118    int objc;			/* Number of arguments. */
4119    Tcl_Obj *CONST objv[];	/* Argument objects. */
4120{
4121    register WmInfo *wmPtr = winPtr->wmInfoPtr;
4122    int x, y;
4123
4124    if ((objc != 3) && (objc != 5)) {
4125	Tcl_WrongNumArgs(interp, 2, objv, "window ?x y?");
4126	return TCL_ERROR;
4127    }
4128    if (objc == 3) {
4129	if (wmPtr->hints.flags & IconPositionHint) {
4130	    char buf[TCL_INTEGER_SPACE * 2];
4131
4132	    sprintf(buf, "%d %d", wmPtr->hints.icon_x,
4133		    wmPtr->hints.icon_y);
4134	    Tcl_SetResult(interp, buf, TCL_VOLATILE);
4135	}
4136	return TCL_OK;
4137    }
4138    if (*Tcl_GetString(objv[3]) == '\0') {
4139	wmPtr->hints.flags &= ~IconPositionHint;
4140    } else {
4141	if ((Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK)
4142		|| (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK)){
4143	    return TCL_ERROR;
4144	}
4145	wmPtr->hints.icon_x = x;
4146	wmPtr->hints.icon_y = y;
4147	wmPtr->hints.flags |= IconPositionHint;
4148    }
4149    return TCL_OK;
4150}
4151
4152/*
4153 *----------------------------------------------------------------------
4154 *
4155 * WmIconwindowCmd --
4156 *
4157 *	This procedure is invoked to process the "wm iconwindow" Tcl command.
4158 *	See the user documentation for details on what it does.
4159 *
4160 * Results:
4161 *	A standard Tcl result.
4162 *
4163 * Side effects:
4164 *	See the user documentation.
4165 *
4166 *----------------------------------------------------------------------
4167 */
4168
4169static int
4170WmIconwindowCmd(tkwin, winPtr, interp, objc, objv)
4171    Tk_Window tkwin;		/* Main window of the application. */
4172    TkWindow *winPtr;           /* Toplevel to work with */
4173    Tcl_Interp *interp;		/* Current interpreter. */
4174    int objc;			/* Number of arguments. */
4175    Tcl_Obj *CONST objv[];	/* Argument objects. */
4176{
4177    register WmInfo *wmPtr = winPtr->wmInfoPtr;
4178    Tk_Window tkwin2;
4179    WmInfo *wmPtr2;
4180    XSetWindowAttributes atts;
4181
4182    if ((objc != 3) && (objc != 4)) {
4183	Tcl_WrongNumArgs(interp, 2, objv, "window ?pathName?");
4184	return TCL_ERROR;
4185    }
4186    if (objc == 3) {
4187	if (wmPtr->icon != NULL) {
4188	    Tcl_SetResult(interp, Tk_PathName(wmPtr->icon), TCL_STATIC);
4189	}
4190	return TCL_OK;
4191    }
4192    if (*Tcl_GetString(objv[3]) == '\0') {
4193	wmPtr->hints.flags &= ~IconWindowHint;
4194	if (wmPtr->icon != NULL) {
4195	    /*
4196	     * Let the window use button events again, then remove
4197	     * it as icon window.
4198	     */
4199
4200	    atts.event_mask = Tk_Attributes(wmPtr->icon)->event_mask
4201		    | ButtonPressMask;
4202	    Tk_ChangeWindowAttributes(wmPtr->icon, CWEventMask, &atts);
4203	    wmPtr2 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
4204	    wmPtr2->iconFor = NULL;
4205	    wmPtr2->hints.initial_state = WithdrawnState;
4206	}
4207	wmPtr->icon = NULL;
4208    } else {
4209	if (TkGetWindowFromObj(interp, tkwin, objv[3], &tkwin2) != TCL_OK) {
4210	    return TCL_ERROR;
4211	}
4212	if (!Tk_IsTopLevel(tkwin2)) {
4213	    Tcl_AppendResult(interp, "can't use ", Tcl_GetString(objv[3]),
4214		    " as icon window: not at top level", (char *) NULL);
4215	    return TCL_ERROR;
4216	}
4217	wmPtr2 = ((TkWindow *) tkwin2)->wmInfoPtr;
4218	if (wmPtr2->iconFor != NULL) {
4219	    Tcl_AppendResult(interp, Tcl_GetString(objv[3]),
4220		    " is already an icon for ",
4221		    Tk_PathName(wmPtr2->iconFor), (char *) NULL);
4222	    return TCL_ERROR;
4223	}
4224	if (wmPtr->icon != NULL) {
4225	    WmInfo *wmPtr3 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
4226	    wmPtr3->iconFor = NULL;
4227
4228	    /*
4229	     * Let the window use button events again.
4230	     */
4231
4232	    atts.event_mask = Tk_Attributes(wmPtr->icon)->event_mask
4233		    | ButtonPressMask;
4234	    Tk_ChangeWindowAttributes(wmPtr->icon, CWEventMask, &atts);
4235	}
4236
4237	/*
4238	 * Disable button events in the icon window:  some window
4239	 * managers (like olvwm) want to get the events themselves,
4240	 * but X only allows one application at a time to receive
4241	 * button events for a window.
4242	 */
4243
4244	atts.event_mask = Tk_Attributes(tkwin2)->event_mask
4245		& ~ButtonPressMask;
4246	Tk_ChangeWindowAttributes(tkwin2, CWEventMask, &atts);
4247	Tk_MakeWindowExist(tkwin2);
4248	wmPtr->hints.icon_window = Tk_WindowId(tkwin2);
4249	wmPtr->hints.flags |= IconWindowHint;
4250	wmPtr->icon = tkwin2;
4251	wmPtr2->iconFor = (Tk_Window) winPtr;
4252	if (!(wmPtr2->flags & WM_NEVER_MAPPED)) {
4253	    wmPtr2->flags |= WM_WITHDRAWN;
4254	    TkpWmSetState(((TkWindow *) tkwin2), WithdrawnState);
4255	}
4256    }
4257    return TCL_OK;
4258}
4259
4260/*
4261 *----------------------------------------------------------------------
4262 *
4263 * WmMaxsizeCmd --
4264 *
4265 *	This procedure is invoked to process the "wm maxsize" Tcl command.
4266 *	See the user documentation for details on what it does.
4267 *
4268 * Results:
4269 *	A standard Tcl result.
4270 *
4271 * Side effects:
4272 *	See the user documentation.
4273 *
4274 *----------------------------------------------------------------------
4275 */
4276
4277static int
4278WmMaxsizeCmd(tkwin, winPtr, interp, objc, objv)
4279    Tk_Window tkwin;		/* Main window of the application. */
4280    TkWindow *winPtr;           /* Toplevel to work with */
4281    Tcl_Interp *interp;		/* Current interpreter. */
4282    int objc;			/* Number of arguments. */
4283    Tcl_Obj *CONST objv[];	/* Argument objects. */
4284{
4285    register WmInfo *wmPtr = winPtr->wmInfoPtr;
4286    int width, height;
4287
4288    if ((objc != 3) && (objc != 5)) {
4289	Tcl_WrongNumArgs(interp, 2, objv, "window ?width height?");
4290	return TCL_ERROR;
4291    }
4292    if (objc == 3) {
4293	char buf[TCL_INTEGER_SPACE * 2];
4294
4295	GetMaxSize(wmPtr, &width, &height);
4296	sprintf(buf, "%d %d", width, height);
4297	Tcl_SetResult(interp, buf, TCL_VOLATILE);
4298	return TCL_OK;
4299    }
4300    if ((Tcl_GetIntFromObj(interp, objv[3], &width) != TCL_OK)
4301	    || (Tcl_GetIntFromObj(interp, objv[4], &height) != TCL_OK)) {
4302	return TCL_ERROR;
4303    }
4304    wmPtr->maxWidth = width;
4305    wmPtr->maxHeight = height;
4306    WmUpdateGeom(wmPtr, winPtr);
4307    return TCL_OK;
4308}
4309
4310/*
4311 *----------------------------------------------------------------------
4312 *
4313 * WmMinsizeCmd --
4314 *
4315 *	This procedure is invoked to process the "wm minsize" Tcl command.
4316 *	See the user documentation for details on what it does.
4317 *
4318 * Results:
4319 *	A standard Tcl result.
4320 *
4321 * Side effects:
4322 *	See the user documentation.
4323 *
4324 *----------------------------------------------------------------------
4325 */
4326
4327static int
4328WmMinsizeCmd(tkwin, winPtr, interp, objc, objv)
4329    Tk_Window tkwin;		/* Main window of the application. */
4330    TkWindow *winPtr;           /* Toplevel to work with */
4331    Tcl_Interp *interp;		/* Current interpreter. */
4332    int objc;			/* Number of arguments. */
4333    Tcl_Obj *CONST objv[];	/* Argument objects. */
4334{
4335    register WmInfo *wmPtr = winPtr->wmInfoPtr;
4336    int width, height;
4337
4338    if ((objc != 3) && (objc != 5)) {
4339	Tcl_WrongNumArgs(interp, 2, objv, "window ?width height?");
4340	return TCL_ERROR;
4341    }
4342    if (objc == 3) {
4343	char buf[TCL_INTEGER_SPACE * 2];
4344
4345	GetMinSize(wmPtr, &width, &height);
4346	sprintf(buf, "%d %d", width, height);
4347	Tcl_SetResult(interp, buf, TCL_VOLATILE);
4348	return TCL_OK;
4349    }
4350    if ((Tcl_GetIntFromObj(interp, objv[3], &width) != TCL_OK)
4351	    || (Tcl_GetIntFromObj(interp, objv[4], &height) != TCL_OK)) {
4352	return TCL_ERROR;
4353    }
4354    wmPtr->minWidth = width;
4355    wmPtr->minHeight = height;
4356    WmUpdateGeom(wmPtr, winPtr);
4357    return TCL_OK;
4358}
4359
4360/*
4361 *----------------------------------------------------------------------
4362 *
4363 * WmOverrideredirectCmd --
4364 *
4365 *	This procedure is invoked to process the "wm overrideredirect"
4366 *	Tcl command.
4367 *	See the user documentation for details on what it does.
4368 *
4369 * Results:
4370 *	A standard Tcl result.
4371 *
4372 * Side effects:
4373 *	See the user documentation.
4374 *
4375 *----------------------------------------------------------------------
4376 */
4377
4378static int
4379WmOverrideredirectCmd(tkwin, winPtr, interp, objc, objv)
4380    Tk_Window tkwin;		/* Main window of the application. */
4381    TkWindow *winPtr;           /* Toplevel to work with */
4382    Tcl_Interp *interp;		/* Current interpreter. */
4383    int objc;			/* Number of arguments. */
4384    Tcl_Obj *CONST objv[];	/* Argument objects. */
4385{
4386    register WmInfo *wmPtr = winPtr->wmInfoPtr;
4387    int boolean, curValue;
4388    XSetWindowAttributes atts;
4389
4390    if ((objc != 3) && (objc != 4)) {
4391	Tcl_WrongNumArgs(interp, 2, objv, "window ?boolean?");
4392	return TCL_ERROR;
4393    }
4394    curValue = Tk_Attributes((Tk_Window) winPtr)->override_redirect;
4395    if (objc == 3) {
4396	Tcl_SetBooleanObj(Tcl_GetObjResult(interp), curValue);
4397	return TCL_OK;
4398    }
4399    if (Tcl_GetBooleanFromObj(interp, objv[3], &boolean) != TCL_OK) {
4400	return TCL_ERROR;
4401    }
4402    if (curValue != boolean) {
4403	/*
4404	 * Only do this if we are really changing value, because it
4405	 * causes some funky stuff to occur
4406	 */
4407	atts.override_redirect = (boolean) ? True : False;
4408	Tk_ChangeWindowAttributes((Tk_Window) winPtr, CWOverrideRedirect,
4409		&atts);
4410	if (!(wmPtr->flags & (WM_NEVER_MAPPED)
4411		&& !(winPtr->flags & TK_EMBEDDED))) {
4412	    UpdateWrapper(winPtr);
4413	}
4414    }
4415    return TCL_OK;
4416}
4417
4418/*
4419 *----------------------------------------------------------------------
4420 *
4421 * WmPositionfromCmd --
4422 *
4423 *	This procedure is invoked to process the "wm positionfrom"
4424 *	Tcl command.
4425 *	See the user documentation for details on what it does.
4426 *
4427 * Results:
4428 *	A standard Tcl result.
4429 *
4430 * Side effects:
4431 *	See the user documentation.
4432 *
4433 *----------------------------------------------------------------------
4434 */
4435
4436static int
4437WmPositionfromCmd(tkwin, winPtr, interp, objc, objv)
4438    Tk_Window tkwin;		/* Main window of the application. */
4439    TkWindow *winPtr;           /* Toplevel to work with */
4440    Tcl_Interp *interp;		/* Current interpreter. */
4441    int objc;			/* Number of arguments. */
4442    Tcl_Obj *CONST objv[];	/* Argument objects. */
4443{
4444    register WmInfo *wmPtr = winPtr->wmInfoPtr;
4445    static CONST char *optionStrings[] = {
4446	"program", "user", (char *) NULL };
4447    enum options {
4448	OPT_PROGRAM, OPT_USER };
4449    int index;
4450
4451    if ((objc != 3) && (objc != 4)) {
4452	Tcl_WrongNumArgs(interp, 2, objv, "window ?user/program?");
4453	return TCL_ERROR;
4454    }
4455    if (objc == 3) {
4456	if (wmPtr->sizeHintsFlags & USPosition) {
4457	    Tcl_SetResult(interp, "user", TCL_STATIC);
4458	} else if (wmPtr->sizeHintsFlags & PPosition) {
4459	    Tcl_SetResult(interp, "program", TCL_STATIC);
4460	}
4461	return TCL_OK;
4462    }
4463    if (*Tcl_GetString(objv[3]) == '\0') {
4464	wmPtr->sizeHintsFlags &= ~(USPosition|PPosition);
4465    } else {
4466	if (Tcl_GetIndexFromObj(interp, objv[3], optionStrings, "argument", 0,
4467		&index) != TCL_OK) {
4468	    return TCL_ERROR;
4469	}
4470	if (index == OPT_USER) {
4471	    wmPtr->sizeHintsFlags &= ~PPosition;
4472	    wmPtr->sizeHintsFlags |= USPosition;
4473	} else {
4474	    wmPtr->sizeHintsFlags &= ~USPosition;
4475	    wmPtr->sizeHintsFlags |= PPosition;
4476	}
4477    }
4478    WmUpdateGeom(wmPtr, winPtr);
4479    return TCL_OK;
4480}
4481
4482/*
4483 *----------------------------------------------------------------------
4484 *
4485 * WmProtocolCmd --
4486 *
4487 *	This procedure is invoked to process the "wm protocol" Tcl command.
4488 *	See the user documentation for details on what it does.
4489 *
4490 * Results:
4491 *	A standard Tcl result.
4492 *
4493 * Side effects:
4494 *	See the user documentation.
4495 *
4496 *----------------------------------------------------------------------
4497 */
4498
4499static int
4500WmProtocolCmd(tkwin, winPtr, interp, objc, objv)
4501    Tk_Window tkwin;		/* Main window of the application. */
4502    TkWindow *winPtr;           /* Toplevel to work with */
4503    Tcl_Interp *interp;		/* Current interpreter. */
4504    int objc;			/* Number of arguments. */
4505    Tcl_Obj *CONST objv[];	/* Argument objects. */
4506{
4507    register WmInfo *wmPtr = winPtr->wmInfoPtr;
4508    register ProtocolHandler *protPtr, *prevPtr;
4509    Atom protocol;
4510    char *cmd;
4511    int cmdLength;
4512
4513    if ((objc < 3) || (objc > 5)) {
4514	Tcl_WrongNumArgs(interp, 2, objv, "window ?name? ?command?");
4515	return TCL_ERROR;
4516    }
4517    if (objc == 3) {
4518	/*
4519	 * Return a list of all defined protocols for the window.
4520	 */
4521	for (protPtr = wmPtr->protPtr; protPtr != NULL;
4522	     protPtr = protPtr->nextPtr) {
4523	    Tcl_AppendElement(interp,
4524		    Tk_GetAtomName((Tk_Window) winPtr, protPtr->protocol));
4525	}
4526	return TCL_OK;
4527    }
4528    protocol = Tk_InternAtom((Tk_Window) winPtr, Tcl_GetString(objv[3]));
4529    if (objc == 4) {
4530	/*
4531	 * Return the command to handle a given protocol.
4532	 */
4533	for (protPtr = wmPtr->protPtr; protPtr != NULL;
4534	     protPtr = protPtr->nextPtr) {
4535	    if (protPtr->protocol == protocol) {
4536		Tcl_SetResult(interp, protPtr->command, TCL_STATIC);
4537		return TCL_OK;
4538	    }
4539	}
4540	return TCL_OK;
4541    }
4542
4543    /*
4544     * Delete any current protocol handler, then create a new
4545     * one with the specified command, unless the command is
4546     * empty.
4547     */
4548
4549    for (protPtr = wmPtr->protPtr, prevPtr = NULL; protPtr != NULL;
4550	 prevPtr = protPtr, protPtr = protPtr->nextPtr) {
4551	if (protPtr->protocol == protocol) {
4552	    if (prevPtr == NULL) {
4553		wmPtr->protPtr = protPtr->nextPtr;
4554	    } else {
4555		prevPtr->nextPtr = protPtr->nextPtr;
4556	    }
4557	    Tcl_EventuallyFree((ClientData) protPtr, TCL_DYNAMIC);
4558	    break;
4559	}
4560    }
4561    cmd = Tcl_GetStringFromObj(objv[4], &cmdLength);
4562    if (cmdLength > 0) {
4563	protPtr = (ProtocolHandler *) ckalloc(HANDLER_SIZE(cmdLength));
4564	protPtr->protocol = protocol;
4565	protPtr->nextPtr = wmPtr->protPtr;
4566	wmPtr->protPtr = protPtr;
4567	protPtr->interp = interp;
4568	strcpy(protPtr->command, cmd);
4569    }
4570    return TCL_OK;
4571}
4572
4573/*
4574 *----------------------------------------------------------------------
4575 *
4576 * WmResizableCmd --
4577 *
4578 *	This procedure is invoked to process the "wm resizable" Tcl command.
4579 *	See the user documentation for details on what it does.
4580 *
4581 * Results:
4582 *	A standard Tcl result.
4583 *
4584 * Side effects:
4585 *	See the user documentation.
4586 *
4587 *----------------------------------------------------------------------
4588 */
4589
4590static int
4591WmResizableCmd(tkwin, winPtr, interp, objc, objv)
4592    Tk_Window tkwin;		/* Main window of the application. */
4593    TkWindow *winPtr;           /* Toplevel to work with */
4594    Tcl_Interp *interp;		/* Current interpreter. */
4595    int objc;			/* Number of arguments. */
4596    Tcl_Obj *CONST objv[];	/* Argument objects. */
4597{
4598    register WmInfo *wmPtr = winPtr->wmInfoPtr;
4599    int width, height;
4600
4601    if ((objc != 3) && (objc != 5)) {
4602	Tcl_WrongNumArgs(interp, 2, objv, "window ?width height?");
4603	return TCL_ERROR;
4604    }
4605    if (objc == 3) {
4606	char buf[TCL_INTEGER_SPACE * 2];
4607
4608	sprintf(buf, "%d %d",
4609		(wmPtr->flags  & WM_WIDTH_NOT_RESIZABLE) ? 0 : 1,
4610		(wmPtr->flags  & WM_HEIGHT_NOT_RESIZABLE) ? 0 : 1);
4611	Tcl_SetResult(interp, buf, TCL_VOLATILE);
4612	return TCL_OK;
4613    }
4614    if ((Tcl_GetBooleanFromObj(interp, objv[3], &width) != TCL_OK)
4615	    || (Tcl_GetBooleanFromObj(interp, objv[4], &height) != TCL_OK)) {
4616	return TCL_ERROR;
4617    }
4618    if (width) {
4619	wmPtr->flags &= ~WM_WIDTH_NOT_RESIZABLE;
4620    } else {
4621	wmPtr->flags |= WM_WIDTH_NOT_RESIZABLE;
4622    }
4623    if (height) {
4624	wmPtr->flags &= ~WM_HEIGHT_NOT_RESIZABLE;
4625    } else {
4626	wmPtr->flags |= WM_HEIGHT_NOT_RESIZABLE;
4627    }
4628    if (!((wmPtr->flags & WM_NEVER_MAPPED)
4629	    && !(winPtr->flags & TK_EMBEDDED))) {
4630	UpdateWrapper(winPtr);
4631    }
4632    WmUpdateGeom(wmPtr, winPtr);
4633    return TCL_OK;
4634}
4635
4636/*
4637 *----------------------------------------------------------------------
4638 *
4639 * WmSizefromCmd --
4640 *
4641 *	This procedure is invoked to process the "wm sizefrom" Tcl command.
4642 *	See the user documentation for details on what it does.
4643 *
4644 * Results:
4645 *	A standard Tcl result.
4646 *
4647 * Side effects:
4648 *	See the user documentation.
4649 *
4650 *----------------------------------------------------------------------
4651 */
4652
4653static int
4654WmSizefromCmd(tkwin, winPtr, interp, objc, objv)
4655    Tk_Window tkwin;		/* Main window of the application. */
4656    TkWindow *winPtr;           /* Toplevel to work with */
4657    Tcl_Interp *interp;		/* Current interpreter. */
4658    int objc;			/* Number of arguments. */
4659    Tcl_Obj *CONST objv[];	/* Argument objects. */
4660{
4661    register WmInfo *wmPtr = winPtr->wmInfoPtr;
4662    static CONST char *optionStrings[] = {
4663	"program", "user", (char *) NULL };
4664    enum options {
4665	OPT_PROGRAM, OPT_USER };
4666    int index;
4667
4668    if ((objc != 3) && (objc != 4)) {
4669	Tcl_WrongNumArgs(interp, 2, objv, "window ?user|program?");
4670	return TCL_ERROR;
4671    }
4672    if (objc == 3) {
4673	if (wmPtr->sizeHintsFlags & USSize) {
4674	    Tcl_SetResult(interp, "user", TCL_STATIC);
4675	} else if (wmPtr->sizeHintsFlags & PSize) {
4676	    Tcl_SetResult(interp, "program", TCL_STATIC);
4677	}
4678	return TCL_OK;
4679    }
4680
4681    if (*Tcl_GetString(objv[3]) == '\0') {
4682	wmPtr->sizeHintsFlags &= ~(USSize|PSize);
4683    } else {
4684	if (Tcl_GetIndexFromObj(interp, objv[3], optionStrings, "argument", 0,
4685		&index) != TCL_OK) {
4686	    return TCL_ERROR;
4687	}
4688	if (index == OPT_USER) {
4689	    wmPtr->sizeHintsFlags &= ~PSize;
4690	    wmPtr->sizeHintsFlags |= USSize;
4691	} else { /* OPT_PROGRAM */
4692	    wmPtr->sizeHintsFlags &= ~USSize;
4693	    wmPtr->sizeHintsFlags |= PSize;
4694	}
4695    }
4696    WmUpdateGeom(wmPtr, winPtr);
4697    return TCL_OK;
4698}
4699
4700/*
4701 *----------------------------------------------------------------------
4702 *
4703 * WmStackorderCmd --
4704 *
4705 *	This procedure is invoked to process the "wm stackorder" Tcl command.
4706 *	See the user documentation for details on what it does.
4707 *
4708 * Results:
4709 *	A standard Tcl result.
4710 *
4711 * Side effects:
4712 *	See the user documentation.
4713 *
4714 *----------------------------------------------------------------------
4715 */
4716
4717static int
4718WmStackorderCmd(tkwin, winPtr, interp, objc, objv)
4719    Tk_Window tkwin;		/* Main window of the application. */
4720    TkWindow *winPtr;           /* Toplevel to work with */
4721    Tcl_Interp *interp;		/* Current interpreter. */
4722    int objc;			/* Number of arguments. */
4723    Tcl_Obj *CONST objv[];	/* Argument objects. */
4724{
4725    TkWindow **windows, **window_ptr;
4726    static CONST char *optionStrings[] = {
4727	"isabove", "isbelow", (char *) NULL };
4728    enum options {
4729	OPT_ISABOVE, OPT_ISBELOW };
4730    int index;
4731
4732    if ((objc != 3) && (objc != 5)) {
4733	Tcl_WrongNumArgs(interp, 2, objv, "window ?isabove|isbelow window?");
4734	return TCL_ERROR;
4735    }
4736
4737    if (objc == 3) {
4738	windows = TkWmStackorderToplevel(winPtr);
4739	if (windows == NULL) {
4740	    Tcl_Panic("TkWmStackorderToplevel failed");
4741	} else {
4742	    for (window_ptr = windows; *window_ptr ; window_ptr++) {
4743		Tcl_AppendElement(interp, (*window_ptr)->pathName);
4744	    }
4745	    ckfree((char *) windows);
4746	    return TCL_OK;
4747	}
4748    } else {
4749	TkWindow *winPtr2;
4750	int index1=-1, index2=-1, result;
4751
4752	if (TkGetWindowFromObj(interp, tkwin, objv[4], (Tk_Window *) &winPtr2)
4753		!= TCL_OK) {
4754	    return TCL_ERROR;
4755	}
4756
4757	if (!Tk_IsTopLevel(winPtr2)) {
4758	    Tcl_AppendResult(interp, "window \"", winPtr2->pathName,
4759		    "\" isn't a top-level window", (char *) NULL);
4760	    return TCL_ERROR;
4761	}
4762
4763	if (!Tk_IsMapped(winPtr)) {
4764	    Tcl_AppendResult(interp, "window \"", winPtr->pathName,
4765		    "\" isn't mapped", (char *) NULL);
4766	    return TCL_ERROR;
4767	}
4768
4769	if (!Tk_IsMapped(winPtr2)) {
4770	    Tcl_AppendResult(interp, "window \"", winPtr2->pathName,
4771		    "\" isn't mapped", (char *) NULL);
4772	    return TCL_ERROR;
4773	}
4774
4775	/*
4776	 * Lookup stacking order of all toplevels that are children
4777	 * of "." and find the position of winPtr and winPtr2
4778	 * in the stacking order.
4779	 */
4780
4781	windows = TkWmStackorderToplevel(winPtr->mainPtr->winPtr);
4782
4783	if (windows == NULL) {
4784	    Tcl_AppendResult(interp, "TkWmStackorderToplevel failed",
4785                    (char *) NULL);
4786	    return TCL_ERROR;
4787	} else {
4788	    for (window_ptr = windows; *window_ptr ; window_ptr++) {
4789		if (*window_ptr == winPtr)
4790		    index1 = (window_ptr - windows);
4791		if (*window_ptr == winPtr2)
4792		    index2 = (window_ptr - windows);
4793	    }
4794	    if (index1 == -1)
4795		Tcl_Panic("winPtr window not found");
4796	    if (index2 == -1)
4797		Tcl_Panic("winPtr2 window not found");
4798
4799	    ckfree((char *) windows);
4800	}
4801
4802	if (Tcl_GetIndexFromObj(interp, objv[3], optionStrings, "argument", 0,
4803		&index) != TCL_OK) {
4804	    return TCL_ERROR;
4805	}
4806	if (index == OPT_ISABOVE) {
4807	    result = index1 > index2;
4808	} else { /* OPT_ISBELOW */
4809	    result = index1 < index2;
4810	}
4811	Tcl_SetIntObj(Tcl_GetObjResult(interp), result);
4812	return TCL_OK;
4813    }
4814    return TCL_OK;
4815}
4816
4817/*
4818 *----------------------------------------------------------------------
4819 *
4820 * WmStateCmd --
4821 *
4822 *	This procedure is invoked to process the "wm state" Tcl command.
4823 *	See the user documentation for details on what it does.
4824 *
4825 * Results:
4826 *	A standard Tcl result.
4827 *
4828 * Side effects:
4829 *	See the user documentation.
4830 *
4831 *----------------------------------------------------------------------
4832 */
4833
4834static int
4835WmStateCmd(tkwin, winPtr, interp, objc, objv)
4836    Tk_Window tkwin;		/* Main window of the application. */
4837    TkWindow *winPtr;           /* Toplevel to work with */
4838    Tcl_Interp *interp;		/* Current interpreter. */
4839    int objc;			/* Number of arguments. */
4840    Tcl_Obj *CONST objv[];	/* Argument objects. */
4841{
4842    register WmInfo *wmPtr = winPtr->wmInfoPtr;
4843    static CONST char *optionStrings[] = {
4844	"normal", "iconic", "withdrawn", "zoomed", (char *) NULL };
4845    enum options {
4846	OPT_NORMAL, OPT_ICONIC, OPT_WITHDRAWN, OPT_ZOOMED };
4847    int index;
4848
4849    if ((objc < 3) || (objc > 4)) {
4850	Tcl_WrongNumArgs(interp, 2, objv, "window ?state?");
4851	return TCL_ERROR;
4852    }
4853    if (objc == 4) {
4854	if (wmPtr->iconFor != NULL) {
4855	    Tcl_AppendResult(interp, "can't change state of ",
4856		    Tcl_GetString(objv[2]),
4857		    ": it is an icon for ", Tk_PathName(wmPtr->iconFor),
4858		    (char *) NULL);
4859	    return TCL_ERROR;
4860	}
4861	if (winPtr->flags & TK_EMBEDDED) {
4862	    Tcl_AppendResult(interp, "can't change state of ",
4863		    winPtr->pathName, ": it is an embedded window",
4864		    (char *) NULL);
4865	    return TCL_ERROR;
4866	}
4867
4868	if (Tcl_GetIndexFromObj(interp, objv[3], optionStrings, "argument", 0,
4869		&index) != TCL_OK) {
4870	    return TCL_ERROR;
4871	}
4872
4873	if (index == OPT_NORMAL) {
4874	    wmPtr->flags &= ~WM_WITHDRAWN;
4875	    TkpWmSetState(winPtr, NormalState);
4876	    /*
4877	     * This varies from 'wm deiconify' because it does not
4878	     * force the window to be raised and receive focus
4879	     */
4880	} else if (index == OPT_ICONIC) {
4881	    if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) {
4882		Tcl_AppendResult(interp, "can't iconify \"",
4883			winPtr->pathName,
4884			"\": override-redirect flag is set",
4885			(char *) NULL);
4886		return TCL_ERROR;
4887	    }
4888	    if (wmPtr->masterPtr != NULL) {
4889		Tcl_AppendResult(interp, "can't iconify \"",
4890			winPtr->pathName,
4891			"\": it is a transient", (char *) NULL);
4892		return TCL_ERROR;
4893	    }
4894	    TkpWmSetState(winPtr, IconicState);
4895	} else if (index == OPT_WITHDRAWN) {
4896	    wmPtr->flags |= WM_WITHDRAWN;
4897	    TkpWmSetState(winPtr, WithdrawnState);
4898	} else if (index == OPT_ZOOMED) {
4899	    TkpWmSetState(winPtr, ZoomState);
4900	} else {
4901	    Tcl_Panic("wm state not matched");
4902	}
4903    } else {
4904	if (wmPtr->iconFor != NULL) {
4905	    Tcl_SetResult(interp, "icon", TCL_STATIC);
4906	} else {
4907	    switch (wmPtr->hints.initial_state) {
4908	      case NormalState:
4909		Tcl_SetResult(interp, "normal", TCL_STATIC);
4910		break;
4911	      case IconicState:
4912		Tcl_SetResult(interp, "iconic", TCL_STATIC);
4913		break;
4914	      case WithdrawnState:
4915		Tcl_SetResult(interp, "withdrawn", TCL_STATIC);
4916		break;
4917	      case ZoomState:
4918		Tcl_SetResult(interp, "zoomed", TCL_STATIC);
4919		break;
4920	    }
4921	}
4922    }
4923    return TCL_OK;
4924}
4925
4926/*
4927 *----------------------------------------------------------------------
4928 *
4929 * WmTitleCmd --
4930 *
4931 *	This procedure is invoked to process the "wm title" Tcl command.
4932 *	See the user documentation for details on what it does.
4933 *
4934 * Results:
4935 *	A standard Tcl result.
4936 *
4937 * Side effects:
4938 *	See the user documentation.
4939 *
4940 *----------------------------------------------------------------------
4941 */
4942
4943static int
4944WmTitleCmd(tkwin, winPtr, interp, objc, objv)
4945    Tk_Window tkwin;		/* Main window of the application. */
4946    TkWindow *winPtr;           /* Toplevel to work with */
4947    Tcl_Interp *interp;		/* Current interpreter. */
4948    int objc;			/* Number of arguments. */
4949    Tcl_Obj *CONST objv[];	/* Argument objects. */
4950{
4951    register WmInfo *wmPtr = winPtr->wmInfoPtr;
4952    char *argv3;
4953    int length;
4954
4955    if (objc > 4) {
4956	Tcl_WrongNumArgs(interp, 2, objv, "window ?newTitle?");
4957	return TCL_ERROR;
4958    }
4959    if (objc == 3) {
4960	Tcl_SetResult(interp, (char *)
4961		((wmPtr->title != NULL) ? wmPtr->title : winPtr->nameUid),
4962		TCL_STATIC);
4963	return TCL_OK;
4964    } else {
4965	if (wmPtr->title != NULL) {
4966	    ckfree((char *) wmPtr->title);
4967	}
4968	argv3 = Tcl_GetStringFromObj(objv[3], &length);
4969	wmPtr->title = ckalloc((unsigned) (length + 1));
4970	strcpy(wmPtr->title, argv3);
4971
4972	if (!(wmPtr->flags & WM_NEVER_MAPPED) && wmPtr->wrapper != NULL) {
4973	    Tcl_DString titleString;
4974	    Tcl_WinUtfToTChar(wmPtr->title, -1, &titleString);
4975	    (*tkWinProcs->setWindowText)(wmPtr->wrapper,
4976		    (LPCTSTR) Tcl_DStringValue(&titleString));
4977	    Tcl_DStringFree(&titleString);
4978	}
4979    }
4980    return TCL_OK;
4981}
4982
4983/*
4984 *----------------------------------------------------------------------
4985 *
4986 * WmTransientCmd --
4987 *
4988 *	This procedure is invoked to process the "wm transient" Tcl command.
4989 *	See the user documentation for details on what it does.
4990 *
4991 * Results:
4992 *	A standard Tcl result.
4993 *
4994 * Side effects:
4995 *	See the user documentation.
4996 *
4997 *----------------------------------------------------------------------
4998 */
4999
5000static int
5001WmTransientCmd(tkwin, winPtr, interp, objc, objv)
5002    Tk_Window tkwin;		/* Main window of the application. */
5003    TkWindow *winPtr;           /* Toplevel to work with */
5004    Tcl_Interp *interp;		/* Current interpreter. */
5005    int objc;			/* Number of arguments. */
5006    Tcl_Obj *CONST objv[];	/* Argument objects. */
5007{
5008    register WmInfo *wmPtr = winPtr->wmInfoPtr;
5009    TkWindow *masterPtr = wmPtr->masterPtr;
5010    WmInfo *wmPtr2;
5011
5012    if ((objc != 3) && (objc != 4)) {
5013	Tcl_WrongNumArgs(interp, 2, objv, "window ?master?");
5014	return TCL_ERROR;
5015    }
5016    if (objc == 3) {
5017	if (masterPtr != NULL) {
5018	    Tcl_SetResult(interp, Tk_PathName(masterPtr), TCL_STATIC);
5019	}
5020	return TCL_OK;
5021    }
5022    if (Tcl_GetString(objv[3])[0] == '\0') {
5023	if (masterPtr != NULL) {
5024	    /*
5025	     * If we had a master, tell them that we aren't tied
5026	     * to them anymore
5027	     */
5028
5029	    masterPtr->wmInfoPtr->numTransients--;
5030	    Tk_DeleteEventHandler((Tk_Window) masterPtr,
5031		    VisibilityChangeMask|StructureNotifyMask,
5032		    WmWaitVisibilityOrMapProc, (ClientData) winPtr);
5033	}
5034
5035	wmPtr->masterPtr = NULL;
5036    } else {
5037	if (TkGetWindowFromObj(interp, tkwin, objv[3],
5038		(Tk_Window *) &masterPtr) != TCL_OK) {
5039	    return TCL_ERROR;
5040	}
5041	while (!Tk_TopWinHierarchy(masterPtr)) {
5042	    /*
5043	     * Ensure that the master window is actually a Tk toplevel.
5044	     */
5045
5046	    masterPtr = masterPtr->parentPtr;
5047	}
5048	Tk_MakeWindowExist((Tk_Window) masterPtr);
5049
5050	if (wmPtr->iconFor != NULL) {
5051	    Tcl_AppendResult(interp, "can't make \"",
5052		    Tcl_GetString(objv[2]),
5053		    "\" a transient: it is an icon for ",
5054		    Tk_PathName(wmPtr->iconFor),
5055		    (char *) NULL);
5056	    return TCL_ERROR;
5057	}
5058
5059	wmPtr2 = masterPtr->wmInfoPtr;
5060
5061	if (wmPtr2->iconFor != NULL) {
5062	    Tcl_AppendResult(interp, "can't make \"",
5063		    Tcl_GetString(objv[3]),
5064		    "\" a master: it is an icon for ",
5065		    Tk_PathName(wmPtr2->iconFor),
5066		    (char *) NULL);
5067	    return TCL_ERROR;
5068	}
5069
5070	if (masterPtr == winPtr) {
5071	    Tcl_AppendResult(interp, "can't make \"", Tk_PathName(winPtr),
5072		    "\" its own master",
5073		    (char *) NULL);
5074	    return TCL_ERROR;
5075	} else if (masterPtr != wmPtr->masterPtr) {
5076	    /*
5077	     * Remove old master map/unmap binding before setting
5078	     * the new master. The event handler will ensure that
5079	     * transient states reflect the state of the master.
5080	     */
5081
5082	    if (wmPtr->masterPtr != NULL) {
5083		wmPtr->masterPtr->wmInfoPtr->numTransients--;
5084		Tk_DeleteEventHandler((Tk_Window) wmPtr->masterPtr,
5085			VisibilityChangeMask|StructureNotifyMask,
5086			WmWaitVisibilityOrMapProc, (ClientData) winPtr);
5087	    }
5088
5089	    masterPtr->wmInfoPtr->numTransients++;
5090	    Tk_CreateEventHandler((Tk_Window) masterPtr,
5091		    VisibilityChangeMask|StructureNotifyMask,
5092		    WmWaitVisibilityOrMapProc, (ClientData) winPtr);
5093
5094	    wmPtr->masterPtr = masterPtr;
5095	}
5096    }
5097    if (!((wmPtr->flags & WM_NEVER_MAPPED)
5098	    && !(winPtr->flags & TK_EMBEDDED))) {
5099	if (wmPtr->masterPtr != NULL &&
5100		!Tk_IsMapped(wmPtr->masterPtr)) {
5101	    TkpWmSetState(winPtr, WithdrawnState);
5102	} else {
5103	    UpdateWrapper(winPtr);
5104	}
5105    }
5106    return TCL_OK;
5107}
5108
5109/*
5110 *----------------------------------------------------------------------
5111 *
5112 * WmWithdrawCmd --
5113 *
5114 *	This procedure is invoked to process the "wm withdraw" Tcl command.
5115 *	See the user documentation for details on what it does.
5116 *
5117 * Results:
5118 *	A standard Tcl result.
5119 *
5120 * Side effects:
5121 *	See the user documentation.
5122 *
5123 *----------------------------------------------------------------------
5124 */
5125
5126static int
5127WmWithdrawCmd(tkwin, winPtr, interp, objc, objv)
5128    Tk_Window tkwin;		/* Main window of the application. */
5129    TkWindow *winPtr;           /* Toplevel to work with */
5130    Tcl_Interp *interp;		/* Current interpreter. */
5131    int objc;			/* Number of arguments. */
5132    Tcl_Obj *CONST objv[];	/* Argument objects. */
5133{
5134    register WmInfo *wmPtr = winPtr->wmInfoPtr;
5135
5136    if (objc != 3) {
5137	Tcl_WrongNumArgs(interp, 2, objv, "window");
5138	return TCL_ERROR;
5139    }
5140    if (wmPtr->iconFor != NULL) {
5141	Tcl_AppendResult(interp, "can't withdraw ", Tcl_GetString(objv[2]),
5142		": it is an icon for ", Tk_PathName(wmPtr->iconFor),
5143		(char *) NULL);
5144	return TCL_ERROR;
5145    }
5146    wmPtr->flags |= WM_WITHDRAWN;
5147    TkpWmSetState(winPtr, WithdrawnState);
5148    return TCL_OK;
5149}
5150
5151/*
5152 * Invoked by those wm subcommands that affect geometry.
5153 * Schedules a geometry update.
5154 */
5155static void
5156WmUpdateGeom(wmPtr, winPtr)
5157    WmInfo *wmPtr;
5158    TkWindow *winPtr;
5159{
5160    if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
5161	Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
5162	wmPtr->flags |= WM_UPDATE_PENDING;
5163    }
5164}
5165
5166	/*ARGSUSED*/
5167static void
5168WmWaitVisibilityOrMapProc(clientData, eventPtr)
5169    ClientData clientData;	/* Pointer to window. */
5170    XEvent *eventPtr;		/* Information about event. */
5171{
5172    TkWindow *winPtr = (TkWindow *) clientData;
5173    TkWindow *masterPtr = winPtr->wmInfoPtr->masterPtr;
5174
5175    if (masterPtr == NULL)
5176	return;
5177
5178    if (eventPtr->type == MapNotify) {
5179	if (!(winPtr->wmInfoPtr->flags & WM_WITHDRAWN))
5180	    TkpWmSetState(winPtr, NormalState);
5181    } else if (eventPtr->type == UnmapNotify) {
5182	TkpWmSetState(winPtr, WithdrawnState);
5183    }
5184
5185    if (eventPtr->type == VisibilityNotify) {
5186	int state = masterPtr->wmInfoPtr->hints.initial_state;
5187
5188	if ((state == NormalState) || (state == ZoomState)) {
5189	    state = winPtr->wmInfoPtr->hints.initial_state;
5190	    if ((state == NormalState) || (state == ZoomState)) {
5191		UpdateWrapper(winPtr);
5192	    }
5193	}
5194    }
5195}
5196
5197/*
5198 *----------------------------------------------------------------------
5199 *
5200 * Tk_SetGrid --
5201 *
5202 *	This procedure is invoked by a widget when it wishes to set a grid
5203 *	coordinate system that controls the size of a top-level window.
5204 *	It provides a C interface equivalent to the "wm grid" command and
5205 *	is usually asscoiated with the -setgrid option.
5206 *
5207 * Results:
5208 *	None.
5209 *
5210 * Side effects:
5211 *	Grid-related information will be passed to the window manager, so
5212 *	that the top-level window associated with tkwin will resize on
5213 *	even grid units.  If some other window already controls gridding
5214 *	for the top-level window then this procedure call has no effect.
5215 *
5216 *----------------------------------------------------------------------
5217 */
5218
5219void
5220Tk_SetGrid(tkwin, reqWidth, reqHeight, widthInc, heightInc)
5221    Tk_Window tkwin;		/* Token for window.  New window mgr info
5222				 * will be posted for the top-level window
5223				 * associated with this window. */
5224    int reqWidth;		/* Width (in grid units) corresponding to
5225				 * the requested geometry for tkwin. */
5226    int reqHeight;		/* Height (in grid units) corresponding to
5227				 * the requested geometry for tkwin. */
5228    int widthInc, heightInc;	/* Pixel increments corresponding to a
5229				 * change of one grid unit. */
5230{
5231    TkWindow *winPtr = (TkWindow *) tkwin;
5232    register WmInfo *wmPtr;
5233
5234    /*
5235     * Ensure widthInc and heightInc are greater than 0
5236     */
5237    if (widthInc <= 0) {
5238	widthInc = 1;
5239    }
5240    if (heightInc <= 0) {
5241	heightInc = 1;
5242    }
5243
5244
5245    /*
5246     * Find the top-level window for tkwin, plus the window manager
5247     * information.
5248     */
5249
5250    while (!(winPtr->flags & TK_TOP_HIERARCHY)) {
5251	winPtr = winPtr->parentPtr;
5252    }
5253    wmPtr = winPtr->wmInfoPtr;
5254    if (wmPtr == NULL) {
5255	return;
5256    }
5257
5258    if ((wmPtr->gridWin != NULL) && (wmPtr->gridWin != tkwin)) {
5259	return;
5260    }
5261
5262    if ((wmPtr->reqGridWidth == reqWidth)
5263	    && (wmPtr->reqGridHeight == reqHeight)
5264	    && (wmPtr->widthInc == widthInc)
5265	    && (wmPtr->heightInc == heightInc)
5266	    && ((wmPtr->sizeHintsFlags & (PBaseSize|PResizeInc))
5267		    == (PBaseSize|PResizeInc))) {
5268	return;
5269    }
5270
5271    /*
5272     * If gridding was previously off, then forget about any window
5273     * size requests made by the user or via "wm geometry":  these are
5274     * in pixel units and there's no easy way to translate them to
5275     * grid units since the new requested size of the top-level window in
5276     * pixels may not yet have been registered yet (it may filter up
5277     * the hierarchy in DoWhenIdle handlers).  However, if the window
5278     * has never been mapped yet then just leave the window size alone:
5279     * assume that it is intended to be in grid units but just happened
5280     * to have been specified before this procedure was called.
5281     */
5282
5283    if ((wmPtr->gridWin == NULL) && !(wmPtr->flags & WM_NEVER_MAPPED)) {
5284	wmPtr->width = -1;
5285	wmPtr->height = -1;
5286    }
5287
5288    /*
5289     * Set the new gridding information, and start the process of passing
5290     * all of this information to the window manager.
5291     */
5292
5293    wmPtr->gridWin = tkwin;
5294    wmPtr->reqGridWidth = reqWidth;
5295    wmPtr->reqGridHeight = reqHeight;
5296    wmPtr->widthInc = widthInc;
5297    wmPtr->heightInc = heightInc;
5298    wmPtr->sizeHintsFlags |= PBaseSize|PResizeInc;
5299    if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
5300	Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
5301	wmPtr->flags |= WM_UPDATE_PENDING;
5302    }
5303}
5304
5305/*
5306 *----------------------------------------------------------------------
5307 *
5308 * Tk_UnsetGrid --
5309 *
5310 *	This procedure cancels the effect of a previous call
5311 *	to Tk_SetGrid.
5312 *
5313 * Results:
5314 *	None.
5315 *
5316 * Side effects:
5317 *	If tkwin currently controls gridding for its top-level window,
5318 *	gridding is cancelled for that top-level window;  if some other
5319 *	window controls gridding then this procedure has no effect.
5320 *
5321 *----------------------------------------------------------------------
5322 */
5323
5324void
5325Tk_UnsetGrid(tkwin)
5326    Tk_Window tkwin;		/* Token for window that is currently
5327				 * controlling gridding. */
5328{
5329    TkWindow *winPtr = (TkWindow *) tkwin;
5330    register WmInfo *wmPtr;
5331
5332    /*
5333     * Find the top-level window for tkwin, plus the window manager
5334     * information.
5335     */
5336
5337    while (!(winPtr->flags & TK_TOP_HIERARCHY)) {
5338	winPtr = winPtr->parentPtr;
5339    }
5340    wmPtr = winPtr->wmInfoPtr;
5341    if (wmPtr == NULL) {
5342	return;
5343    }
5344
5345    if (tkwin != wmPtr->gridWin) {
5346	return;
5347    }
5348
5349    wmPtr->gridWin = NULL;
5350    wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc);
5351    if (wmPtr->width != -1) {
5352	wmPtr->width = winPtr->reqWidth + (wmPtr->width
5353		- wmPtr->reqGridWidth)*wmPtr->widthInc;
5354	wmPtr->height = winPtr->reqHeight + (wmPtr->height
5355		- wmPtr->reqGridHeight)*wmPtr->heightInc;
5356    }
5357    wmPtr->widthInc = 1;
5358    wmPtr->heightInc = 1;
5359
5360    if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
5361	Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
5362	wmPtr->flags |= WM_UPDATE_PENDING;
5363    }
5364}
5365
5366/*
5367 *----------------------------------------------------------------------
5368 *
5369 * TopLevelEventProc --
5370 *
5371 *	This procedure is invoked when a top-level (or other externally-
5372 *	managed window) is restructured in any way.
5373 *
5374 * Results:
5375 *	None.
5376 *
5377 * Side effects:
5378 *	Tk's internal data structures for the window get modified to
5379 *	reflect the structural change.
5380 *
5381 *----------------------------------------------------------------------
5382 */
5383
5384static void
5385TopLevelEventProc(clientData, eventPtr)
5386    ClientData clientData;		/* Window for which event occurred. */
5387    XEvent *eventPtr;			/* Event that just happened. */
5388{
5389    register TkWindow *winPtr = (TkWindow *) clientData;
5390
5391    if (eventPtr->type == DestroyNotify) {
5392	Tk_ErrorHandler handler;
5393
5394	if (!(winPtr->flags & TK_ALREADY_DEAD)) {
5395	    /*
5396	     * A top-level window was deleted externally (e.g., by the window
5397	     * manager).  This is probably not a good thing, but cleanup as
5398	     * best we can.  The error handler is needed because
5399	     * Tk_DestroyWindow will try to destroy the window, but of course
5400	     * it's already gone.
5401	     */
5402
5403	    handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
5404		    (Tk_ErrorProc *) NULL, (ClientData) NULL);
5405	    Tk_DestroyWindow((Tk_Window) winPtr);
5406	    Tk_DeleteErrorHandler(handler);
5407	}
5408    }
5409    else if (eventPtr->type == ConfigureNotify) {
5410	WmInfo *wmPtr;
5411	wmPtr = winPtr->wmInfoPtr;
5412
5413	if (winPtr->flags & TK_EMBEDDED) {
5414	    Tk_Window tkwin = (Tk_Window)winPtr;
5415	    SendMessage(wmPtr->wrapper, TK_GEOMETRYREQ, Tk_ReqWidth(tkwin),
5416	        Tk_ReqHeight(tkwin));
5417	}
5418    }
5419}
5420
5421/*
5422 *----------------------------------------------------------------------
5423 *
5424 * TopLevelReqProc --
5425 *
5426 *	This procedure is invoked by the geometry manager whenever
5427 *	the requested size for a top-level window is changed.
5428 *
5429 * Results:
5430 *	None.
5431 *
5432 * Side effects:
5433 *	Arrange for the window to be resized to satisfy the request
5434 *	(this happens as a when-idle action).
5435 *
5436 *----------------------------------------------------------------------
5437 */
5438
5439	/* ARGSUSED */
5440static void
5441TopLevelReqProc(dummy, tkwin)
5442    ClientData dummy;			/* Not used. */
5443    Tk_Window tkwin;			/* Information about window. */
5444{
5445    TkWindow *winPtr = (TkWindow *) tkwin;
5446    WmInfo *wmPtr;
5447
5448    wmPtr = winPtr->wmInfoPtr;
5449    if ((winPtr->flags & TK_EMBEDDED) && (wmPtr->wrapper != NULL)) {
5450	SendMessage(wmPtr->wrapper, TK_GEOMETRYREQ, Tk_ReqWidth(tkwin),
5451	    Tk_ReqHeight(tkwin));
5452    }
5453    if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
5454	Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
5455	wmPtr->flags |= WM_UPDATE_PENDING;
5456    }
5457}
5458
5459/*
5460 *----------------------------------------------------------------------
5461 *
5462 * UpdateGeometryInfo --
5463 *
5464 *	This procedure is invoked when a top-level window is first
5465 *	mapped, and also as a when-idle procedure, to bring the
5466 *	geometry and/or position of a top-level window back into
5467 *	line with what has been requested by the user and/or widgets.
5468 *	This procedure doesn't return until the system has
5469 *	responded to the geometry change.
5470 *
5471 * Results:
5472 *	None.
5473 *
5474 * Side effects:
5475 *	The window's size and location may change, unless the WM prevents
5476 *	that from happening.
5477 *
5478 *----------------------------------------------------------------------
5479 */
5480
5481static void
5482UpdateGeometryInfo(clientData)
5483    ClientData clientData;		/* Pointer to the window's record. */
5484{
5485    int x, y;			/* Position of border on desktop. */
5486    int width, height;		/* Size of client area. */
5487    int min, max;
5488    RECT rect;
5489    register TkWindow *winPtr = (TkWindow *) clientData;
5490    register WmInfo *wmPtr = winPtr->wmInfoPtr;
5491
5492    wmPtr->flags &= ~WM_UPDATE_PENDING;
5493
5494    /*
5495     * If the window is minimized or maximized, we should not update
5496     * our geometry since it will end up with the wrong values.
5497     * ConfigureToplevel will reschedule UpdateGeometryInfo when the
5498     * state of the window changes.
5499     */
5500
5501    if (wmPtr->wrapper && (IsIconic(wmPtr->wrapper) ||
5502	    IsZoomed(wmPtr->wrapper))) {
5503	return;
5504    }
5505
5506    /*
5507     * Compute the border size for the current window style.  This
5508     * size will include the resize handles, the title bar and the
5509     * menubar.  Note that this size will not be correct if the
5510     * menubar spans multiple lines.  The height will be off by a
5511     * multiple of the menubar height.  It really only measures the
5512     * minimum size of the border.
5513     */
5514
5515    rect.left = rect.right = rect.top = rect.bottom = 0;
5516    AdjustWindowRectEx(&rect, wmPtr->style, wmPtr->hMenu != NULL,
5517	    wmPtr->exStyle);
5518    wmPtr->borderWidth = rect.right - rect.left;
5519    wmPtr->borderHeight = rect.bottom - rect.top;
5520
5521    /*
5522     * Compute the new size for the top-level window.  See the
5523     * user documentation for details on this, but the size
5524     * requested depends on (a) the size requested internally
5525     * by the window's widgets, (b) the size requested by the
5526     * user in a "wm geometry" command or via wm-based interactive
5527     * resizing (if any), (c) whether or not the window is
5528     * gridded, and (d) the current min or max size for
5529     * the toplevel. Don't permit sizes <= 0 because this upsets
5530     * the X server.
5531     */
5532
5533    if (wmPtr->width == -1) {
5534	width = winPtr->reqWidth;
5535    } else if (wmPtr->gridWin != NULL) {
5536	width = winPtr->reqWidth
5537		+ (wmPtr->width - wmPtr->reqGridWidth)*wmPtr->widthInc;
5538    } else {
5539	width = wmPtr->width;
5540    }
5541    if (width <= 0) {
5542	width = 1;
5543    }
5544    /*
5545     * Account for window max/min width
5546     */
5547    if (wmPtr->gridWin != NULL) {
5548	min = winPtr->reqWidth
5549		+ (wmPtr->minWidth - wmPtr->reqGridWidth)*wmPtr->widthInc;
5550	if (wmPtr->maxWidth > 0) {
5551	    max = winPtr->reqWidth
5552		+ (wmPtr->maxWidth - wmPtr->reqGridWidth)*wmPtr->widthInc;
5553	} else {
5554	    max = 0;
5555	}
5556    } else {
5557	min = wmPtr->minWidth;
5558	max = wmPtr->maxWidth;
5559    }
5560    if (width < min) {
5561	width = min;
5562    } else if ((max > 0) && (width > max)) {
5563	width = max;
5564    }
5565
5566    if (wmPtr->height == -1) {
5567	height = winPtr->reqHeight;
5568    } else if (wmPtr->gridWin != NULL) {
5569	height = winPtr->reqHeight
5570		+ (wmPtr->height - wmPtr->reqGridHeight)*wmPtr->heightInc;
5571    } else {
5572	height = wmPtr->height;
5573    }
5574    if (height <= 0) {
5575	height = 1;
5576    }
5577    /*
5578     * Account for window max/min height
5579     */
5580    if (wmPtr->gridWin != NULL) {
5581	min = winPtr->reqHeight
5582		+ (wmPtr->minHeight - wmPtr->reqGridHeight)*wmPtr->heightInc;
5583	if (wmPtr->maxHeight > 0) {
5584	    max = winPtr->reqHeight
5585		+ (wmPtr->maxHeight - wmPtr->reqGridHeight)*wmPtr->heightInc;
5586	} else {
5587	    max = 0;
5588	}
5589    } else {
5590	min = wmPtr->minHeight;
5591	max = wmPtr->maxHeight;
5592    }
5593    if (height < min) {
5594	height = min;
5595    } else if ((max > 0) && (height > max)) {
5596	height = max;
5597    }
5598
5599    /*
5600     * Compute the new position for the upper-left pixel of the window's
5601     * decorative frame.  This is tricky, because we need to include the
5602     * border widths supplied by a reparented parent in this calculation,
5603     * but can't use the parent's current overall size since that may
5604     * change as a result of this code.
5605     */
5606
5607    if (wmPtr->flags & WM_NEGATIVE_X) {
5608	x = DisplayWidth(winPtr->display, winPtr->screenNum) - wmPtr->x
5609		- (width + wmPtr->borderWidth);
5610    } else {
5611	x =  wmPtr->x;
5612    }
5613    if (wmPtr->flags & WM_NEGATIVE_Y) {
5614	y = DisplayHeight(winPtr->display, winPtr->screenNum) - wmPtr->y
5615		- (height + wmPtr->borderHeight);
5616    } else {
5617	y =  wmPtr->y;
5618    }
5619
5620    /*
5621     * If this window is embedded and the container is also in this
5622     * process, we don't need to do anything special about the
5623     * geometry, except to make sure that the desired size is known
5624     * by the container.  Also, zero out any position information,
5625     * since embedded windows are not allowed to move.
5626     */
5627
5628    if (winPtr->flags & TK_BOTH_HALVES) {
5629	TkWindow *childPtr = TkpGetOtherWindow(winPtr);
5630
5631	wmPtr->x = wmPtr->y = 0;
5632	wmPtr->flags &= ~(WM_NEGATIVE_X|WM_NEGATIVE_Y);
5633	if (childPtr != NULL) {
5634	    Tk_GeometryRequest((Tk_Window) childPtr, width, height);
5635	}
5636	return;
5637    }
5638
5639    /*
5640     * Reconfigure the window if it isn't already configured correctly.  Base
5641     * the size check on what we *asked for* last time, not what we got.
5642     * Return immediately if there have been no changes in the requested
5643     * geometry of the toplevel.
5644     */
5645    /* TODO: need to add flag for possible menu size change */
5646
5647    if (!((wmPtr->flags & WM_MOVE_PENDING)
5648	    || (width != wmPtr->configWidth)
5649	    || (height != wmPtr->configHeight))) {
5650	return;
5651    }
5652    wmPtr->flags &= ~WM_MOVE_PENDING;
5653
5654    wmPtr->configWidth = width;
5655    wmPtr->configHeight = height;
5656
5657    /*
5658     * Don't bother moving the window if we are in the process of
5659     * creating it.  Just update the geometry info based on what
5660     * we asked for.
5661     */
5662
5663    if (wmPtr->flags & WM_CREATE_PENDING) {
5664	winPtr->changes.x = x;
5665	winPtr->changes.y = y;
5666	winPtr->changes.width = width;
5667	winPtr->changes.height = height;
5668	return;
5669    }
5670
5671    wmPtr->flags |= WM_SYNC_PENDING;
5672    if (winPtr->flags & TK_EMBEDDED) {
5673	/*
5674	 * The wrapper window is in a different process, so we need
5675	 * to send it a geometry request.  This protocol assumes that
5676	 * the other process understands this Tk message, otherwise
5677	 * our requested geometry will be ignored.
5678	 */
5679
5680	SendMessage(wmPtr->wrapper, TK_GEOMETRYREQ, width, height);
5681    } else {
5682	int reqHeight, reqWidth;
5683	RECT windowRect;
5684	int menuInc = GetSystemMetrics(SM_CYMENU);
5685	int newHeight;
5686
5687	/*
5688	 * We have to keep resizing the window until we get the
5689	 * requested height in the client area. If the client
5690	 * area has zero height, then the window rect is too
5691	 * small by definition. Try increasing the border height
5692	 * and try again. Once we have a positive size, then
5693	 * we can adjust the height exactly. If the window
5694	 * rect comes back smaller than we requested, we have
5695	 * hit the maximum constraints that Windows imposes.
5696	 * Once we find a positive client size, the next size
5697	 * is the one we try no matter what.
5698	 */
5699
5700	reqHeight = height + wmPtr->borderHeight;
5701	reqWidth = width + wmPtr->borderWidth;
5702
5703	while (1) {
5704	    MoveWindow(wmPtr->wrapper, x, y, reqWidth, reqHeight, TRUE);
5705	    GetWindowRect(wmPtr->wrapper, &windowRect);
5706	    newHeight = windowRect.bottom - windowRect.top;
5707
5708	    /*
5709	     * If the request wasn't satisfied, we have hit an external
5710	     * constraint and must stop.
5711	     */
5712
5713	    if (newHeight < reqHeight) {
5714		break;
5715	    }
5716
5717	    /*
5718	     * Now check the size of the client area against our ideal.
5719	     */
5720
5721	    GetClientRect(wmPtr->wrapper, &windowRect);
5722	    newHeight = windowRect.bottom - windowRect.top;
5723
5724	    if (newHeight == height) {
5725		/*
5726		 * We're done.
5727		 */
5728		break;
5729	    } else if (newHeight > height) {
5730		/*
5731		 * One last resize to get rid of the extra space.
5732		 */
5733		menuInc = newHeight - height;
5734		reqHeight -= menuInc;
5735		if (wmPtr->flags & WM_NEGATIVE_Y) {
5736		    y += menuInc;
5737		}
5738		MoveWindow(wmPtr->wrapper, x, y, reqWidth, reqHeight, TRUE);
5739		break;
5740	    }
5741
5742	    /*
5743	     * We didn't get enough space to satisfy our requested
5744	     * height, so the menu must have wrapped.  Increase the
5745	     * size of the window by one menu height and move the
5746	     * window if it is positioned relative to the lower right
5747	     * corner of the screen.
5748	     */
5749
5750	    reqHeight += menuInc;
5751	    if (wmPtr->flags & WM_NEGATIVE_Y) {
5752		y -= menuInc;
5753	    }
5754	}
5755	if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
5756	    DrawMenuBar(wmPtr->wrapper);
5757	}
5758    }
5759    wmPtr->flags &= ~WM_SYNC_PENDING;
5760}
5761
5762/*
5763 *--------------------------------------------------------------
5764 *
5765 * ParseGeometry --
5766 *
5767 *	This procedure parses a geometry string and updates
5768 *	information used to control the geometry of a top-level
5769 *	window.
5770 *
5771 * Results:
5772 *	A standard Tcl return value, plus an error message in
5773 *	the interp's result if an error occurs.
5774 *
5775 * Side effects:
5776 *	The size and/or location of winPtr may change.
5777 *
5778 *--------------------------------------------------------------
5779 */
5780
5781static int
5782ParseGeometry(interp, string, winPtr)
5783    Tcl_Interp *interp;		/* Used for error reporting. */
5784    char *string;		/* String containing new geometry.  Has the
5785				 * standard form "=wxh+x+y". */
5786    TkWindow *winPtr;		/* Pointer to top-level window whose
5787				 * geometry is to be changed. */
5788{
5789    register WmInfo *wmPtr = winPtr->wmInfoPtr;
5790    int x, y, width, height, flags;
5791    char *end;
5792    register char *p = string;
5793
5794    /*
5795     * The leading "=" is optional.
5796     */
5797
5798    if (*p == '=') {
5799	p++;
5800    }
5801
5802    /*
5803     * Parse the width and height, if they are present.  Don't
5804     * actually update any of the fields of wmPtr until we've
5805     * successfully parsed the entire geometry string.
5806     */
5807
5808    width = wmPtr->width;
5809    height = wmPtr->height;
5810    x = wmPtr->x;
5811    y = wmPtr->y;
5812    flags = wmPtr->flags;
5813    if (isdigit(UCHAR(*p))) {
5814	width = strtoul(p, &end, 10);
5815	p = end;
5816	if (*p != 'x') {
5817	    goto error;
5818	}
5819	p++;
5820	if (!isdigit(UCHAR(*p))) {
5821	    goto error;
5822	}
5823	height = strtoul(p, &end, 10);
5824	p = end;
5825    }
5826
5827    /*
5828     * Parse the X and Y coordinates, if they are present.
5829     */
5830
5831    if (*p != '\0') {
5832	flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y);
5833	if (*p == '-') {
5834	    flags |= WM_NEGATIVE_X;
5835	} else if (*p != '+') {
5836	    goto error;
5837	}
5838	p++;
5839	if (!isdigit(UCHAR(*p)) && (*p != '-')) {
5840	    goto error;
5841	}
5842	x = strtol(p, &end, 10);
5843	p = end;
5844	if (*p == '-') {
5845	    flags |= WM_NEGATIVE_Y;
5846	} else if (*p != '+') {
5847	    goto error;
5848	}
5849	p++;
5850	if (!isdigit(UCHAR(*p)) && (*p != '-')) {
5851	    goto error;
5852	}
5853	y = strtol(p, &end, 10);
5854	if (*end != '\0') {
5855	    goto error;
5856	}
5857
5858	/*
5859	 * Assume that the geometry information came from the user,
5860	 * unless an explicit source has been specified.  Otherwise
5861	 * most window managers assume that the size hints were
5862	 * program-specified and they ignore them.
5863	 */
5864
5865	if ((wmPtr->sizeHintsFlags & (USPosition|PPosition)) == 0) {
5866	    wmPtr->sizeHintsFlags |= USPosition;
5867	}
5868    }
5869
5870    /*
5871     * Everything was parsed OK.  Update the fields of *wmPtr and
5872     * arrange for the appropriate information to be percolated out
5873     * to the window manager at the next idle moment.
5874     */
5875
5876    wmPtr->width = width;
5877    wmPtr->height = height;
5878    wmPtr->x = x;
5879    wmPtr->y = y;
5880    flags |= WM_MOVE_PENDING;
5881    wmPtr->flags = flags;
5882
5883    if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
5884	Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
5885	wmPtr->flags |= WM_UPDATE_PENDING;
5886    }
5887    return TCL_OK;
5888
5889    error:
5890    Tcl_AppendResult(interp, "bad geometry specifier \"",
5891	    string, "\"", (char *) NULL);
5892    return TCL_ERROR;
5893}
5894
5895/*
5896 *----------------------------------------------------------------------
5897 *
5898 * Tk_GetRootCoords --
5899 *
5900 *	Given a token for a window, this procedure traces through the
5901 *	window's lineage to find the (virtual) root-window coordinates
5902 *	corresponding to point (0,0) in the window.
5903 *
5904 * Results:
5905 *	The locations pointed to by xPtr and yPtr are filled in with
5906 *	the root coordinates of the (0,0) point in tkwin.
5907 *
5908 * Side effects:
5909 *	None.
5910 *
5911 *----------------------------------------------------------------------
5912 */
5913
5914void
5915Tk_GetRootCoords(tkwin, xPtr, yPtr)
5916    Tk_Window tkwin;		/* Token for window. */
5917    int *xPtr;			/* Where to store x-displacement of (0,0). */
5918    int *yPtr;			/* Where to store y-displacement of (0,0). */
5919{
5920    register TkWindow *winPtr = (TkWindow *) tkwin;
5921
5922    /*
5923     * If the window is mapped, let Windows figure out the translation.
5924     */
5925
5926    if (winPtr->window != None) {
5927	HWND hwnd = Tk_GetHWND(winPtr->window);
5928	POINT point;
5929
5930	point.x = 0;
5931	point.y = 0;
5932
5933	ClientToScreen(hwnd, &point);
5934
5935	*xPtr = point.x;
5936	*yPtr = point.y;
5937    } else {
5938	*xPtr = 0;
5939	*yPtr = 0;
5940    }
5941}
5942
5943/*
5944 *----------------------------------------------------------------------
5945 *
5946 * Tk_CoordsToWindow --
5947 *
5948 *	Given the (virtual) root coordinates of a point, this procedure
5949 *	returns the token for the top-most window covering that point,
5950 *	if there exists such a window in this application.
5951 *
5952 * Results:
5953 *	The return result is either a token for the window corresponding
5954 *	to rootX and rootY, or else NULL to indicate that there is no such
5955 *	window.
5956 *
5957 * Side effects:
5958 *	None.
5959 *
5960 *----------------------------------------------------------------------
5961 */
5962
5963Tk_Window
5964Tk_CoordsToWindow(rootX, rootY, tkwin)
5965    int rootX, rootY;		/* Coordinates of point in root window.  If
5966				 * a virtual-root window manager is in use,
5967				 * these coordinates refer to the virtual
5968				 * root, not the real root. */
5969    Tk_Window tkwin;		/* Token for any window in application;
5970				 * used to identify the display. */
5971{
5972    POINT pos;
5973    HWND hwnd;
5974    TkWindow *winPtr;
5975
5976    pos.x = rootX;
5977    pos.y = rootY;
5978    hwnd = WindowFromPoint(pos);
5979
5980    winPtr = (TkWindow *) Tk_HWNDToWindow(hwnd);
5981    if (winPtr && (winPtr->mainPtr == ((TkWindow *) tkwin)->mainPtr)) {
5982	return (Tk_Window) winPtr;
5983    }
5984    return NULL;
5985}
5986
5987/*
5988 *----------------------------------------------------------------------
5989 *
5990 * Tk_GetVRootGeometry --
5991 *
5992 *	This procedure returns information about the virtual root
5993 *	window corresponding to a particular Tk window.
5994 *
5995 * Results:
5996 *	The values at xPtr, yPtr, widthPtr, and heightPtr are set
5997 *	with the offset and dimensions of the root window corresponding
5998 *	to tkwin.  If tkwin is being managed by a virtual root window
5999 *	manager these values correspond to the virtual root window being
6000 *	used for tkwin;  otherwise the offsets will be 0 and the
6001 *	dimensions will be those of the screen.
6002 *
6003 * Side effects:
6004 *	Vroot window information is refreshed if it is out of date.
6005 *
6006 *----------------------------------------------------------------------
6007 */
6008
6009void
6010Tk_GetVRootGeometry(tkwin, xPtr, yPtr, widthPtr, heightPtr)
6011    Tk_Window tkwin;		/* Window whose virtual root is to be
6012				 * queried. */
6013    int *xPtr, *yPtr;		/* Store x and y offsets of virtual root
6014				 * here. */
6015    int *widthPtr, *heightPtr;	/* Store dimensions of virtual root here. */
6016{
6017    TkWindow *winPtr = (TkWindow *) tkwin;
6018
6019    /*
6020     * XXX: This is not correct for multiple monitors.  There may be many
6021     * changes required to get this right, and it may effect existing
6022     * applications that don't consider possible <0 vroot.  See
6023     * http://msdn.microsoft.com/library/en-us/gdi/monitor_3lrn.asp
6024     * for more info.
6025     */
6026    *xPtr = 0;
6027    *yPtr = 0;
6028    *widthPtr = DisplayWidth(winPtr->display, winPtr->screenNum);
6029    *heightPtr = DisplayHeight(winPtr->display, winPtr->screenNum);
6030}
6031
6032/*
6033 *----------------------------------------------------------------------
6034 *
6035 * Tk_MoveToplevelWindow --
6036 *
6037 *	This procedure is called instead of Tk_MoveWindow to adjust
6038 *	the x-y location of a top-level window.  It delays the actual
6039 *	move to a later time and keeps window-manager information
6040 *	up-to-date with the move
6041 *
6042 * Results:
6043 *	None.
6044 *
6045 * Side effects:
6046 *	The window is eventually moved so that its upper-left corner
6047 *	(actually, the upper-left corner of the window's decorative
6048 *	frame, if there is one) is at (x,y).
6049 *
6050 *----------------------------------------------------------------------
6051 */
6052
6053void
6054Tk_MoveToplevelWindow(tkwin, x, y)
6055    Tk_Window tkwin;		/* Window to move. */
6056    int x, y;			/* New location for window (within
6057				 * parent). */
6058{
6059    TkWindow *winPtr = (TkWindow *) tkwin;
6060    register WmInfo *wmPtr = winPtr->wmInfoPtr;
6061
6062    if (!(winPtr->flags & TK_TOP_LEVEL)) {
6063	Tcl_Panic("Tk_MoveToplevelWindow called with non-toplevel window");
6064    }
6065    wmPtr->x = x;
6066    wmPtr->y = y;
6067    wmPtr->flags |= WM_MOVE_PENDING;
6068    wmPtr->flags &= ~(WM_NEGATIVE_X|WM_NEGATIVE_Y);
6069    if ((wmPtr->sizeHintsFlags & (USPosition|PPosition)) == 0) {
6070	wmPtr->sizeHintsFlags |= USPosition;
6071    }
6072
6073    /*
6074     * If the window has already been mapped, must bring its geometry
6075     * up-to-date immediately, otherwise an event might arrive from the
6076     * server that would overwrite wmPtr->x and wmPtr->y and lose the
6077     * new position.
6078     */
6079
6080    if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
6081	if (wmPtr->flags & WM_UPDATE_PENDING) {
6082	    Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
6083	}
6084	UpdateGeometryInfo((ClientData) winPtr);
6085    }
6086}
6087
6088/*
6089 *----------------------------------------------------------------------
6090 *
6091 * TkWmProtocolEventProc --
6092 *
6093 *	This procedure is called by the Tk_HandleEvent whenever a
6094 *	ClientMessage event arrives whose type is "WM_PROTOCOLS".
6095 *	This procedure handles the message from the window manager
6096 *	in an appropriate fashion.
6097 *
6098 * Results:
6099 *	None.
6100 *
6101 * Side effects:
6102 *	Depends on what sort of handler, if any, was set up for the
6103 *	protocol.
6104 *
6105 *----------------------------------------------------------------------
6106 */
6107
6108void
6109TkWmProtocolEventProc(winPtr, eventPtr)
6110    TkWindow *winPtr;		/* Window to which the event was sent. */
6111    XEvent *eventPtr;		/* X event. */
6112{
6113    WmInfo *wmPtr;
6114    register ProtocolHandler *protPtr;
6115    Atom protocol;
6116    int result;
6117    Tcl_Interp *interp;
6118
6119    wmPtr = winPtr->wmInfoPtr;
6120    if (wmPtr == NULL) {
6121	return;
6122    }
6123    protocol = (Atom) eventPtr->xclient.data.l[0];
6124    for (protPtr = wmPtr->protPtr; protPtr != NULL;
6125	    protPtr = protPtr->nextPtr) {
6126	if (protocol == protPtr->protocol) {
6127	    /*
6128	     * Cache atom name, as we might destroy the window as a
6129	     * result of the eval.
6130	     */
6131	    CONST char *name = Tk_GetAtomName((Tk_Window) winPtr, protocol);
6132
6133	    Tcl_Preserve((ClientData) protPtr);
6134            interp = protPtr->interp;
6135            Tcl_Preserve((ClientData) interp);
6136	    result = Tcl_GlobalEval(interp, protPtr->command);
6137	    if (result != TCL_OK) {
6138		Tcl_AddErrorInfo(interp, "\n    (command for \"");
6139		Tcl_AddErrorInfo(interp, name);
6140		Tcl_AddErrorInfo(interp, "\" window manager protocol)");
6141		Tcl_BackgroundError(interp);
6142	    }
6143            Tcl_Release((ClientData) interp);
6144	    Tcl_Release((ClientData) protPtr);
6145	    return;
6146	}
6147    }
6148
6149    /*
6150     * No handler was present for this protocol.  If this is a
6151     * WM_DELETE_WINDOW message then just destroy the window.
6152     */
6153
6154    if (protocol == Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW")) {
6155	Tk_DestroyWindow((Tk_Window) winPtr);
6156    }
6157}
6158
6159/*
6160 *----------------------------------------------------------------------
6161 *
6162 * TkWmStackorderToplevelEnumProc --
6163 *
6164 *	This procedure is invoked once for each HWND Window on the
6165 *	display as a result of calling EnumWindows from
6166 *	TkWmStackorderToplevel.
6167 *
6168 * Results:
6169 *	TRUE to request further iteration.
6170 *
6171 * Side effects:
6172 *	Adds entries to the passed array of TkWindows.
6173 *
6174 *----------------------------------------------------------------------
6175 */
6176
6177BOOL CALLBACK TkWmStackorderToplevelEnumProc(hwnd, lParam)
6178    HWND hwnd;     /* handle to parent window */
6179    LPARAM lParam; /* application-defined value */
6180{
6181    Tcl_HashEntry *hPtr;
6182    TkWindow *childWinPtr;
6183
6184    TkWmStackorderToplevelPair *pair =
6185        (TkWmStackorderToplevelPair *) lParam;
6186
6187    /*fprintf(stderr, "Looking up HWND %d\n", hwnd);*/
6188
6189    hPtr = Tcl_FindHashEntry(pair->table, (char *) hwnd);
6190    if (hPtr != NULL) {
6191        childWinPtr = (TkWindow *) Tcl_GetHashValue(hPtr);
6192        /* Double check that same HWND does not get passed twice */
6193        if (childWinPtr == NULL) {
6194            Tcl_Panic("duplicate HWND in TkWmStackorderToplevelEnumProc");
6195        } else {
6196            Tcl_SetHashValue(hPtr, NULL);
6197        }
6198        /*fprintf(stderr, "Found mapped HWND %d -> %x (%s)\n", hwnd,
6199	  childWinPtr, childWinPtr->pathName);*/
6200        *(pair->window_ptr)-- = childWinPtr;
6201    }
6202    return TRUE;
6203}
6204
6205/*
6206 *----------------------------------------------------------------------
6207 *
6208 * TkWmStackorderToplevelWrapperMap --
6209 *
6210 *	This procedure will create a table that maps the wrapper
6211 *	HWND id for a toplevel to the TkWindow structure that is wraps.
6212 *
6213 * Results:
6214 *	None.
6215 *
6216 * Side effects:
6217 *	Adds entries to the passed hashtable.
6218 *
6219 *----------------------------------------------------------------------
6220 */
6221
6222static void
6223TkWmStackorderToplevelWrapperMap(winPtr, display, table)
6224    TkWindow *winPtr;				/* TkWindow to recurse on */
6225    Display *display;                          /* X display of parent window */
6226    Tcl_HashTable *table;		/* Table to maps HWND to TkWindow */
6227{
6228    TkWindow *childPtr;
6229    Tcl_HashEntry *hPtr;
6230    HWND wrapper;
6231    int newEntry;
6232
6233    if (Tk_IsMapped(winPtr) && Tk_IsTopLevel(winPtr) &&
6234            !Tk_IsEmbedded(winPtr) && (winPtr->display == display)) {
6235        wrapper = TkWinGetWrapperWindow((Tk_Window) winPtr);
6236
6237        /*fprintf(stderr, "Mapped HWND %d to %x (%s)\n", wrapper,
6238	  winPtr, winPtr->pathName);*/
6239
6240        hPtr = Tcl_CreateHashEntry(table,
6241            (char *) wrapper, &newEntry);
6242        Tcl_SetHashValue(hPtr, winPtr);
6243    }
6244
6245    for (childPtr = winPtr->childList; childPtr != NULL;
6246            childPtr = childPtr->nextPtr) {
6247        TkWmStackorderToplevelWrapperMap(childPtr, display, table);
6248    }
6249}
6250/*
6251 *----------------------------------------------------------------------
6252 *
6253 * TkWmStackorderToplevel --
6254 *
6255 *	This procedure returns the stack order of toplevel windows.
6256 *
6257 * Results:
6258 *	An array of pointers to tk window objects in stacking order
6259 *	or else NULL if there was an error.
6260 *
6261 * Side effects:
6262 *	None.
6263 *
6264 *----------------------------------------------------------------------
6265 */
6266
6267TkWindow **
6268TkWmStackorderToplevel(parentPtr)
6269    TkWindow *parentPtr;		/* Parent toplevel window. */
6270{
6271    TkWmStackorderToplevelPair pair;
6272    TkWindow **windows;
6273    Tcl_HashTable table;
6274    Tcl_HashEntry *hPtr;
6275    Tcl_HashSearch search;
6276
6277    /*
6278     * Map HWND ids to a TkWindow of the wrapped toplevel.
6279     */
6280
6281    Tcl_InitHashTable(&table, TCL_ONE_WORD_KEYS);
6282    TkWmStackorderToplevelWrapperMap(parentPtr, parentPtr->display, &table);
6283
6284    windows = (TkWindow **) ckalloc((table.numEntries+1)
6285        * sizeof(TkWindow *));
6286
6287    /*
6288     * Special cases: If zero or one toplevels were mapped
6289     * there is no need to call EnumWindows.
6290     */
6291
6292    switch (table.numEntries) {
6293    case 0:
6294        windows[0] = NULL;
6295        goto done;
6296    case 1:
6297        hPtr = Tcl_FirstHashEntry(&table, &search);
6298        windows[0] = (TkWindow *) Tcl_GetHashValue(hPtr);
6299        windows[1] = NULL;
6300        goto done;
6301    }
6302
6303    /*
6304     * We will be inserting into the array starting at the end
6305     * and working our way to the beginning since EnumWindows
6306     * returns windows in highest to lowest order.
6307     */
6308
6309    pair.table = &table;
6310    pair.window_ptr = windows + table.numEntries;
6311    *pair.window_ptr-- = NULL;
6312
6313    if (EnumWindows((WNDENUMPROC) TkWmStackorderToplevelEnumProc,
6314	    (LPARAM) &pair) == 0) {
6315        ckfree((char *) windows);
6316        windows = NULL;
6317    } else {
6318        if (pair.window_ptr != (windows-1))
6319            Tcl_Panic("num matched toplevel windows does not equal num children");
6320    }
6321
6322    done:
6323    Tcl_DeleteHashTable(&table);
6324    return windows;
6325}
6326
6327/*
6328 *----------------------------------------------------------------------
6329 *
6330 * TkWmRestackToplevel --
6331 *
6332 *	This procedure restacks a top-level window.
6333 *
6334 * Results:
6335 *	None.
6336 *
6337 * Side effects:
6338 *	WinPtr gets restacked  as specified by aboveBelow and otherPtr.
6339 *	This procedure doesn't return until the restack has taken
6340 *	effect and the ConfigureNotify event for it has been received.
6341 *
6342 *----------------------------------------------------------------------
6343 */
6344
6345void
6346TkWmRestackToplevel(winPtr, aboveBelow, otherPtr)
6347    TkWindow *winPtr;		/* Window to restack. */
6348    int aboveBelow;		/* Gives relative position for restacking;
6349				 * must be Above or Below. */
6350    TkWindow *otherPtr;		/* Window relative to which to restack;
6351				 * if NULL, then winPtr gets restacked
6352				 * above or below *all* siblings. */
6353{
6354    HWND hwnd, insertAfter;
6355
6356    /*
6357     * Can't set stacking order properly until the window is on the
6358     * screen (mapping it may give it a reparent window).
6359     */
6360
6361    if (winPtr->window == None) {
6362	Tk_MakeWindowExist((Tk_Window) winPtr);
6363    }
6364    if (winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
6365	TkWmMapWindow(winPtr);
6366    }
6367    hwnd = (winPtr->wmInfoPtr->wrapper != NULL)
6368	? winPtr->wmInfoPtr->wrapper : Tk_GetHWND(winPtr->window);
6369
6370    if (otherPtr != NULL) {
6371	if (otherPtr->window == None) {
6372	    Tk_MakeWindowExist((Tk_Window) otherPtr);
6373	}
6374	if (otherPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
6375	    TkWmMapWindow(otherPtr);
6376	}
6377	insertAfter = (otherPtr->wmInfoPtr->wrapper != NULL)
6378	    ? otherPtr->wmInfoPtr->wrapper : Tk_GetHWND(otherPtr->window);
6379    } else {
6380	insertAfter = NULL;
6381    }
6382
6383    TkWinSetWindowPos(hwnd, insertAfter, aboveBelow);
6384}
6385
6386/*
6387 *----------------------------------------------------------------------
6388 *
6389 * TkWmAddToColormapWindows --
6390 *
6391 *	This procedure is called to add a given window to the
6392 *	WM_COLORMAP_WINDOWS property for its top-level, if it
6393 *	isn't already there.  It is invoked by the Tk code that
6394 *	creates a new colormap, in order to make sure that colormap
6395 *	information is propagated to the window manager by default.
6396 *
6397 * Results:
6398 *	None.
6399 *
6400 * Side effects:
6401 *	WinPtr's window gets added to the WM_COLORMAP_WINDOWS
6402 *	property of its nearest top-level ancestor, unless the
6403 *	colormaps have been set explicitly with the
6404 *	"wm colormapwindows" command.
6405 *
6406 *----------------------------------------------------------------------
6407 */
6408
6409void
6410TkWmAddToColormapWindows(winPtr)
6411    TkWindow *winPtr;		/* Window with a non-default colormap.
6412				 * Should not be a top-level window. */
6413{
6414    TkWindow *topPtr;
6415    TkWindow **oldPtr, **newPtr;
6416    int count, i;
6417
6418    if (winPtr->window == None) {
6419	return;
6420    }
6421
6422    for (topPtr = winPtr->parentPtr; ; topPtr = topPtr->parentPtr) {
6423	if (topPtr == NULL) {
6424	    /*
6425	     * Window is being deleted.  Skip the whole operation.
6426	     */
6427
6428	    return;
6429	}
6430	if (topPtr->flags & TK_TOP_HIERARCHY) {
6431	    break;
6432	}
6433    }
6434    if (topPtr->wmInfoPtr == NULL) {
6435	return;
6436    }
6437
6438    if (topPtr->wmInfoPtr->flags & WM_COLORMAPS_EXPLICIT) {
6439	return;
6440    }
6441
6442    /*
6443     * Make sure that the window isn't already in the list.
6444     */
6445
6446    count = topPtr->wmInfoPtr->cmapCount;
6447    oldPtr = topPtr->wmInfoPtr->cmapList;
6448
6449    for (i = 0; i < count; i++) {
6450	if (oldPtr[i] == winPtr) {
6451	    return;
6452	}
6453    }
6454
6455    /*
6456     * Make a new bigger array and use it to reset the property.
6457     * Automatically add the toplevel itself as the last element
6458     * of the list.
6459     */
6460
6461    newPtr = (TkWindow **) ckalloc((unsigned) ((count+2)*sizeof(TkWindow*)));
6462    if (count > 0) {
6463	memcpy(newPtr, oldPtr, count * sizeof(TkWindow*));
6464    }
6465    if (count == 0) {
6466	count++;
6467    }
6468    newPtr[count-1] = winPtr;
6469    newPtr[count] = topPtr;
6470    if (oldPtr != NULL) {
6471	ckfree((char *) oldPtr);
6472    }
6473
6474    topPtr->wmInfoPtr->cmapList = newPtr;
6475    topPtr->wmInfoPtr->cmapCount = count+1;
6476
6477    /*
6478     * Now we need to force the updated colormaps to be installed.
6479     */
6480
6481    if (topPtr->wmInfoPtr == winPtr->dispPtr->foregroundWmPtr) {
6482	InstallColormaps(topPtr->wmInfoPtr->wrapper, WM_QUERYNEWPALETTE, 1);
6483    } else {
6484	InstallColormaps(topPtr->wmInfoPtr->wrapper, WM_PALETTECHANGED, 0);
6485    }
6486}
6487
6488/*
6489 *----------------------------------------------------------------------
6490 *
6491 * TkWmRemoveFromColormapWindows --
6492 *
6493 *	This procedure is called to remove a given window from the
6494 *	WM_COLORMAP_WINDOWS property for its top-level.  It is invoked
6495 *	when windows are deleted.
6496 *
6497 * Results:
6498 *	None.
6499 *
6500 * Side effects:
6501 *	WinPtr's window gets removed from the WM_COLORMAP_WINDOWS
6502 *	property of its nearest top-level ancestor, unless the
6503 *	top-level itself is being deleted too.
6504 *
6505 *----------------------------------------------------------------------
6506 */
6507
6508void
6509TkWmRemoveFromColormapWindows(winPtr)
6510    TkWindow *winPtr;		/* Window that may be present in
6511				 * WM_COLORMAP_WINDOWS property for its
6512				 * top-level.  Should not be a top-level
6513				 * window. */
6514{
6515    TkWindow *topPtr;
6516    TkWindow **oldPtr;
6517    int count, i, j;
6518
6519    for (topPtr = winPtr->parentPtr; ; topPtr = topPtr->parentPtr) {
6520	if (topPtr == NULL) {
6521	    /*
6522	     * Ancestors have been deleted, so skip the whole operation.
6523	     * Seems like this can't ever happen?
6524	     */
6525
6526	    return;
6527	}
6528	if (topPtr->flags & TK_TOP_LEVEL) {
6529	    break;
6530	}
6531    }
6532    if (topPtr->flags & TK_ALREADY_DEAD) {
6533	/*
6534	 * Top-level is being deleted, so there's no need to cleanup
6535	 * the WM_COLORMAP_WINDOWS property.
6536	 */
6537
6538	return;
6539    }
6540
6541    if (topPtr->wmInfoPtr == NULL) {
6542	return;
6543    }
6544
6545    /*
6546     * Find the window and slide the following ones down to cover
6547     * it up.
6548     */
6549
6550    count = topPtr->wmInfoPtr->cmapCount;
6551    oldPtr = topPtr->wmInfoPtr->cmapList;
6552    for (i = 0; i < count; i++) {
6553	if (oldPtr[i] == winPtr) {
6554	    for (j = i ; j < count-1; j++) {
6555		oldPtr[j] = oldPtr[j+1];
6556	    }
6557	    topPtr->wmInfoPtr->cmapCount = count-1;
6558	    break;
6559	}
6560    }
6561}
6562
6563/*
6564 *----------------------------------------------------------------------
6565 *
6566 * TkWinSetMenu--
6567 *
6568 *	Associcates a given HMENU to a window.
6569 *
6570 * Results:
6571 *	None.
6572 *
6573 * Side effects:
6574 *	The menu will end up being drawn in the window, and the geometry
6575 *	of the window will have to be changed.
6576 *
6577 *----------------------------------------------------------------------
6578 */
6579
6580void
6581TkWinSetMenu(tkwin, hMenu)
6582    Tk_Window tkwin;		/* the window to put the menu in */
6583    HMENU hMenu;		/* the menu to set */
6584{
6585    TkWindow *winPtr = (TkWindow *) tkwin;
6586    WmInfo *wmPtr = winPtr->wmInfoPtr;
6587
6588    wmPtr->hMenu = hMenu;
6589
6590    if (!(wmPtr->flags & TK_EMBEDDED)) {
6591	if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
6592	    int syncPending = wmPtr->flags & WM_SYNC_PENDING;
6593
6594	    wmPtr->flags |= WM_SYNC_PENDING;
6595	    SetMenu(wmPtr->wrapper, hMenu);
6596	    if (!syncPending) {
6597		wmPtr->flags &= ~WM_SYNC_PENDING;
6598	    }
6599	}
6600	if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
6601	    Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
6602	    wmPtr->flags |= WM_UPDATE_PENDING|WM_MOVE_PENDING;
6603	}
6604    }
6605}
6606
6607/*
6608 *----------------------------------------------------------------------
6609 *
6610 * ConfigureTopLevel --
6611 *
6612 *	Generate a ConfigureNotify event based on the current position
6613 *	information.  This procedure is called by TopLevelProc.
6614 *
6615 * Results:
6616 *	None.
6617 *
6618 * Side effects:
6619 *	Queues a new event.
6620 *
6621 *----------------------------------------------------------------------
6622 */
6623
6624static void
6625ConfigureTopLevel(pos)
6626    WINDOWPOS *pos;
6627{
6628    TkWindow *winPtr = GetTopLevel(pos->hwnd);
6629    WmInfo *wmPtr;
6630    int state;			/* Current window state. */
6631    RECT rect;
6632    WINDOWPLACEMENT windowPos;
6633
6634    if (winPtr == NULL) {
6635	return;
6636    }
6637
6638    wmPtr = winPtr->wmInfoPtr;
6639
6640    /*
6641     * Determine the current window state.
6642     */
6643
6644    if (!IsWindowVisible(wmPtr->wrapper)) {
6645	state = WithdrawnState;
6646    } else {
6647	windowPos.length = sizeof(WINDOWPLACEMENT);
6648	GetWindowPlacement(wmPtr->wrapper, &windowPos);
6649	switch (windowPos.showCmd) {
6650	    case SW_SHOWMAXIMIZED:
6651		state = ZoomState;
6652		break;
6653	    case SW_SHOWMINIMIZED:
6654		state = IconicState;
6655		break;
6656	    case SW_SHOWNORMAL:
6657		state = NormalState;
6658		break;
6659	}
6660    }
6661
6662    /*
6663     * If the state of the window just changed, be sure to update the
6664     * child window information.
6665     */
6666
6667    if (wmPtr->hints.initial_state != state) {
6668	wmPtr->hints.initial_state = state;
6669	switch (state) {
6670	    case WithdrawnState:
6671	    case IconicState:
6672		XUnmapWindow(winPtr->display, winPtr->window);
6673		break;
6674
6675	    case NormalState:
6676		/*
6677		 * Schedule a geometry update.  Since we ignore geometry
6678		 * requests while in any other state, the geometry info
6679		 * may be stale.
6680		 */
6681
6682		if (!(wmPtr->flags & WM_UPDATE_PENDING)) {
6683		    Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
6684		    wmPtr->flags |= WM_UPDATE_PENDING;
6685		}
6686		/* fall through */
6687	    case ZoomState:
6688		XMapWindow(winPtr->display, winPtr->window);
6689		pos->flags |= SWP_NOMOVE | SWP_NOSIZE;
6690		break;
6691	}
6692    }
6693
6694    /*
6695     * Don't report geometry changes in the Iconic or Withdrawn states.
6696     */
6697
6698    if (state == WithdrawnState || state == IconicState) {
6699	return;
6700    }
6701
6702
6703    /*
6704     * Compute the current geometry of the client area, reshape the
6705     * Tk window and generate a ConfigureNotify event.
6706     */
6707
6708    GetClientRect(wmPtr->wrapper, &rect);
6709    winPtr->changes.x = pos->x;
6710    winPtr->changes.y = pos->y;
6711    winPtr->changes.width = rect.right - rect.left;
6712    winPtr->changes.height = rect.bottom - rect.top;
6713    wmPtr->borderHeight = pos->cy - winPtr->changes.height;
6714    MoveWindow(Tk_GetHWND(winPtr->window), 0, 0,
6715	    winPtr->changes.width, winPtr->changes.height, TRUE);
6716    GenerateConfigureNotify(winPtr);
6717
6718    /*
6719     * Update window manager geometry info if needed.
6720     */
6721
6722    if (state == NormalState) {
6723
6724	/*
6725	 * Update size information from the event.  There are a couple of
6726	 * tricky points here:
6727	 *
6728	 * 1. If the user changed the size externally then set wmPtr->width
6729	 *    and wmPtr->height just as if a "wm geometry" command had been
6730	 *    invoked with the same information.
6731	 * 2. However, if the size is changing in response to a request
6732	 *    coming from us (sync is set), then don't set
6733	 *    wmPtr->width or wmPtr->height (otherwise the window will stop
6734	 *    tracking geometry manager requests).
6735	 */
6736
6737	if (!(wmPtr->flags & WM_SYNC_PENDING)) {
6738	    if (!(pos->flags & SWP_NOSIZE)) {
6739		if ((wmPtr->width == -1)
6740			&& (winPtr->changes.width == winPtr->reqWidth)) {
6741		    /*
6742		     * Don't set external width, since the user didn't
6743		     * change it from what the widgets asked for.
6744		     */
6745		} else {
6746		    if (wmPtr->gridWin != NULL) {
6747			wmPtr->width = wmPtr->reqGridWidth
6748			    + (winPtr->changes.width - winPtr->reqWidth)
6749			    / wmPtr->widthInc;
6750			if (wmPtr->width < 0) {
6751			    wmPtr->width = 0;
6752			}
6753		    } else {
6754			wmPtr->width = winPtr->changes.width;
6755		    }
6756		}
6757		if ((wmPtr->height == -1)
6758			&& (winPtr->changes.height == winPtr->reqHeight)) {
6759		    /*
6760		     * Don't set external height, since the user didn't change
6761		     * it from what the widgets asked for.
6762		     */
6763		} else {
6764		    if (wmPtr->gridWin != NULL) {
6765			wmPtr->height = wmPtr->reqGridHeight
6766			    + (winPtr->changes.height - winPtr->reqHeight)
6767			    / wmPtr->heightInc;
6768			if (wmPtr->height < 0) {
6769			    wmPtr->height = 0;
6770			}
6771		    } else {
6772			wmPtr->height = winPtr->changes.height;
6773		    }
6774		}
6775		wmPtr->configWidth = winPtr->changes.width;
6776		wmPtr->configHeight = winPtr->changes.height;
6777	    }
6778	    /*
6779	     * If the user moved the window, we should switch back
6780	     * to normal coordinates.
6781	     */
6782
6783	    if (!(pos->flags & SWP_NOMOVE)) {
6784		wmPtr->flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y);
6785	    }
6786	}
6787
6788	/*
6789	 * Update the wrapper window location information.
6790	 */
6791
6792	if (wmPtr->flags & WM_NEGATIVE_X) {
6793	    wmPtr->x = DisplayWidth(winPtr->display, winPtr->screenNum)
6794		- winPtr->changes.x - (winPtr->changes.width
6795			+ wmPtr->borderWidth);
6796	} else {
6797	    wmPtr->x = winPtr->changes.x;
6798	}
6799	if (wmPtr->flags & WM_NEGATIVE_Y) {
6800	    wmPtr->y = DisplayHeight(winPtr->display, winPtr->screenNum)
6801		- winPtr->changes.y - (winPtr->changes.height
6802			+ wmPtr->borderHeight);
6803	} else {
6804	    wmPtr->y = winPtr->changes.y;
6805	}
6806    }
6807}
6808
6809/*
6810 *----------------------------------------------------------------------
6811 *
6812 * GenerateConfigureNotify --
6813 *
6814 *	Generate a ConfigureNotify event from the current geometry
6815 *	information for the specified toplevel window.
6816 *
6817 * Results:
6818 *	None.
6819 *
6820 * Side effects:
6821 *	Sends an X event.
6822 *
6823 *----------------------------------------------------------------------
6824 */
6825
6826static void
6827GenerateConfigureNotify(winPtr)
6828    TkWindow *winPtr;
6829{
6830    XEvent event;
6831
6832    /*
6833     * Generate a ConfigureNotify event.
6834     */
6835
6836    event.type = ConfigureNotify;
6837    event.xconfigure.serial = winPtr->display->request;
6838    event.xconfigure.send_event = False;
6839    event.xconfigure.display = winPtr->display;
6840    event.xconfigure.event = winPtr->window;
6841    event.xconfigure.window = winPtr->window;
6842    event.xconfigure.border_width = winPtr->changes.border_width;
6843    event.xconfigure.override_redirect = winPtr->atts.override_redirect;
6844    event.xconfigure.x = winPtr->changes.x;
6845    event.xconfigure.y = winPtr->changes.y;
6846    event.xconfigure.width = winPtr->changes.width;
6847    event.xconfigure.height = winPtr->changes.height;
6848    event.xconfigure.above = None;
6849    Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
6850}
6851
6852/*
6853 *----------------------------------------------------------------------
6854 *
6855 * InstallColormaps --
6856 *
6857 *	Installs the colormaps associated with the toplevel which is
6858 *	currently active.
6859 *
6860 * Results:
6861 *	None.
6862 *
6863 * Side effects:
6864 *	May change the system palette and generate damage.
6865 *
6866 *----------------------------------------------------------------------
6867 */
6868
6869static int
6870InstallColormaps(hwnd, message, isForemost)
6871    HWND hwnd;			/* Toplevel wrapper window whose colormaps
6872				 * should be installed. */
6873    int message;		/* Either WM_PALETTECHANGED or
6874				 * WM_QUERYNEWPALETTE */
6875    int isForemost;		/* 1 if window is foremost, else 0 */
6876{
6877    int i;
6878    HDC dc;
6879    HPALETTE oldPalette;
6880    TkWindow *winPtr = GetTopLevel(hwnd);
6881    WmInfo *wmPtr;
6882    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
6883            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
6884
6885    if (winPtr == NULL || (winPtr->flags & TK_ALREADY_DEAD) ) {
6886	return 0;
6887    }
6888
6889    wmPtr = winPtr->wmInfoPtr;
6890
6891    if (message == WM_QUERYNEWPALETTE) {
6892	/*
6893	 * Case 1: This window is about to become the foreground window, so we
6894	 * need to install the primary palette. If the system palette was
6895	 * updated, then Windows will generate a WM_PALETTECHANGED message.
6896	 * Otherwise, we have to synthesize one in order to ensure that the
6897	 * secondary palettes are installed properly.
6898	 */
6899
6900	winPtr->dispPtr->foregroundWmPtr = wmPtr;
6901
6902	if (wmPtr->cmapCount > 0) {
6903	    winPtr = wmPtr->cmapList[0];
6904	}
6905
6906	tsdPtr->systemPalette = TkWinGetPalette(winPtr->atts.colormap);
6907	dc = GetDC(hwnd);
6908	oldPalette = SelectPalette(dc, tsdPtr->systemPalette, FALSE);
6909	if (RealizePalette(dc)) {
6910	    RefreshColormap(winPtr->atts.colormap, winPtr->dispPtr);
6911	} else if (wmPtr->cmapCount > 1) {
6912	    SelectPalette(dc, oldPalette, TRUE);
6913	    RealizePalette(dc);
6914	    ReleaseDC(hwnd, dc);
6915	    SendMessage(hwnd, WM_PALETTECHANGED, (WPARAM)hwnd,
6916		    (LPARAM)NULL);
6917	    return TRUE;
6918	}
6919
6920    } else {
6921	/*
6922	 * Window is being notified of a change in the system palette.
6923	 * If this window is the foreground window, then we should only
6924	 * install the secondary palettes, since the primary was installed
6925	 * in response to the WM_QUERYPALETTE message.  Otherwise, install
6926	 * all of the palettes.
6927	 */
6928
6929
6930	if (!isForemost) {
6931	    if (wmPtr->cmapCount > 0) {
6932		winPtr = wmPtr->cmapList[0];
6933	    }
6934	    i = 1;
6935	} else {
6936	    if (wmPtr->cmapCount <= 1) {
6937		return TRUE;
6938	    }
6939	    winPtr = wmPtr->cmapList[1];
6940	    i = 2;
6941	}
6942	dc = GetDC(hwnd);
6943	oldPalette = SelectPalette(dc,
6944		TkWinGetPalette(winPtr->atts.colormap), TRUE);
6945	if (RealizePalette(dc)) {
6946	    RefreshColormap(winPtr->atts.colormap, winPtr->dispPtr);
6947	}
6948	for (; i < wmPtr->cmapCount; i++) {
6949	    winPtr = wmPtr->cmapList[i];
6950	    SelectPalette(dc, TkWinGetPalette(winPtr->atts.colormap), TRUE);
6951	    if (RealizePalette(dc)) {
6952		RefreshColormap(winPtr->atts.colormap, winPtr->dispPtr);
6953	    }
6954	}
6955    }
6956
6957    SelectPalette(dc, oldPalette, TRUE);
6958    RealizePalette(dc);
6959    ReleaseDC(hwnd, dc);
6960    return TRUE;
6961}
6962
6963/*
6964 *----------------------------------------------------------------------
6965 *
6966 * RefreshColormap --
6967 *
6968 *	This function is called to force all of the windows that use
6969 *	a given colormap to redraw themselves.  The quickest way to
6970 *	do this is to iterate over the toplevels, looking in the
6971 *	cmapList for matches.  This will quickly eliminate subtrees
6972 *	that don't use a given colormap.
6973 *
6974 * Results:
6975 *	None.
6976 *
6977 * Side effects:
6978 *	Causes damage events to be generated.
6979 *
6980 *----------------------------------------------------------------------
6981 */
6982
6983static void
6984RefreshColormap(colormap, dispPtr)
6985    Colormap colormap;
6986    TkDisplay *dispPtr;
6987{
6988    WmInfo *wmPtr;
6989    int i;
6990
6991    for (wmPtr = dispPtr->firstWmPtr; wmPtr != NULL; wmPtr = wmPtr->nextPtr) {
6992	if (wmPtr->cmapCount > 0) {
6993	    for (i = 0; i < wmPtr->cmapCount; i++) {
6994		if ((wmPtr->cmapList[i]->atts.colormap == colormap)
6995			&& Tk_IsMapped(wmPtr->cmapList[i])) {
6996		    InvalidateSubTree(wmPtr->cmapList[i], colormap);
6997		}
6998	    }
6999	} else if ((wmPtr->winPtr->atts.colormap == colormap)
7000		&& Tk_IsMapped(wmPtr->winPtr)) {
7001	    InvalidateSubTree(wmPtr->winPtr, colormap);
7002	}
7003    }
7004}
7005
7006/*
7007 *----------------------------------------------------------------------
7008 *
7009 * InvalidateSubTree --
7010 *
7011 *	This function recursively generates damage for a window and
7012 *	all of its mapped children that belong to the same toplevel and
7013 *	are using the specified colormap.
7014 *
7015 * Results:
7016 *	None.
7017 *
7018 * Side effects:
7019 *	Generates damage for the specified subtree.
7020 *
7021 *----------------------------------------------------------------------
7022 */
7023
7024static void
7025InvalidateSubTree(winPtr, colormap)
7026    TkWindow *winPtr;
7027    Colormap colormap;
7028{
7029    TkWindow *childPtr;
7030
7031    /*
7032     * Generate damage for the current window if it is using the
7033     * specified colormap.
7034     */
7035
7036    if (winPtr->atts.colormap == colormap) {
7037	InvalidateRect(Tk_GetHWND(winPtr->window), NULL, FALSE);
7038    }
7039
7040    for (childPtr = winPtr->childList; childPtr != NULL;
7041	    childPtr = childPtr->nextPtr) {
7042	/*
7043	 * We can stop the descent when we hit an unmapped or
7044	 * toplevel window.
7045	 */
7046
7047	if (!Tk_TopWinHierarchy(childPtr) && Tk_IsMapped(childPtr)) {
7048	    InvalidateSubTree(childPtr, colormap);
7049	}
7050    }
7051}
7052
7053/*
7054 *----------------------------------------------------------------------
7055 *
7056 * InvalidateSubTreeDepth --
7057 *
7058 *	This function recursively updates depth info for a window and
7059 *	all of its children that belong to the same toplevel.
7060 *
7061 * Results:
7062 *	None.
7063 *
7064 * Side effects:
7065 *	Sets the depth of each window to that of the display.
7066 *
7067 *----------------------------------------------------------------------
7068 */
7069
7070static void
7071InvalidateSubTreeDepth(winPtr)
7072    TkWindow *winPtr;
7073{
7074    Display *display = Tk_Display(winPtr);
7075    int screenNum = Tk_ScreenNumber(winPtr);
7076    TkWindow *childPtr;
7077
7078    winPtr->depth = DefaultDepth(display, screenNum);
7079
7080#if 0
7081    /*
7082     * XXX: What other elements may require changes?  Changing just
7083     * the depth works for standard windows and 16/24/32-bpp changes.
7084     * I suspect 8-bit (palettized) displays may require colormap and/or
7085     * visual changes as well.
7086     */
7087
7088    if (winPtr->window) {
7089	InvalidateRect(Tk_GetHWND(winPtr->window), NULL, FALSE);
7090    }
7091    winPtr->visual = DefaultVisual(display, screenNum);
7092    winPtr->atts.colormap = DefaultColormap(display, screenNum);
7093    winPtr->dirtyAtts |= CWColormap;
7094#endif
7095
7096    for (childPtr = winPtr->childList; childPtr != NULL;
7097	 childPtr = childPtr->nextPtr) {
7098	/*
7099	 * We can stop the descent when we hit a toplevel window, as it
7100	 * should get its own message.
7101	 */
7102
7103	if (!Tk_TopWinHierarchy(childPtr)) {
7104	    InvalidateSubTreeDepth(childPtr);
7105	}
7106    }
7107}
7108
7109/*
7110 *----------------------------------------------------------------------
7111 *
7112 * TkWinGetSystemPalette --
7113 *
7114 *	Retrieves the currently installed foreground palette.
7115 *
7116 * Results:
7117 *	Returns the global foreground palette, if there is one.
7118 *	Otherwise, returns NULL.
7119 *
7120 * Side effects:
7121 *	None.
7122 *
7123 *----------------------------------------------------------------------
7124 */
7125
7126HPALETTE
7127TkWinGetSystemPalette()
7128{
7129    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
7130            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
7131
7132    return tsdPtr->systemPalette;
7133}
7134
7135/*
7136 *----------------------------------------------------------------------
7137 *
7138 * GetMinSize --
7139 *
7140 *	This procedure computes the current minWidth and minHeight
7141 *	values for a window, taking into account the possibility
7142 *	that they may be defaulted.
7143 *
7144 * Results:
7145 *	The values at *minWidthPtr and *minHeightPtr are filled
7146 *	in with the minimum allowable dimensions of wmPtr's window,
7147 *	in grid units.  If the requested minimum is smaller than the
7148 *	system required minimum, then this procedure computes the
7149 *	smallest size that will satisfy both the system and the
7150 *	grid constraints.
7151 *
7152 * Side effects:
7153 *	None.
7154 *
7155 *----------------------------------------------------------------------
7156 */
7157
7158static void
7159GetMinSize(wmPtr, minWidthPtr, minHeightPtr)
7160    WmInfo *wmPtr;		/* Window manager information for the
7161				 * window. */
7162    int *minWidthPtr;		/* Where to store the current minimum
7163				 * width of the window. */
7164    int *minHeightPtr;		/* Where to store the current minimum
7165				 * height of the window. */
7166{
7167    int tmp, base;
7168    TkWindow *winPtr = wmPtr->winPtr;
7169
7170    /*
7171     * Compute the minimum width by taking the default client size
7172     * and rounding it up to the nearest grid unit.  Return the greater
7173     * of the default minimum and the specified minimum.
7174     */
7175
7176    tmp = wmPtr->defMinWidth - wmPtr->borderWidth;
7177    if (tmp < 0) {
7178	tmp = 0;
7179    }
7180    if (wmPtr->gridWin != NULL) {
7181	base = winPtr->reqWidth - (wmPtr->reqGridWidth * wmPtr->widthInc);
7182	if (base < 0) {
7183	    base = 0;
7184	}
7185	tmp = ((tmp - base) + wmPtr->widthInc - 1)/wmPtr->widthInc;
7186    }
7187    if (tmp < wmPtr->minWidth) {
7188	tmp = wmPtr->minWidth;
7189    }
7190    *minWidthPtr = tmp;
7191
7192    /*
7193     * Compute the minimum height in a similar fashion.
7194     */
7195
7196    tmp = wmPtr->defMinHeight - wmPtr->borderHeight;
7197    if (tmp < 0) {
7198	tmp = 0;
7199    }
7200    if (wmPtr->gridWin != NULL) {
7201	base = winPtr->reqHeight - (wmPtr->reqGridHeight * wmPtr->heightInc);
7202	if (base < 0) {
7203	    base = 0;
7204	}
7205	tmp = ((tmp - base) + wmPtr->heightInc - 1)/wmPtr->heightInc;
7206    }
7207    if (tmp < wmPtr->minHeight) {
7208	tmp = wmPtr->minHeight;
7209    }
7210    *minHeightPtr = tmp;
7211}
7212
7213/*
7214 *----------------------------------------------------------------------
7215 *
7216 * GetMaxSize --
7217 *
7218 *	This procedure computes the current maxWidth and maxHeight
7219 *	values for a window, taking into account the possibility
7220 *	that they may be defaulted.
7221 *
7222 * Results:
7223 *	The values at *maxWidthPtr and *maxHeightPtr are filled
7224 *	in with the maximum allowable dimensions of wmPtr's window,
7225 *	in grid units.  If no maximum has been specified for the
7226 *	window, then this procedure computes the largest sizes that
7227 *	will fit on the screen.
7228 *
7229 * Side effects:
7230 *	None.
7231 *
7232 *----------------------------------------------------------------------
7233 */
7234
7235static void
7236GetMaxSize(wmPtr, maxWidthPtr, maxHeightPtr)
7237    WmInfo *wmPtr;		/* Window manager information for the
7238				 * window. */
7239    int *maxWidthPtr;		/* Where to store the current maximum
7240				 * width of the window. */
7241    int *maxHeightPtr;		/* Where to store the current maximum
7242				 * height of the window. */
7243{
7244    int tmp;
7245
7246    if (wmPtr->maxWidth > 0) {
7247	*maxWidthPtr = wmPtr->maxWidth;
7248    } else {
7249	/*
7250	 * Must compute a default width.  Fill up the display, leaving a
7251	 * bit of extra space for the window manager's borders.
7252	 */
7253
7254	tmp = wmPtr->defMaxWidth - wmPtr->borderWidth;
7255	if (wmPtr->gridWin != NULL) {
7256	    /*
7257	     * Gridding is turned on;  convert from pixels to grid units.
7258	     */
7259
7260	    tmp = wmPtr->reqGridWidth
7261		    + (tmp - wmPtr->winPtr->reqWidth)/wmPtr->widthInc;
7262	}
7263	*maxWidthPtr = tmp;
7264    }
7265    if (wmPtr->maxHeight > 0) {
7266	*maxHeightPtr = wmPtr->maxHeight;
7267    } else {
7268	tmp = wmPtr->defMaxHeight - wmPtr->borderHeight;
7269	if (wmPtr->gridWin != NULL) {
7270	    tmp = wmPtr->reqGridHeight
7271		    + (tmp - wmPtr->winPtr->reqHeight)/wmPtr->heightInc;
7272	}
7273	*maxHeightPtr = tmp;
7274    }
7275}
7276
7277/*
7278 *----------------------------------------------------------------------
7279 *
7280 * TopLevelProc --
7281 *
7282 *	Callback from Windows whenever an event occurs on a top level
7283 *	window.
7284 *
7285 * Results:
7286 *	Standard Windows return value.
7287 *
7288 * Side effects:
7289 *	Default window behavior.
7290 *
7291 *----------------------------------------------------------------------
7292 */
7293
7294static LRESULT CALLBACK
7295TopLevelProc(hwnd, message, wParam, lParam)
7296    HWND hwnd;
7297    UINT message;
7298    WPARAM wParam;
7299    LPARAM lParam;
7300{
7301    if (message == WM_WINDOWPOSCHANGED) {
7302	WINDOWPOS *pos = (WINDOWPOS *) lParam;
7303	TkWindow *winPtr = (TkWindow *) Tk_HWNDToWindow(pos->hwnd);
7304
7305	if (winPtr == NULL) {
7306	    return 0;
7307	}
7308
7309	/*
7310	 * Update the shape of the contained window.
7311	 */
7312
7313	if (!(pos->flags & SWP_NOSIZE)) {
7314	    winPtr->changes.width = pos->cx;
7315	    winPtr->changes.height = pos->cy;
7316	}
7317	if (!(pos->flags & SWP_NOMOVE)) {
7318	    winPtr->changes.x = pos->x;
7319	    winPtr->changes.y = pos->y;
7320	}
7321
7322	GenerateConfigureNotify(winPtr);
7323
7324	Tcl_ServiceAll();
7325	return 0;
7326    }
7327    return TkWinChildProc(hwnd, message, wParam, lParam);
7328}
7329
7330/*
7331 *----------------------------------------------------------------------
7332 *
7333 * WmProc --
7334 *
7335 *	Callback from Windows whenever an event occurs on the decorative
7336 *	frame.
7337 *
7338 * Results:
7339 *	Standard Windows return value.
7340 *
7341 * Side effects:
7342 *	Default window behavior.
7343 *
7344 *----------------------------------------------------------------------
7345 */
7346
7347static LRESULT CALLBACK
7348WmProc(hwnd, message, wParam, lParam)
7349    HWND hwnd;
7350    UINT message;
7351    WPARAM wParam;
7352    LPARAM lParam;
7353{
7354    static int inMoveSize = 0;
7355    static int oldMode;	/* This static is set upon entering move/size mode
7356			 * and is used to reset the service mode after
7357			 * leaving move/size mode.  Note that this mechanism
7358			 * assumes move/size is only one level deep. */
7359    LRESULT result;
7360    TkWindow *winPtr = NULL;
7361
7362    if (TkWinHandleMenuEvent(&hwnd, &message, &wParam, &lParam, &result)) {
7363	goto done;
7364    }
7365
7366    switch (message) {
7367	case WM_KILLFOCUS:
7368	case WM_ERASEBKGND:
7369	    result = 0;
7370	    goto done;
7371
7372	case WM_ENTERSIZEMOVE:
7373	    inMoveSize = 1;
7374
7375	    /*
7376	     * Cancel any current mouse timer.  If the mouse timer
7377	     * fires during the size/move mouse capture, it will
7378	     * release the capture, which is wrong.
7379	     */
7380
7381	    TkWinCancelMouseTimer();
7382
7383	    oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL);
7384	    break;
7385
7386	case WM_ACTIVATE:
7387	case WM_EXITSIZEMOVE:
7388	    if (inMoveSize) {
7389		inMoveSize = 0;
7390		Tcl_SetServiceMode(oldMode);
7391	    }
7392	    break;
7393
7394	case WM_GETMINMAXINFO:
7395	    SetLimits(hwnd, (MINMAXINFO *) lParam);
7396	    result = 0;
7397	    goto done;
7398
7399	case WM_DISPLAYCHANGE:
7400	    /* display and/or color resolution changed */
7401	    winPtr = GetTopLevel(hwnd);
7402	    if (winPtr) {
7403		Screen *screen = Tk_Screen(winPtr);
7404		if (screen->root_depth != (int) wParam) {
7405		    /*
7406		     * Color resolution changed, so do extensive rebuild of
7407		     * display parameters.  This will affect the display for
7408		     * all Tk windows.  We will receive this event for each
7409		     * toplevel, but this check makes us update only once, for
7410		     * the first toplevel that receives the message.
7411		     */
7412		    TkWinDisplayChanged(Tk_Display(winPtr));
7413		} else {
7414		    HDC dc = GetDC(NULL);
7415		    screen->width   = LOWORD(lParam); /* horizontal res */
7416		    screen->height  = HIWORD(lParam); /* vertical res */
7417		    screen->mwidth  = MulDiv(screen->width, 254,
7418			    GetDeviceCaps(dc, LOGPIXELSX) * 10);
7419		    screen->mheight = MulDiv(screen->height, 254,
7420			    GetDeviceCaps(dc, LOGPIXELSY) * 10);
7421		    ReleaseDC(NULL, dc);
7422		}
7423		if (Tk_Depth(winPtr) != (int) wParam) {
7424		    /*
7425		     * Defer the window depth check to here so that each
7426		     * toplevel will properly update depth info.
7427		     */
7428		    InvalidateSubTreeDepth(winPtr);
7429		}
7430	    }
7431	    result = 0;
7432	    goto done;
7433
7434	case WM_SYSCOLORCHANGE:
7435	    /*
7436	     * XXX: Called when system color changes.  We need to
7437	     * update any widgets that use a system color.
7438	     */
7439	    break;
7440
7441	case WM_PALETTECHANGED:
7442	    result = InstallColormaps(hwnd, WM_PALETTECHANGED,
7443		    hwnd == (HWND)wParam);
7444	    goto done;
7445
7446	case WM_QUERYNEWPALETTE:
7447	    result = InstallColormaps(hwnd, WM_QUERYNEWPALETTE, TRUE);
7448	    goto done;
7449
7450	case WM_WINDOWPOSCHANGED:
7451	    ConfigureTopLevel((WINDOWPOS *) lParam);
7452	    result = 0;
7453	    goto done;
7454
7455	case WM_NCHITTEST: {
7456	    winPtr = GetTopLevel(hwnd);
7457	    if (winPtr && (TkGrabState(winPtr) == TK_GRAB_EXCLUDED)) {
7458		/*
7459		 * This window is outside the grab heirarchy, so don't let any
7460		 * of the normal non-client processing occur.  Note that this
7461		 * implementation is not strictly correct because the grab
7462		 * might change between now and when the event would have been
7463		 * processed by Tk, but it's close enough.
7464		 */
7465
7466		result = HTCLIENT;
7467		goto done;
7468	    }
7469	    break;
7470	}
7471
7472	case WM_MOUSEACTIVATE: {
7473	    ActivateEvent *eventPtr;
7474	    winPtr = GetTopLevel((HWND) wParam);
7475
7476	    if (winPtr && (TkGrabState(winPtr) != TK_GRAB_EXCLUDED)) {
7477		/*
7478		 * This allows us to pass the message onto the
7479		 * native menus [Bug: 2272]
7480		 */
7481		result = (*tkWinProcs->defWindowProc)(hwnd, message,
7482			wParam, lParam);
7483		goto done;
7484	    }
7485
7486	    /*
7487	     * Don't activate the window yet since there is a grab
7488	     * that takes precedence.  Instead we need to queue
7489	     * an event so we can check the grab state right before we
7490	     * handle the mouse event.
7491	     */
7492
7493	    if (winPtr) {
7494		eventPtr = (ActivateEvent *)ckalloc(sizeof(ActivateEvent));
7495		eventPtr->ev.proc = ActivateWindow;
7496		eventPtr->winPtr = winPtr;
7497		Tcl_QueueEvent((Tcl_Event*)eventPtr, TCL_QUEUE_TAIL);
7498	    }
7499	    result = MA_NOACTIVATE;
7500	    goto done;
7501	}
7502
7503	case WM_QUERYENDSESSION: {
7504	    XEvent event;
7505
7506	    winPtr = GetTopLevel(hwnd);
7507	    event.xclient.message_type =
7508		Tk_InternAtom((Tk_Window) winPtr, "WM_PROTOCOLS");
7509	    event.xclient.data.l[0] =
7510		Tk_InternAtom((Tk_Window) winPtr, "WM_SAVE_YOURSELF");
7511	    TkWmProtocolEventProc(winPtr, &event);
7512	    break;
7513	}
7514
7515	default:
7516	    break;
7517    }
7518
7519    winPtr = GetTopLevel(hwnd);
7520    if (winPtr && winPtr->window) {
7521	HWND child = Tk_GetHWND(winPtr->window);
7522	if (message == WM_SETFOCUS) {
7523	    SetFocus(child);
7524	    result = 0;
7525	} else if (!Tk_TranslateWinEvent(child, message, wParam, lParam,
7526		&result)) {
7527	    result = (*tkWinProcs->defWindowProc)(hwnd, message,
7528		    wParam, lParam);
7529	}
7530    } else {
7531	result = (*tkWinProcs->defWindowProc)(hwnd, message, wParam, lParam);
7532    }
7533
7534    done:
7535    Tcl_ServiceAll();
7536    return result;
7537}
7538
7539/*
7540 *----------------------------------------------------------------------
7541 *
7542 * TkpMakeMenuWindow --
7543 *
7544 *	Configure the window to be either a pull-down (or pop-up)
7545 *	menu, or as a toplevel (torn-off) menu or palette.
7546 *
7547 * Results:
7548 *	None.
7549 *
7550 * Side effects:
7551 *	Changes the style bit used to create a new toplevel.
7552 *
7553 *----------------------------------------------------------------------
7554 */
7555
7556void
7557TkpMakeMenuWindow(tkwin, transient)
7558    Tk_Window tkwin;		/* New window. */
7559    int transient;		/* 1 means menu is only posted briefly as
7560				 * a popup or pulldown or cascade.  0 means
7561				 * menu is always visible, e.g. as a torn-off
7562				 * menu.  Determines whether save_under and
7563				 * override_redirect should be set. */
7564{
7565    XSetWindowAttributes atts;
7566
7567    if (transient) {
7568	atts.override_redirect = True;
7569	atts.save_under = True;
7570    } else {
7571	atts.override_redirect = False;
7572	atts.save_under = False;
7573    }
7574
7575    if ((atts.override_redirect != Tk_Attributes(tkwin)->override_redirect)
7576	    || (atts.save_under != Tk_Attributes(tkwin)->save_under)) {
7577	Tk_ChangeWindowAttributes(tkwin,
7578		CWOverrideRedirect|CWSaveUnder, &atts);
7579    }
7580
7581}
7582
7583/*
7584 *----------------------------------------------------------------------
7585 *
7586 * TkWinGetWrapperWindow --
7587 *
7588 *	Gets the Windows HWND for a given window.
7589 *
7590 * Results:
7591 *	Returns the wrapper window for a Tk window.
7592 *
7593 * Side effects:
7594 *	None.
7595 *
7596 *----------------------------------------------------------------------
7597 */
7598
7599HWND
7600TkWinGetWrapperWindow(
7601    Tk_Window tkwin)		/* The window we need the wrapper from */
7602{
7603    TkWindow *winPtr = (TkWindow *)tkwin;
7604    return (winPtr->wmInfoPtr->wrapper);
7605}
7606
7607
7608/*
7609 *----------------------------------------------------------------------
7610 *
7611 * TkWmFocusToplevel --
7612 *
7613 *	This is a utility procedure invoked by focus-management code. It
7614 *	exists because of the extra wrapper windows that exist under
7615 *	Unix; its job is to map from wrapper windows to the
7616 *	corresponding toplevel windows.  On PCs and Macs there are no
7617 *	wrapper windows so no mapping is necessary;  this procedure just
7618 *	determines whether a window is a toplevel or not.
7619 *
7620 * Results:
7621 *	If winPtr is a toplevel window, returns the pointer to the
7622 *	window; otherwise returns NULL.
7623 *
7624 * Side effects:
7625 *	None.
7626 *
7627 *----------------------------------------------------------------------
7628 */
7629
7630TkWindow *
7631TkWmFocusToplevel(winPtr)
7632    TkWindow *winPtr;		/* Window that received a focus-related
7633				 * event. */
7634{
7635    if (!(winPtr->flags & TK_TOP_HIERARCHY)) {
7636	return NULL;
7637    }
7638    return winPtr;
7639}
7640
7641/*
7642 *----------------------------------------------------------------------
7643 *
7644 * TkpGetWrapperWindow --
7645 *
7646 *	This is a utility procedure invoked by focus-management code. It
7647 *	maps to the wrapper for a top-level, which is just the same
7648 *	as the top-level on Macs and PCs.
7649 *
7650 * Results:
7651 *	If winPtr is a toplevel window, returns the pointer to the
7652 *	window; otherwise returns NULL.
7653 *
7654 * Side effects:
7655 *	None.
7656 *
7657 *----------------------------------------------------------------------
7658 */
7659
7660TkWindow *
7661TkpGetWrapperWindow(
7662    TkWindow *winPtr)		/* Window that received a focus-related
7663				 * event. */
7664{
7665    if (!(winPtr->flags & TK_TOP_HIERARCHY)) {
7666	return NULL;
7667    }
7668    return winPtr;
7669}
7670
7671/*
7672 *----------------------------------------------------------------------
7673 *
7674 * ActivateWindow --
7675 *
7676 *	This function is called when an ActivateEvent is processed.
7677 *
7678 * Results:
7679 *	Returns 1 to indicate that the event was handled, else 0.
7680 *
7681 * Side effects:
7682 *	May activate the toplevel window associated with the event.
7683 *
7684 *----------------------------------------------------------------------
7685 */
7686
7687static int
7688ActivateWindow(
7689    Tcl_Event *evPtr,		/* Pointer to ActivateEvent. */
7690    int flags)			/* Notifier event mask. */
7691{
7692    TkWindow *winPtr;
7693
7694    if (! (flags & TCL_WINDOW_EVENTS)) {
7695	return 0;
7696    }
7697
7698    winPtr = ((ActivateEvent *) evPtr)->winPtr;
7699
7700    /*
7701     * If the window is excluded by a grab, call SetFocus on the
7702     * grabbed window instead. [Bug 220908]
7703     */
7704
7705    if (winPtr) {
7706	if (TkGrabState(winPtr) != TK_GRAB_EXCLUDED) {
7707	    SetFocus(Tk_GetHWND(winPtr->window));
7708	} else {
7709	    SetFocus(Tk_GetHWND(winPtr->dispPtr->grabWinPtr->window));
7710	}
7711    }
7712
7713    return 1;
7714}
7715
7716
7717/*
7718 *----------------------------------------------------------------------
7719 *
7720 * TkWinSetForegroundWindow --
7721 *
7722 *	This function is a wrapper for SetForegroundWindow, calling
7723 *      it on the wrapper window because it has no affect on child
7724 *      windows.
7725 *
7726 * Results:
7727 *	none
7728 *
7729 * Side effects:
7730 *	May activate the toplevel window.
7731 *
7732 *----------------------------------------------------------------------
7733 */
7734
7735void
7736TkWinSetForegroundWindow(winPtr)
7737    TkWindow *winPtr;
7738{
7739    register WmInfo *wmPtr = winPtr->wmInfoPtr;
7740
7741    if (wmPtr->wrapper != NULL) {
7742	SetForegroundWindow(wmPtr->wrapper);
7743    } else {
7744	SetForegroundWindow(Tk_GetHWND(winPtr->window));
7745    }
7746}
7747