1/*
2 * tkUnixWm.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) 1991-1994 The Regents of the University of California.
10 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
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: tkUnixWm.c,v 1.36.2.7 2006/04/11 20:23:45 hobbs Exp $
16 */
17
18#include "tkPort.h"
19#include "tkInt.h"
20#include "tkUnixInt.h"
21
22/*
23 * A data structure of the following type holds information for
24 * each window manager protocol (such as WM_DELETE_WINDOW) for
25 * which a handler (i.e. a Tcl command) has been defined for a
26 * particular top-level window.
27 */
28
29typedef struct ProtocolHandler {
30    Atom protocol;		/* Identifies the protocol. */
31    struct ProtocolHandler *nextPtr;
32				/* Next in list of protocol handlers for
33				 * the same top-level window, or NULL for
34				 * end of list. */
35    Tcl_Interp *interp;		/* Interpreter in which to invoke command. */
36    char command[4];		/* Tcl command to invoke when a client
37				 * message for this protocol arrives.
38				 * The actual size of the structure varies
39				 * to accommodate the needs of the actual
40				 * command. THIS MUST BE THE LAST FIELD OF
41				 * THE STRUCTURE. */
42} ProtocolHandler;
43
44#define HANDLER_SIZE(cmdLength) \
45    ((unsigned) (sizeof(ProtocolHandler) - 3 + cmdLength))
46
47/*
48 * A data structure of the following type holds window-manager-related
49 * information for each top-level window in an application.
50 */
51
52typedef struct TkWmInfo {
53    TkWindow *winPtr;		/* Pointer to main Tk information for
54				 * this window. */
55    Window reparent;		/* If the window has been reparented, this
56				 * gives the ID of the ancestor of the window
57				 * that is a child of the root window (may
58				 * not be window's immediate parent).  If
59				 * the window isn't reparented, this has the
60				 * value None. */
61    char *title;		/* Title to display in window caption.  If
62				 * NULL, use name of widget.  Malloced. */
63    char *iconName;		/* Name to display in icon.  Malloced. */
64    XWMHints hints;		/* Various pieces of information for
65				 * window manager. */
66    char *leaderName;		/* Path name of leader of window group
67				 * (corresponds to hints.window_group).
68				 * Malloc-ed.  Note:  this field doesn't
69				 * get updated if leader is destroyed. */
70    TkWindow *masterPtr;	/* Master window for TRANSIENT_FOR property,
71				 * or NULL. */
72    Tk_Window icon;		/* Window to use as icon for this window,
73				 * or NULL. */
74    Tk_Window iconFor;		/* Window for which this window is icon, or
75				 * NULL if this isn't an icon for anyone. */
76    int withdrawn;		/* Non-zero means window has been withdrawn. */
77
78    /*
79     * In order to support menubars transparently under X, each toplevel
80     * window is encased in an additional window, called the wrapper,
81     * that holds the toplevel and the menubar, if any.  The information
82     * below is used to keep track of the wrapper and the menubar.
83     */
84
85    TkWindow *wrapperPtr;	/* Pointer to information about the wrapper.
86				 * This is the "real" toplevel window as
87				 * seen by the window manager. Although
88				 * this is an official Tk window, it
89				 * doesn't appear in the application's
90				 * window hierarchy.  NULL means that
91				 * the wrapper hasn't been created yet. */
92    Tk_Window menubar;		/* Pointer to information about the
93				 * menubar, or NULL if there is no
94				 * menubar for this toplevel. */
95    int menuHeight;		/* Amount of vertical space needed for
96				 * menubar, measured in pixels.  If
97				 * menubar is non-NULL, this is >= 1 (X
98				 * servers don't like dimensions of 0). */
99
100    /*
101     * Information used to construct an XSizeHints structure for
102     * the window manager:
103     */
104
105    int sizeHintsFlags;		/* Flags word for XSizeHints structure.
106				 * If the PBaseSize flag is set then the
107				 * window is gridded;  otherwise it isn't
108				 * gridded. */
109    int minWidth, minHeight;	/* Minimum dimensions of window, in
110				 * pixels or grid units. */
111    int maxWidth, maxHeight;	/* Maximum dimensions of window, in
112				 * pixels or grid units. 0 to default.*/
113    Tk_Window gridWin;		/* Identifies the window that controls
114				 * gridding for this top-level, or NULL if
115				 * the top-level isn't currently gridded. */
116    int widthInc, heightInc;	/* Increments for size changes (# pixels
117				 * per step). */
118    struct {
119	int x;	/* numerator */
120	int y;  /* denominator */
121    } minAspect, maxAspect;	/* Min/max aspect ratios for window. */
122    int reqGridWidth, reqGridHeight;
123				/* The dimensions of the window (in
124				 * grid units) requested through
125				 * the geometry manager. */
126    int gravity;		/* Desired window gravity. */
127
128    /*
129     * Information used to manage the size and location of a window.
130     */
131
132    int width, height;		/* Desired dimensions of window, specified
133				 * in pixels or grid units.  These values are
134				 * set by the "wm geometry" command and by
135				 * ConfigureNotify events (for when wm
136				 * resizes window).  -1 means user hasn't
137				 * requested dimensions. */
138    int x, y;			/* Desired X and Y coordinates for window.
139				 * These values are set by "wm geometry",
140				 * plus by ConfigureNotify events (when wm
141				 * moves window).  These numbers are
142				 * different than the numbers stored in
143				 * winPtr->changes because (a) they could be
144				 * measured from the right or bottom edge
145				 * of the screen (see WM_NEGATIVE_X and
146				 * WM_NEGATIVE_Y flags) and (b) if the window
147				 * has been reparented then they refer to the
148				 * parent rather than the window itself. */
149    int parentWidth, parentHeight;
150				/* Width and height of reparent, in pixels
151				 * *including border*.  If window hasn't been
152				 * reparented then these will be the outer
153				 * dimensions of the window, including
154				 * border. */
155    int xInParent, yInParent;	/* Offset of wrapperPtr within reparent,
156				 * measured in pixels from upper-left outer
157				 * corner of reparent's border to upper-left
158				 * outer corner of wrapperPtr's border.  If
159				 * not reparented then these are zero. */
160    int configWidth, configHeight;
161				/* Dimensions passed to last request that we
162				 * issued to change geometry of the wrapper.
163				 * Used to eliminate redundant resize
164				 * operations. */
165
166    /*
167     * Information about the virtual root window for this top-level,
168     * if there is one.
169     */
170
171    Window vRoot;		/* Virtual root window for this top-level,
172				 * or None if there is no virtual root
173				 * window (i.e. just use the screen's root). */
174    int vRootX, vRootY;		/* Position of the virtual root inside the
175				 * root window.  If the WM_VROOT_OFFSET_STALE
176				 * flag is set then this information may be
177				 * incorrect and needs to be refreshed from
178				 * the X server.  If vRoot is None then these
179				 * values are both 0. */
180    int vRootWidth, vRootHeight;/* Dimensions of the virtual root window.
181				 * If vRoot is None, gives the dimensions
182				 * of the containing screen.  This information
183				 * is never stale, even though vRootX and
184				 * vRootY can be. */
185
186    /*
187     * Miscellaneous information.
188     */
189
190    ProtocolHandler *protPtr;	/* First in list of protocol handlers for
191				 * this window (NULL means none). */
192    int cmdArgc;		/* Number of elements in cmdArgv below. */
193    CONST char **cmdArgv;	/* Array of strings to store in the
194				 * WM_COMMAND property.  NULL means nothing
195				 * available. */
196    char *clientMachine;	/* String to store in WM_CLIENT_MACHINE
197				 * property, or NULL. */
198    int flags;			/* Miscellaneous flags, defined below. */
199    int numTransients;		/* number of transients on this window */
200    int iconDataSize;		/* size of iconphoto image data */
201    unsigned char *iconDataPtr;	/* iconphoto image data, if set */
202    struct TkWmInfo *nextPtr;	/* Next in list of all top-level windows. */
203} WmInfo;
204
205/*
206 * Flag values for WmInfo structures:
207 *
208 * WM_NEVER_MAPPED -		non-zero means window has never been
209 *				mapped;  need to update all info when
210 *				window is first mapped.
211 * WM_UPDATE_PENDING -		non-zero means a call to UpdateGeometryInfo
212 *				has already been scheduled for this
213 *				window;  no need to schedule another one.
214 * WM_NEGATIVE_X -		non-zero means x-coordinate is measured in
215 *				pixels from right edge of screen, rather
216 *				than from left edge.
217 * WM_NEGATIVE_Y -		non-zero means y-coordinate is measured in
218 *				pixels up from bottom of screen, rather than
219 *				down from top.
220 * WM_UPDATE_SIZE_HINTS -	non-zero means that new size hints need to be
221 *				propagated to window manager.
222 * WM_SYNC_PENDING -		set to non-zero while waiting for the window
223 *				manager to respond to some state change.
224 * WM_VROOT_OFFSET_STALE -	non-zero means that (x,y) offset information
225 *				about the virtual root window is stale and
226 *				needs to be fetched fresh from the X server.
227 * WM_ABOUT_TO_MAP -		non-zero means that the window is about to
228 *				be mapped by TkWmMapWindow.  This is used
229 *				by UpdateGeometryInfo to modify its behavior.
230 * WM_MOVE_PENDING -		non-zero means the application has requested
231 *				a new position for the window, but it hasn't
232 *				been reflected through the window manager
233 *				yet.
234 * WM_COLORMAPS_EXPLICIT -	non-zero means the colormap windows were
235 *				set explicitly via "wm colormapwindows".
236 * WM_ADDED_TOPLEVEL_COLORMAP - non-zero means that when "wm colormapwindows"
237 *				was called the top-level itself wasn't
238 *				specified, so we added it implicitly at
239 *				the end of the list.
240 * WM_WIDTH_NOT_RESIZABLE -	non-zero means that we're not supposed to
241 *				allow the user to change the width of the
242 *				window (controlled by "wm resizable"
243 *				command).
244 * WM_HEIGHT_NOT_RESIZABLE -	non-zero means that we're not supposed to
245 *				allow the user to change the height of the
246 *				window (controlled by "wm resizable"
247 *				command).
248 * WM_WITHDRAWN -		non-zero means that this window has explicitly
249 *				been withdrawn. If it's a transient, it should
250 *				not mirror state changes in the master.
251 */
252
253#define WM_NEVER_MAPPED			1
254#define WM_UPDATE_PENDING		2
255#define WM_NEGATIVE_X			4
256#define WM_NEGATIVE_Y			8
257#define WM_UPDATE_SIZE_HINTS		0x10
258#define WM_SYNC_PENDING			0x20
259#define WM_VROOT_OFFSET_STALE		0x40
260#define WM_ABOUT_TO_MAP			0x100
261#define WM_MOVE_PENDING			0x200
262#define WM_COLORMAPS_EXPLICIT		0x400
263#define WM_ADDED_TOPLEVEL_COLORMAP	0x800
264#define WM_WIDTH_NOT_RESIZABLE		0x1000
265#define WM_HEIGHT_NOT_RESIZABLE		0x2000
266#define WM_WITHDRAWN		       	0x4000
267
268/*
269 * This module keeps a list of all top-level windows, primarily to
270 * simplify the job of Tk_CoordsToWindow.  The list is called
271 * firstWmPtr and is stored in the TkDisplay structure.
272 */
273
274/*
275 * The following structures are the official type records for geometry
276 * management of top-level and menubar windows.
277 */
278
279static void		TopLevelReqProc _ANSI_ARGS_((ClientData dummy,
280			    Tk_Window tkwin));
281
282static Tk_GeomMgr wmMgrType = {
283    "wm",				/* name */
284    TopLevelReqProc,			/* requestProc */
285    (Tk_GeomLostSlaveProc *) NULL,	/* lostSlaveProc */
286};
287
288static void		MenubarReqProc _ANSI_ARGS_((ClientData clientData,
289			    Tk_Window tkwin));
290
291static Tk_GeomMgr menubarMgrType = {
292    "menubar",				/* name */
293    MenubarReqProc,			/* requestProc */
294    (Tk_GeomLostSlaveProc *) NULL,	/* lostSlaveProc */
295};
296
297/*
298 * Structures of the following type are used for communication between
299 * WaitForEvent, WaitRestrictProc, and WaitTimeoutProc.
300 */
301
302typedef struct WaitRestrictInfo {
303    Display *display;		/* Window belongs to this display. */
304    WmInfo *wmInfoPtr;
305    int type;			/* We only care about this type of event. */
306    XEvent *eventPtr;		/* Where to store the event when it's found. */
307    int foundEvent;		/* Non-zero means that an event of the
308				 * desired type has been found. */
309} WaitRestrictInfo;
310
311/*
312 * Forward declarations for procedures defined in this file:
313 */
314
315static int		ComputeReparentGeometry _ANSI_ARGS_((WmInfo *wmPtr));
316static void		ConfigureEvent _ANSI_ARGS_((WmInfo *wmPtr,
317			    XConfigureEvent *eventPtr));
318static void		CreateWrapper _ANSI_ARGS_((WmInfo *wmPtr));
319static void		GetMaxSize _ANSI_ARGS_((WmInfo *wmPtr,
320			    int *maxWidthPtr, int *maxHeightPtr));
321static void		MenubarDestroyProc _ANSI_ARGS_((ClientData clientData,
322			    XEvent *eventPtr));
323static int		ParseGeometry _ANSI_ARGS_((Tcl_Interp *interp,
324			    char *string, TkWindow *winPtr));
325static void		ReparentEvent _ANSI_ARGS_((WmInfo *wmPtr,
326			    XReparentEvent *eventPtr));
327static int		SetNetWmType _ANSI_ARGS_((TkWindow *winPtr,
328			    Tcl_Obj *typePtr));
329static Tcl_Obj *	GetNetWmType _ANSI_ARGS_((TkWindow *winPtr));
330static void		TkSetTransientFor _ANSI_ARGS_((Tk_Window tkwin,
331			    Tk_Window parent));
332static void		TkWmStackorderToplevelWrapperMap _ANSI_ARGS_((
333			    TkWindow *winPtr,
334			    Display *display,
335			    Tcl_HashTable *reparentTable));
336static void		TopLevelReqProc _ANSI_ARGS_((ClientData dummy,
337			    Tk_Window tkwin));
338static void		UpdateCommand _ANSI_ARGS_((TkWindow *winPtr));
339static void		UpdateGeometryInfo _ANSI_ARGS_((
340			    ClientData clientData));
341static void		UpdateHints _ANSI_ARGS_((TkWindow *winPtr));
342static void		UpdateSizeHints _ANSI_ARGS_((TkWindow *winPtr,
343			    int newWidth, int newHeight));
344static void		UpdateTitle _ANSI_ARGS_((TkWindow *winPtr));
345static void		UpdatePhotoIcon _ANSI_ARGS_((TkWindow *winPtr));
346static void		UpdateVRootGeometry _ANSI_ARGS_((WmInfo *wmPtr));
347static void		UpdateWmProtocols _ANSI_ARGS_((WmInfo *wmPtr));
348static void		WaitForConfigureNotify _ANSI_ARGS_((TkWindow *winPtr,
349			    unsigned long serial));
350static int		WaitForEvent _ANSI_ARGS_((Display *display,
351			    WmInfo *wmInfoPtr, int type, XEvent *eventPtr));
352static void		WaitForMapNotify _ANSI_ARGS_((TkWindow *winPtr,
353			    int mapped));
354static Tk_RestrictAction
355			WaitRestrictProc _ANSI_ARGS_((ClientData clientData,
356			    XEvent *eventPtr));
357static void		WrapperEventProc _ANSI_ARGS_((ClientData clientData,
358			    XEvent *eventPtr));
359static void		WmWaitMapProc _ANSI_ARGS_((
360			    ClientData clientData, XEvent *eventPtr));
361
362static int 		WmAspectCmd _ANSI_ARGS_((Tk_Window tkwin,
363			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
364			    Tcl_Obj *CONST objv[]));
365static int 		WmAttributesCmd _ANSI_ARGS_((Tk_Window tkwin,
366			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
367			    Tcl_Obj *CONST objv[]));
368static int 		WmClientCmd _ANSI_ARGS_((Tk_Window tkwin,
369			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
370			    Tcl_Obj *CONST objv[]));
371static int 		WmColormapwindowsCmd _ANSI_ARGS_((Tk_Window tkwin,
372			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
373			    Tcl_Obj *CONST objv[]));
374static int 		WmCommandCmd _ANSI_ARGS_((Tk_Window tkwin,
375			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
376			    Tcl_Obj *CONST objv[]));
377static int 		WmDeiconifyCmd _ANSI_ARGS_((Tk_Window tkwin,
378			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
379			    Tcl_Obj *CONST objv[]));
380static int 		WmFocusmodelCmd _ANSI_ARGS_((Tk_Window tkwin,
381			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
382			    Tcl_Obj *CONST objv[]));
383static int 		WmFrameCmd _ANSI_ARGS_((Tk_Window tkwin,
384			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
385			    Tcl_Obj *CONST objv[]));
386static int 		WmGeometryCmd _ANSI_ARGS_((Tk_Window tkwin,
387			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
388			    Tcl_Obj *CONST objv[]));
389static int 		WmGridCmd _ANSI_ARGS_((Tk_Window tkwin,
390			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
391			    Tcl_Obj *CONST objv[]));
392static int 		WmGroupCmd _ANSI_ARGS_((Tk_Window tkwin,
393			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
394			    Tcl_Obj *CONST objv[]));
395static int 		WmIconbitmapCmd _ANSI_ARGS_((Tk_Window tkwin,
396			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
397			    Tcl_Obj *CONST objv[]));
398static int 		WmIconifyCmd _ANSI_ARGS_((Tk_Window tkwin,
399			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
400			    Tcl_Obj *CONST objv[]));
401static int 		WmIconmaskCmd _ANSI_ARGS_((Tk_Window tkwin,
402			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
403			    Tcl_Obj *CONST objv[]));
404static int 		WmIconnameCmd _ANSI_ARGS_((Tk_Window tkwin,
405			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
406			    Tcl_Obj *CONST objv[]));
407static int 		WmIconphotoCmd _ANSI_ARGS_((Tk_Window tkwin,
408			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
409			    Tcl_Obj *CONST objv[]));
410static int 		WmIconpositionCmd _ANSI_ARGS_((Tk_Window tkwin,
411			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
412			    Tcl_Obj *CONST objv[]));
413static int 		WmIconwindowCmd _ANSI_ARGS_((Tk_Window tkwin,
414			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
415			    Tcl_Obj *CONST objv[]));
416static int 		WmMaxsizeCmd _ANSI_ARGS_((Tk_Window tkwin,
417			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
418			    Tcl_Obj *CONST objv[]));
419static int 		WmMinsizeCmd _ANSI_ARGS_((Tk_Window tkwin,
420			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
421			    Tcl_Obj *CONST objv[]));
422static int 		WmOverrideredirectCmd _ANSI_ARGS_((Tk_Window tkwin,
423			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
424			    Tcl_Obj *CONST objv[]));
425static int 		WmPositionfromCmd _ANSI_ARGS_((Tk_Window tkwin,
426			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
427			    Tcl_Obj *CONST objv[]));
428static int 		WmProtocolCmd _ANSI_ARGS_((Tk_Window tkwin,
429			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
430			    Tcl_Obj *CONST objv[]));
431static int 		WmResizableCmd _ANSI_ARGS_((Tk_Window tkwin,
432			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
433			    Tcl_Obj *CONST objv[]));
434static int 		WmSizefromCmd _ANSI_ARGS_((Tk_Window tkwin,
435			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
436			    Tcl_Obj *CONST objv[]));
437static int 		WmStackorderCmd _ANSI_ARGS_((Tk_Window tkwin,
438			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
439			    Tcl_Obj *CONST objv[]));
440static int 		WmStateCmd _ANSI_ARGS_((Tk_Window tkwin,
441			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
442			    Tcl_Obj *CONST objv[]));
443static int 		WmTitleCmd _ANSI_ARGS_((Tk_Window tkwin,
444			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
445			    Tcl_Obj *CONST objv[]));
446static int 		WmTransientCmd _ANSI_ARGS_((Tk_Window tkwin,
447			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
448			    Tcl_Obj *CONST objv[]));
449static int 		WmWithdrawCmd _ANSI_ARGS_((Tk_Window tkwin,
450			    TkWindow *winPtr, Tcl_Interp *interp, int objc,
451			    Tcl_Obj *CONST objv[]));
452static void		WmUpdateGeom _ANSI_ARGS_((WmInfo *wmPtr,
453			    TkWindow *winPtr));
454
455/*
456 *--------------------------------------------------------------
457 *
458 * TkWmCleanup --
459 *
460 *	This procedure is invoked to cleanup remaining wm resources
461 *	associated with a display.
462 *
463 * Results:
464 *	None.
465 *
466 * Side effects:
467 *	All WmInfo structure resources are freed and invalidated.
468 *
469 *--------------------------------------------------------------
470 */
471
472void TkWmCleanup(dispPtr)
473    TkDisplay *dispPtr;
474{
475    WmInfo *wmPtr, *nextPtr;
476    for (wmPtr = dispPtr->firstWmPtr; wmPtr != NULL; wmPtr = nextPtr) {
477	/*
478	 * We can't assume we have access to winPtr's anymore, so some
479	 * cleanup requiring winPtr data is avoided.
480	 */
481	nextPtr = wmPtr->nextPtr;
482	if (wmPtr->title != NULL) {
483	    ckfree(wmPtr->title);
484	}
485	if (wmPtr->iconName != NULL) {
486	    ckfree(wmPtr->iconName);
487	}
488	if (wmPtr->iconDataPtr != NULL) {
489	    ckfree((char *)wmPtr->iconDataPtr);
490	}
491	if (wmPtr->leaderName != NULL) {
492	    ckfree(wmPtr->leaderName);
493	}
494	if (wmPtr->menubar != NULL) {
495	    Tk_DestroyWindow(wmPtr->menubar);
496	}
497	if (wmPtr->wrapperPtr != NULL) {
498	    Tk_DestroyWindow((Tk_Window) wmPtr->wrapperPtr);
499	}
500	while (wmPtr->protPtr != NULL) {
501	    ProtocolHandler *protPtr;
502
503	    protPtr = wmPtr->protPtr;
504	    wmPtr->protPtr = protPtr->nextPtr;
505	    Tcl_EventuallyFree((ClientData) protPtr, TCL_DYNAMIC);
506	}
507	if (wmPtr->cmdArgv != NULL) {
508	    ckfree((char *) wmPtr->cmdArgv);
509	}
510	if (wmPtr->clientMachine != NULL) {
511	    ckfree((char *) wmPtr->clientMachine);
512	}
513	ckfree((char *) wmPtr);
514    }
515    if (dispPtr->iconDataPtr != NULL) {
516	ckfree((char *)dispPtr->iconDataPtr);
517	dispPtr->iconDataPtr = NULL;
518    }
519}
520
521/*
522 *--------------------------------------------------------------
523 *
524 * TkWmNewWindow --
525 *
526 *	This procedure is invoked whenever a new top-level
527 *	window is created.  Its job is to initialize the WmInfo
528 *	structure for the window.
529 *
530 * Results:
531 *	None.
532 *
533 * Side effects:
534 *	A WmInfo structure gets allocated and initialized.
535 *
536 *--------------------------------------------------------------
537 */
538
539void
540TkWmNewWindow(winPtr)
541    TkWindow *winPtr;		/* Newly-created top-level window. */
542{
543    register WmInfo *wmPtr;
544    TkDisplay *dispPtr = winPtr->dispPtr;
545
546    wmPtr = (WmInfo *) ckalloc(sizeof(WmInfo));
547    memset(wmPtr, 0, sizeof(WmInfo));
548    wmPtr->winPtr = winPtr;
549    wmPtr->reparent = None;
550    wmPtr->masterPtr = NULL;
551    wmPtr->numTransients = 0;
552    wmPtr->hints.flags = InputHint | StateHint;
553    wmPtr->hints.input = True;
554    wmPtr->hints.initial_state = NormalState;
555    wmPtr->hints.icon_pixmap = None;
556    wmPtr->hints.icon_window = None;
557    wmPtr->hints.icon_x = wmPtr->hints.icon_y = 0;
558    wmPtr->hints.icon_mask = None;
559    wmPtr->hints.window_group = None;
560
561    /*
562     * Default the maximum dimensions to the size of the display, minus
563     * a guess about how space is needed for window manager decorations.
564     */
565
566    wmPtr->gridWin = NULL;
567    wmPtr->minWidth = wmPtr->minHeight = 1;
568    wmPtr->maxWidth = wmPtr->maxHeight = 0;
569    wmPtr->widthInc = wmPtr->heightInc = 1;
570    wmPtr->minAspect.x = wmPtr->minAspect.y = 1;
571    wmPtr->maxAspect.x = wmPtr->maxAspect.y = 1;
572    wmPtr->reqGridWidth = wmPtr->reqGridHeight = -1;
573    wmPtr->gravity = NorthWestGravity;
574    wmPtr->width = -1;
575    wmPtr->height = -1;
576    wmPtr->x = winPtr->changes.x;
577    wmPtr->y = winPtr->changes.y;
578    wmPtr->parentWidth = winPtr->changes.width
579	    + 2*winPtr->changes.border_width;
580    wmPtr->parentHeight = winPtr->changes.height
581	    + 2*winPtr->changes.border_width;
582    wmPtr->configWidth = -1;
583    wmPtr->configHeight = -1;
584    wmPtr->vRoot = None;
585    wmPtr->flags = WM_NEVER_MAPPED;
586    wmPtr->nextPtr = (WmInfo *) dispPtr->firstWmPtr;
587    dispPtr->firstWmPtr = wmPtr;
588    winPtr->wmInfoPtr = wmPtr;
589
590    UpdateVRootGeometry(wmPtr);
591
592    /*
593     * Arrange for geometry requests to be reflected from the window
594     * to the window manager.
595     */
596
597    Tk_ManageGeometry((Tk_Window) winPtr, &wmMgrType, (ClientData) 0);
598}
599
600/*
601 *--------------------------------------------------------------
602 *
603 * TkWmMapWindow --
604 *
605 *	This procedure is invoked to map a top-level window.  This
606 *	module gets a chance to update all window-manager-related
607 *	information in properties before the window manager sees
608 *	the map event and checks the properties.  It also gets to
609 *	decide whether or not to even map the window after all.
610 *
611 * Results:
612 *	None.
613 *
614 * Side effects:
615 *	Properties of winPtr may get updated to provide up-to-date
616 *	information to the window manager.  The window may also get
617 *	mapped, but it may not be if this procedure decides that
618 *	isn't appropriate (e.g. because the window is withdrawn).
619 *
620 *--------------------------------------------------------------
621 */
622
623void
624TkWmMapWindow(winPtr)
625    TkWindow *winPtr;		/* Top-level window that's about to
626				 * be mapped. */
627{
628    register WmInfo *wmPtr = winPtr->wmInfoPtr;
629    XTextProperty textProp;
630
631    if (wmPtr->flags & WM_NEVER_MAPPED) {
632	Tcl_DString ds;
633
634	wmPtr->flags &= ~WM_NEVER_MAPPED;
635
636	/*
637	 * This is the first time this window has ever been mapped.
638	 * First create the wrapper window that provides space for a
639	 * menubar.
640	 */
641
642	if (wmPtr->wrapperPtr == NULL) {
643	    CreateWrapper(wmPtr);
644	}
645
646	/*
647	 * Store all the window-manager-related information for the
648	 * window.
649	 */
650
651	TkWmSetClass(winPtr);
652	UpdateTitle(winPtr);
653	UpdatePhotoIcon(winPtr);
654
655	if (wmPtr->masterPtr != NULL) {
656	    /*
657	     * Don't map a transient if the master is not mapped.
658	     */
659
660	    if (!Tk_IsMapped(wmPtr->masterPtr)) {
661	        wmPtr->withdrawn = 1;
662	        wmPtr->hints.initial_state = WithdrawnState;
663	    } else {
664	        XSetTransientForHint(winPtr->display, wmPtr->wrapperPtr->window,
665		        wmPtr->masterPtr->wmInfoPtr->wrapperPtr->window);
666	    }
667	}
668
669	wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
670	UpdateHints(winPtr);
671	UpdateWmProtocols(wmPtr);
672	if (wmPtr->cmdArgv != NULL) {
673	    UpdateCommand(winPtr);
674	}
675	if (wmPtr->clientMachine != NULL) {
676	    Tcl_UtfToExternalDString(NULL, wmPtr->clientMachine, -1, &ds);
677	    if (XStringListToTextProperty(&(Tcl_DStringValue(&ds)), 1,
678		    &textProp) != 0) {
679		XSetWMClientMachine(winPtr->display, wmPtr->wrapperPtr->window,
680			&textProp);
681		XFree((char *) textProp.value);
682	    }
683	    Tcl_DStringFree(&ds);
684	}
685    }
686    if (wmPtr->hints.initial_state == WithdrawnState) {
687	return;
688    }
689    if (wmPtr->iconFor != NULL) {
690	/*
691	 * This window is an icon for somebody else.  Make sure that
692	 * the geometry is up-to-date, then return without mapping
693	 * the window.
694	 */
695
696	if (wmPtr->flags & WM_UPDATE_PENDING) {
697	    Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
698	}
699	UpdateGeometryInfo((ClientData) winPtr);
700	return;
701    }
702    wmPtr->flags |= WM_ABOUT_TO_MAP;
703    if (wmPtr->flags & WM_UPDATE_PENDING) {
704	Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
705    }
706    UpdateGeometryInfo((ClientData) winPtr);
707    wmPtr->flags &= ~WM_ABOUT_TO_MAP;
708
709    /*
710     * Map the window, then wait to be sure that the window manager has
711     * processed the map operation.
712     */
713
714    XMapWindow(winPtr->display, wmPtr->wrapperPtr->window);
715    if (wmPtr->hints.initial_state == NormalState) {
716	WaitForMapNotify(winPtr, 1);
717    }
718}
719
720/*
721 *--------------------------------------------------------------
722 *
723 * TkWmUnmapWindow --
724 *
725 *	This procedure is invoked to unmap a top-level window.  The
726 *	only thing it does special is to wait for the window actually
727 *	to be unmapped.
728 *
729 * Results:
730 *	None.
731 *
732 * Side effects:
733 *	Unmaps the window.
734 *
735 *--------------------------------------------------------------
736 */
737
738void
739TkWmUnmapWindow(winPtr)
740    TkWindow *winPtr;		/* Top-level window that's about to
741				 * be mapped. */
742{
743    /*
744     * It seems to be important to wait after unmapping a top-level
745     * window until the window really gets unmapped.  I don't completely
746     * understand all the interactions with the window manager, but if
747     * we go on without waiting, and if the window is then mapped again
748     * quickly, events seem to get lost so that we think the window isn't
749     * mapped when in fact it is mapped.  I suspect that this has something
750     * to do with the window manager filtering Map events (and possily not
751     * filtering Unmap events?).
752     */
753    XUnmapWindow(winPtr->display, winPtr->wmInfoPtr->wrapperPtr->window);
754    WaitForMapNotify(winPtr, 0);
755}
756
757/*
758 *--------------------------------------------------------------
759 *
760 * TkWmDeadWindow --
761 *
762 *	This procedure is invoked when a top-level window is
763 *	about to be deleted.  It cleans up the wm-related data
764 *	structures for the window.
765 *
766 * Results:
767 *	None.
768 *
769 * Side effects:
770 *	The WmInfo structure for winPtr gets freed up.
771 *
772 *--------------------------------------------------------------
773 */
774
775void
776TkWmDeadWindow(winPtr)
777    TkWindow *winPtr;		/* Top-level window that's being deleted. */
778{
779    register WmInfo *wmPtr = winPtr->wmInfoPtr;
780    WmInfo *wmPtr2;
781
782    if (wmPtr == NULL) {
783	return;
784    }
785    if ((WmInfo *) winPtr->dispPtr->firstWmPtr == wmPtr) {
786	winPtr->dispPtr->firstWmPtr = wmPtr->nextPtr;
787    } else {
788	register WmInfo *prevPtr;
789
790	for (prevPtr = (WmInfo *) winPtr->dispPtr->firstWmPtr; ;
791                prevPtr = prevPtr->nextPtr) {
792	    if (prevPtr == NULL) {
793		Tcl_Panic("couldn't unlink window in TkWmDeadWindow");
794	    }
795	    if (prevPtr->nextPtr == wmPtr) {
796		prevPtr->nextPtr = wmPtr->nextPtr;
797		break;
798	    }
799	}
800    }
801    if (wmPtr->title != NULL) {
802	ckfree(wmPtr->title);
803    }
804    if (wmPtr->iconName != NULL) {
805	ckfree(wmPtr->iconName);
806    }
807    if (wmPtr->iconDataPtr != NULL) {
808	ckfree((char *)wmPtr->iconDataPtr);
809    }
810    if (wmPtr->hints.flags & IconPixmapHint) {
811	Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap);
812    }
813    if (wmPtr->hints.flags & IconMaskHint) {
814	Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_mask);
815    }
816    if (wmPtr->leaderName != NULL) {
817	ckfree(wmPtr->leaderName);
818    }
819    if (wmPtr->icon != NULL) {
820	wmPtr2 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
821	wmPtr2->iconFor = NULL;
822	wmPtr2->withdrawn = 1;
823    }
824    if (wmPtr->iconFor != NULL) {
825	wmPtr2 = ((TkWindow *) wmPtr->iconFor)->wmInfoPtr;
826	wmPtr2->icon = NULL;
827	wmPtr2->hints.flags &= ~IconWindowHint;
828	UpdateHints((TkWindow *) wmPtr->iconFor);
829    }
830    if (wmPtr->menubar != NULL) {
831	Tk_DestroyWindow(wmPtr->menubar);
832    }
833    if (wmPtr->wrapperPtr != NULL) {
834	/*
835	 * The rest of Tk doesn't know that we reparent the toplevel
836	 * inside the wrapper, so reparent it back out again before
837	 * deleting the wrapper; otherwise the toplevel will get deleted
838	 * twice (once implicitly by the deletion of the wrapper).
839	 */
840
841	XUnmapWindow(winPtr->display, winPtr->window);
842	XReparentWindow(winPtr->display, winPtr->window,
843		XRootWindow(winPtr->display, winPtr->screenNum), 0, 0);
844	Tk_DestroyWindow((Tk_Window) wmPtr->wrapperPtr);
845    }
846    while (wmPtr->protPtr != NULL) {
847	ProtocolHandler *protPtr;
848
849	protPtr = wmPtr->protPtr;
850	wmPtr->protPtr = protPtr->nextPtr;
851	Tcl_EventuallyFree((ClientData) protPtr, TCL_DYNAMIC);
852    }
853    if (wmPtr->cmdArgv != NULL) {
854	ckfree((char *) wmPtr->cmdArgv);
855    }
856    if (wmPtr->clientMachine != NULL) {
857	ckfree((char *) wmPtr->clientMachine);
858    }
859    if (wmPtr->flags & WM_UPDATE_PENDING) {
860	Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
861    }
862    /*
863     * Reset all transient windows whose master is the dead window.
864     */
865
866    for (wmPtr2 = winPtr->dispPtr->firstWmPtr; wmPtr2 != NULL;
867	 wmPtr2 = wmPtr2->nextPtr) {
868	if (wmPtr2->masterPtr == winPtr) {
869	    wmPtr->numTransients--;
870	    Tk_DeleteEventHandler((Tk_Window) wmPtr2->masterPtr,
871	            StructureNotifyMask,
872	            WmWaitMapProc, (ClientData) wmPtr2->winPtr);
873	    wmPtr2->masterPtr = NULL;
874	    if (!(wmPtr2->flags & WM_NEVER_MAPPED)) {
875		XDeleteProperty(winPtr->display, wmPtr2->wrapperPtr->window,
876			Tk_InternAtom((Tk_Window) winPtr, "WM_TRANSIENT_FOR"));
877		/* FIXME: Need a call like Win32's UpdateWrapper() so
878		   we can recreate the wrapper and get rid of the
879		   transient window decorations. */
880	    }
881	}
882    }
883    if (wmPtr->numTransients != 0)
884        Tcl_Panic("numTransients should be 0");
885
886    if (wmPtr->masterPtr != NULL) {
887	wmPtr2 = wmPtr->masterPtr->wmInfoPtr;
888	/*
889	 * If we had a master, tell them that we aren't tied
890	 * to them anymore
891	 */
892	if (wmPtr2 != NULL) {
893	    wmPtr2->numTransients--;
894	}
895	Tk_DeleteEventHandler((Tk_Window) wmPtr->masterPtr,
896		StructureNotifyMask,
897		WmWaitMapProc, (ClientData) winPtr);
898	wmPtr->masterPtr = NULL;
899    }
900    ckfree((char *) wmPtr);
901    winPtr->wmInfoPtr = NULL;
902}
903
904/*
905 *--------------------------------------------------------------
906 *
907 * TkWmSetClass --
908 *
909 *	This procedure is invoked whenever a top-level window's
910 *	class is changed.  If the window has been mapped then this
911 *	procedure updates the window manager property for the
912 *	class.  If the window hasn't been mapped, the update is
913 *	deferred until just before the first mapping.
914 *
915 * Results:
916 *	None.
917 *
918 * Side effects:
919 *	A window property may get updated.
920 *
921 *--------------------------------------------------------------
922 */
923
924void
925TkWmSetClass(winPtr)
926    TkWindow *winPtr;		/* Newly-created top-level window. */
927{
928    if (winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
929	return;
930    }
931
932    if (winPtr->classUid != NULL) {
933	XClassHint *classPtr;
934	Tcl_DString name, class;
935
936	Tcl_UtfToExternalDString(NULL, winPtr->nameUid, -1, &name);
937	Tcl_UtfToExternalDString(NULL, winPtr->classUid, -1, &class);
938	classPtr = XAllocClassHint();
939	classPtr->res_name = Tcl_DStringValue(&name);
940	classPtr->res_class = Tcl_DStringValue(&class);
941	XSetClassHint(winPtr->display, winPtr->wmInfoPtr->wrapperPtr->window,
942		classPtr);
943	XFree((char *) classPtr);
944	Tcl_DStringFree(&name);
945	Tcl_DStringFree(&class);
946    }
947}
948
949/*
950 *----------------------------------------------------------------------
951 *
952 * Tk_WmObjCmd --
953 *
954 *	This procedure is invoked to process the "wm" Tcl command.
955 *	See the user documentation for details on what it does.
956 *
957 * Results:
958 *	A standard Tcl result.
959 *
960 * Side effects:
961 *	See the user documentation.
962 *
963 *----------------------------------------------------------------------
964 */
965
966	/* ARGSUSED */
967int
968Tk_WmObjCmd(clientData, interp, objc, objv)
969    ClientData clientData;	/* Main window associated with
970				 * interpreter. */
971    Tcl_Interp *interp;		/* Current interpreter. */
972    int objc;			/* Number of arguments. */
973    Tcl_Obj *CONST objv[];	/* Argument objects. */
974{
975    Tk_Window tkwin = (Tk_Window) clientData;
976    static CONST char *optionStrings[] = {
977	"aspect", "attributes", "client", "colormapwindows",
978	"command", "deiconify", "focusmodel", "frame",
979	"geometry", "grid", "group", "iconbitmap",
980	"iconify", "iconmask", "iconname",
981	"iconphoto", "iconposition",
982	"iconwindow", "maxsize", "minsize", "overrideredirect",
983        "positionfrom", "protocol", "resizable", "sizefrom",
984        "stackorder", "state", "title", "transient",
985	"withdraw", (char *) NULL };
986    enum options {
987        WMOPT_ASPECT, WMOPT_ATTRIBUTES, WMOPT_CLIENT, WMOPT_COLORMAPWINDOWS,
988	WMOPT_COMMAND, WMOPT_DEICONIFY, WMOPT_FOCUSMODEL, WMOPT_FRAME,
989	WMOPT_GEOMETRY, WMOPT_GRID, WMOPT_GROUP, WMOPT_ICONBITMAP,
990	WMOPT_ICONIFY, WMOPT_ICONMASK, WMOPT_ICONNAME,
991	WMOPT_ICONPHOTO, WMOPT_ICONPOSITION,
992	WMOPT_ICONWINDOW, WMOPT_MAXSIZE, WMOPT_MINSIZE, WMOPT_OVERRIDEREDIRECT,
993        WMOPT_POSITIONFROM, WMOPT_PROTOCOL, WMOPT_RESIZABLE, WMOPT_SIZEFROM,
994        WMOPT_STACKORDER, WMOPT_STATE, WMOPT_TITLE, WMOPT_TRANSIENT,
995	WMOPT_WITHDRAW };
996    int index;
997    int length;
998    char *argv1;
999    TkWindow *winPtr;
1000    TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
1001
1002    if (objc < 2) {
1003	wrongNumArgs:
1004	Tcl_WrongNumArgs(interp, 1, objv, "option window ?arg ...?");
1005	return TCL_ERROR;
1006    }
1007
1008    argv1 = Tcl_GetStringFromObj(objv[1], &length);
1009    if ((argv1[0] == 't') && (strncmp(argv1, "tracing", (size_t) length) == 0)
1010	    && (length >= 3)) {
1011	int wmTracing;
1012	if ((objc != 2) && (objc != 3)) {
1013	    Tcl_WrongNumArgs(interp, 2, objv, "?boolean?");
1014	    return TCL_ERROR;
1015	}
1016	if (objc == 2) {
1017	    Tcl_SetResult(interp,
1018		    ((dispPtr->flags & TK_DISPLAY_WM_TRACING) ? "on" : "off"),
1019		    TCL_STATIC);
1020	    return TCL_OK;
1021	}
1022	if (Tcl_GetBooleanFromObj(interp, objv[2], &wmTracing) != TCL_OK) {
1023	    return TCL_ERROR;
1024	}
1025	if (wmTracing) {
1026	    dispPtr->flags |= TK_DISPLAY_WM_TRACING;
1027	} else {
1028	    dispPtr->flags &= ~TK_DISPLAY_WM_TRACING;
1029	}
1030	return TCL_OK;
1031    }
1032
1033    if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0,
1034	    &index) != TCL_OK) {
1035	return TCL_ERROR;
1036    }
1037
1038    if (objc < 3) {
1039	goto wrongNumArgs;
1040    }
1041
1042    if (TkGetWindowFromObj(interp, tkwin, objv[2], (Tk_Window *) &winPtr)
1043	    != TCL_OK) {
1044	return TCL_ERROR;
1045    }
1046    if (!Tk_IsTopLevel(winPtr)) {
1047	Tcl_AppendResult(interp, "window \"", winPtr->pathName,
1048		"\" isn't a top-level window", (char *) NULL);
1049	return TCL_ERROR;
1050    }
1051
1052    switch ((enum options) index) {
1053      case WMOPT_ASPECT:
1054	return WmAspectCmd(tkwin, winPtr, interp, objc, objv);
1055      case WMOPT_ATTRIBUTES:
1056	return WmAttributesCmd(tkwin, winPtr, interp, objc, objv);
1057      case WMOPT_CLIENT:
1058	return WmClientCmd(tkwin, winPtr, interp, objc, objv);
1059      case WMOPT_COLORMAPWINDOWS:
1060	return WmColormapwindowsCmd(tkwin, winPtr, interp, objc, objv);
1061      case WMOPT_COMMAND:
1062	return WmCommandCmd(tkwin, winPtr, interp, objc, objv);
1063      case WMOPT_DEICONIFY:
1064	return WmDeiconifyCmd(tkwin, winPtr, interp, objc, objv);
1065      case WMOPT_FOCUSMODEL:
1066	return WmFocusmodelCmd(tkwin, winPtr, interp, objc, objv);
1067      case WMOPT_FRAME:
1068	return WmFrameCmd(tkwin, winPtr, interp, objc, objv);
1069      case WMOPT_GEOMETRY:
1070	return WmGeometryCmd(tkwin, winPtr, interp, objc, objv);
1071      case WMOPT_GRID:
1072	return WmGridCmd(tkwin, winPtr, interp, objc, objv);
1073      case WMOPT_GROUP:
1074	return WmGroupCmd(tkwin, winPtr, interp, objc, objv);
1075      case WMOPT_ICONBITMAP:
1076	return WmIconbitmapCmd(tkwin, winPtr, interp, objc, objv);
1077      case WMOPT_ICONIFY:
1078	return WmIconifyCmd(tkwin, winPtr, interp, objc, objv);
1079      case WMOPT_ICONMASK:
1080	return WmIconmaskCmd(tkwin, winPtr, interp, objc, objv);
1081      case WMOPT_ICONNAME:
1082	return WmIconnameCmd(tkwin, winPtr, interp, objc, objv);
1083      case WMOPT_ICONPHOTO:
1084        return WmIconphotoCmd(tkwin, winPtr, interp, objc, objv);
1085      case WMOPT_ICONPOSITION:
1086	return WmIconpositionCmd(tkwin, winPtr, interp, objc, objv);
1087      case WMOPT_ICONWINDOW:
1088	return WmIconwindowCmd(tkwin, winPtr, interp, objc, objv);
1089      case WMOPT_MAXSIZE:
1090	return WmMaxsizeCmd(tkwin, winPtr, interp, objc, objv);
1091      case WMOPT_MINSIZE:
1092	return WmMinsizeCmd(tkwin, winPtr, interp, objc, objv);
1093      case WMOPT_OVERRIDEREDIRECT:
1094	return WmOverrideredirectCmd(tkwin, winPtr, interp, objc, objv);
1095      case WMOPT_POSITIONFROM:
1096	return WmPositionfromCmd(tkwin, winPtr, interp, objc, objv);
1097      case WMOPT_PROTOCOL:
1098	return WmProtocolCmd(tkwin, winPtr, interp, objc, objv);
1099      case WMOPT_RESIZABLE:
1100	return WmResizableCmd(tkwin, winPtr, interp, objc, objv);
1101      case WMOPT_SIZEFROM:
1102	return WmSizefromCmd(tkwin, winPtr, interp, objc, objv);
1103      case WMOPT_STACKORDER:
1104	return WmStackorderCmd(tkwin, winPtr, interp, objc, objv);
1105      case WMOPT_STATE:
1106	return WmStateCmd(tkwin, winPtr, interp, objc, objv);
1107      case WMOPT_TITLE:
1108	return WmTitleCmd(tkwin, winPtr, interp, objc, objv);
1109      case WMOPT_TRANSIENT:
1110	return WmTransientCmd(tkwin, winPtr, interp, objc, objv);
1111      case WMOPT_WITHDRAW:
1112	return WmWithdrawCmd(tkwin, winPtr, interp, objc, objv);
1113    }
1114
1115    /* This should not happen */
1116    return TCL_ERROR;
1117}
1118
1119/*
1120 *----------------------------------------------------------------------
1121 *
1122 * WmAspectCmd --
1123 *
1124 *	This procedure is invoked to process the "wm aspect" Tcl command.
1125 *	See the user documentation for details on what it does.
1126 *
1127 * Results:
1128 *	A standard Tcl result.
1129 *
1130 * Side effects:
1131 *	See the user documentation.
1132 *
1133 *----------------------------------------------------------------------
1134 */
1135
1136static int
1137WmAspectCmd(tkwin, winPtr, interp, objc, objv)
1138    Tk_Window tkwin;		/* Main window of the application. */
1139    TkWindow *winPtr;           /* Toplevel to work with */
1140    Tcl_Interp *interp;		/* Current interpreter. */
1141    int objc;			/* Number of arguments. */
1142    Tcl_Obj *CONST objv[];	/* Argument objects. */
1143{
1144    register WmInfo *wmPtr = winPtr->wmInfoPtr;
1145    int numer1, denom1, numer2, denom2;
1146
1147    if ((objc != 3) && (objc != 7)) {
1148	Tcl_WrongNumArgs(interp, 2, objv,
1149		"window ?minNumer minDenom maxNumer maxDenom?");
1150	return TCL_ERROR;
1151    }
1152    if (objc == 3) {
1153	if (wmPtr->sizeHintsFlags & PAspect) {
1154	    char buf[TCL_INTEGER_SPACE * 4];
1155
1156	    sprintf(buf, "%d %d %d %d", wmPtr->minAspect.x,
1157		    wmPtr->minAspect.y, wmPtr->maxAspect.x,
1158		    wmPtr->maxAspect.y);
1159	    Tcl_SetResult(interp, buf, TCL_VOLATILE);
1160	}
1161	return TCL_OK;
1162    }
1163    if (*Tcl_GetString(objv[3]) == '\0') {
1164	wmPtr->sizeHintsFlags &= ~PAspect;
1165    } else {
1166	if ((Tcl_GetIntFromObj(interp, objv[3], &numer1) != TCL_OK)
1167		|| (Tcl_GetIntFromObj(interp, objv[4], &denom1) != TCL_OK)
1168		|| (Tcl_GetIntFromObj(interp, objv[5], &numer2) != TCL_OK)
1169		|| (Tcl_GetIntFromObj(interp, objv[6], &denom2) != TCL_OK)) {
1170	    return TCL_ERROR;
1171	}
1172	if ((numer1 <= 0) || (denom1 <= 0) || (numer2 <= 0) ||
1173		(denom2 <= 0)) {
1174	    Tcl_SetResult(interp, "aspect number can't be <= 0",
1175		    TCL_STATIC);
1176	    return TCL_ERROR;
1177	}
1178	wmPtr->minAspect.x = numer1;
1179	wmPtr->minAspect.y = denom1;
1180	wmPtr->maxAspect.x = numer2;
1181	wmPtr->maxAspect.y = denom2;
1182	wmPtr->sizeHintsFlags |= PAspect;
1183    }
1184    wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
1185    WmUpdateGeom(wmPtr, winPtr);
1186    return TCL_OK;
1187}
1188
1189/*
1190 *----------------------------------------------------------------------
1191 *
1192 * WmAttributesCmd --
1193 *
1194 *	This procedure is invoked to process the "wm attributes" Tcl command.
1195 *	See the user documentation for details on what it does.
1196 *
1197 * Results:
1198 *	A standard Tcl result.
1199 *
1200 * Side effects:
1201 *	See the user documentation.
1202 *
1203 *----------------------------------------------------------------------
1204 */
1205
1206static int
1207WmAttributesCmd(tkwin, winPtr, interp, objc, objv)
1208    Tk_Window tkwin;		/* Main window of the application. */
1209    TkWindow *winPtr;           /* Toplevel to work with */
1210    Tcl_Interp *interp;		/* Current interpreter. */
1211    int objc;			/* Number of arguments. */
1212    Tcl_Obj *CONST objv[];	/* Argument objects. */
1213{
1214    if (objc < 4) {
1215	Tcl_Obj *listObj = Tcl_NewListObj(0, NULL);
1216	Tcl_ListObjAppendElement(interp, listObj,
1217		Tcl_NewStringObj("-type", -1));
1218	Tcl_ListObjAppendElement(interp, listObj, GetNetWmType(winPtr));
1219	Tcl_SetObjResult(interp, listObj);
1220	return TCL_OK;
1221    }
1222    if (objc > 5 || strcmp("-type", Tcl_GetString(objv[3]))) {
1223	Tcl_WrongNumArgs(interp, 2, objv, "window ?-type list?");
1224	return TCL_ERROR;
1225    }
1226    if (objc == 4) {
1227	Tcl_SetObjResult(interp, GetNetWmType(winPtr));
1228	return TCL_OK;
1229    }
1230    return SetNetWmType(winPtr, objv[4]);
1231}
1232
1233/*
1234 *----------------------------------------------------------------------
1235 *
1236 * WmClientCmd --
1237 *
1238 *	This procedure is invoked to process the "wm client" Tcl command.
1239 *	See the user documentation for details on what it does.
1240 *
1241 * Results:
1242 *	A standard Tcl result.
1243 *
1244 * Side effects:
1245 *	See the user documentation.
1246 *
1247 *----------------------------------------------------------------------
1248 */
1249
1250static int
1251WmClientCmd(tkwin, winPtr, interp, objc, objv)
1252    Tk_Window tkwin;		/* Main window of the application. */
1253    TkWindow *winPtr;           /* Toplevel to work with */
1254    Tcl_Interp *interp;		/* Current interpreter. */
1255    int objc;			/* Number of arguments. */
1256    Tcl_Obj *CONST objv[];	/* Argument objects. */
1257{
1258    register WmInfo *wmPtr = winPtr->wmInfoPtr;
1259    char *argv3;
1260    int length;
1261
1262    if ((objc != 3) && (objc != 4)) {
1263	Tcl_WrongNumArgs(interp, 2, objv, "window ?name?");
1264	return TCL_ERROR;
1265    }
1266    if (objc == 3) {
1267	if (wmPtr->clientMachine != NULL) {
1268	    Tcl_SetResult(interp, wmPtr->clientMachine, TCL_STATIC);
1269	}
1270	return TCL_OK;
1271    }
1272    argv3 = Tcl_GetStringFromObj(objv[3], &length);
1273    if (argv3[0] == 0) {
1274	if (wmPtr->clientMachine != NULL) {
1275	    ckfree((char *) wmPtr->clientMachine);
1276	    wmPtr->clientMachine = NULL;
1277	    if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
1278		XDeleteProperty(winPtr->display, wmPtr->wrapperPtr->window,
1279			Tk_InternAtom((Tk_Window) winPtr,
1280				"WM_CLIENT_MACHINE"));
1281	    }
1282	}
1283	return TCL_OK;
1284    }
1285    if (wmPtr->clientMachine != NULL) {
1286	ckfree((char *) wmPtr->clientMachine);
1287    }
1288    wmPtr->clientMachine = (char *)
1289	    ckalloc((unsigned) (length + 1));
1290    strcpy(wmPtr->clientMachine, argv3);
1291    if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
1292	XTextProperty textProp;
1293	Tcl_DString ds;
1294
1295	Tcl_UtfToExternalDString(NULL, wmPtr->clientMachine, -1, &ds);
1296	if (XStringListToTextProperty(&(Tcl_DStringValue(&ds)), 1,
1297		&textProp) != 0) {
1298	    XSetWMClientMachine(winPtr->display, wmPtr->wrapperPtr->window,
1299		    &textProp);
1300	    XFree((char *) textProp.value);
1301	}
1302	Tcl_DStringFree(&ds);
1303    }
1304    return TCL_OK;
1305}
1306
1307/*
1308 *----------------------------------------------------------------------
1309 *
1310 * WmColormapwindowsCmd --
1311 *
1312 *	This procedure is invoked to process the "wm colormapwindows"
1313 *	Tcl command.
1314 *	See the user documentation for details on what it does.
1315 *
1316 * Results:
1317 *	A standard Tcl result.
1318 *
1319 * Side effects:
1320 *	See the user documentation.
1321 *
1322 *----------------------------------------------------------------------
1323 */
1324
1325static int
1326WmColormapwindowsCmd(tkwin, winPtr, interp, objc, objv)
1327    Tk_Window tkwin;		/* Main window of the application. */
1328    TkWindow *winPtr;           /* Toplevel to work with */
1329    Tcl_Interp *interp;		/* Current interpreter. */
1330    int objc;			/* Number of arguments. */
1331    Tcl_Obj *CONST objv[];	/* Argument objects. */
1332{
1333    register WmInfo *wmPtr = winPtr->wmInfoPtr;
1334    Window *cmapList;
1335    TkWindow *winPtr2;
1336    int count, i, windowObjc, gotToplevel;
1337    Tcl_Obj **windowObjv;
1338    char buffer[20];
1339
1340    if ((objc != 3) && (objc != 4)) {
1341	Tcl_WrongNumArgs(interp, 2, objv, "window ?windowList?");
1342	return TCL_ERROR;
1343    }
1344    Tk_MakeWindowExist((Tk_Window) winPtr);
1345    if (wmPtr->wrapperPtr == NULL) {
1346	CreateWrapper(wmPtr);
1347    }
1348    if (objc == 3) {
1349	if (XGetWMColormapWindows(winPtr->display,
1350		wmPtr->wrapperPtr->window, &cmapList, &count) == 0) {
1351	    return TCL_OK;
1352	}
1353	for (i = 0; i < count; i++) {
1354	    if ((i == (count-1))
1355		    && (wmPtr->flags & WM_ADDED_TOPLEVEL_COLORMAP)) {
1356		break;
1357	    }
1358	    winPtr2  = (TkWindow *) Tk_IdToWindow(winPtr->display,
1359		    cmapList[i]);
1360	    if (winPtr2 == NULL) {
1361		sprintf(buffer, "0x%lx", cmapList[i]);
1362		Tcl_AppendElement(interp, buffer);
1363	    } else {
1364		Tcl_AppendElement(interp, winPtr2->pathName);
1365	    }
1366	}
1367	XFree((char *) cmapList);
1368	return TCL_OK;
1369    }
1370    if (Tcl_ListObjGetElements(interp, objv[3], &windowObjc, &windowObjv)
1371	    != TCL_OK) {
1372	return TCL_ERROR;
1373    }
1374    cmapList = (Window *) ckalloc((unsigned)
1375	    ((windowObjc+1)*sizeof(Window)));
1376    gotToplevel = 0;
1377    for (i = 0; i < windowObjc; i++) {
1378	if (TkGetWindowFromObj(interp, tkwin, windowObjv[i],
1379		(Tk_Window *) &winPtr2) != TCL_OK)
1380	{
1381	    ckfree((char *) cmapList);
1382	    return TCL_ERROR;
1383	}
1384	if (winPtr2 == winPtr) {
1385	    gotToplevel = 1;
1386	}
1387	if (winPtr2->window == None) {
1388	    Tk_MakeWindowExist((Tk_Window) winPtr2);
1389	}
1390	cmapList[i] = winPtr2->window;
1391    }
1392    if (!gotToplevel) {
1393	wmPtr->flags |= WM_ADDED_TOPLEVEL_COLORMAP;
1394	cmapList[windowObjc] = wmPtr->wrapperPtr->window;
1395	windowObjc++;
1396    } else {
1397	wmPtr->flags &= ~WM_ADDED_TOPLEVEL_COLORMAP;
1398    }
1399    wmPtr->flags |= WM_COLORMAPS_EXPLICIT;
1400    XSetWMColormapWindows(winPtr->display, wmPtr->wrapperPtr->window,
1401	    cmapList, windowObjc);
1402    ckfree((char *) cmapList);
1403    return TCL_OK;
1404}
1405
1406/*
1407 *----------------------------------------------------------------------
1408 *
1409 * WmCommandCmd --
1410 *
1411 *	This procedure is invoked to process the "wm command" Tcl command.
1412 *	See the user documentation for details on what it does.
1413 *
1414 * Results:
1415 *	A standard Tcl result.
1416 *
1417 * Side effects:
1418 *	See the user documentation.
1419 *
1420 *----------------------------------------------------------------------
1421 */
1422
1423static int
1424WmCommandCmd(tkwin, winPtr, interp, objc, objv)
1425    Tk_Window tkwin;		/* Main window of the application. */
1426    TkWindow *winPtr;           /* Toplevel to work with */
1427    Tcl_Interp *interp;		/* Current interpreter. */
1428    int objc;			/* Number of arguments. */
1429    Tcl_Obj *CONST objv[];	/* Argument objects. */
1430{
1431    register WmInfo *wmPtr = winPtr->wmInfoPtr;
1432    char *argv3;
1433    int cmdArgc;
1434    CONST char **cmdArgv;
1435
1436    if ((objc != 3) && (objc != 4)) {
1437	Tcl_WrongNumArgs(interp, 2, objv, "window ?value?");
1438	return TCL_ERROR;
1439    }
1440    if (objc == 3) {
1441	if (wmPtr->cmdArgv != NULL) {
1442	    Tcl_SetResult(interp,
1443		    Tcl_Merge(wmPtr->cmdArgc, wmPtr->cmdArgv),
1444		    TCL_DYNAMIC);
1445	}
1446	return TCL_OK;
1447    }
1448    argv3 = Tcl_GetString(objv[3]);
1449    if (argv3[0] == 0) {
1450	if (wmPtr->cmdArgv != NULL) {
1451	    ckfree((char *) wmPtr->cmdArgv);
1452	    wmPtr->cmdArgv = NULL;
1453	    if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
1454		XDeleteProperty(winPtr->display, wmPtr->wrapperPtr->window,
1455			Tk_InternAtom((Tk_Window) winPtr, "WM_COMMAND"));
1456	    }
1457	}
1458	return TCL_OK;
1459    }
1460    if (Tcl_SplitList(interp, argv3, &cmdArgc, &cmdArgv) != TCL_OK) {
1461	return TCL_ERROR;
1462    }
1463    if (wmPtr->cmdArgv != NULL) {
1464	ckfree((char *) wmPtr->cmdArgv);
1465    }
1466    wmPtr->cmdArgc = cmdArgc;
1467    wmPtr->cmdArgv = cmdArgv;
1468    if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
1469	UpdateCommand(winPtr);
1470    }
1471    return TCL_OK;
1472}
1473
1474/*
1475 *----------------------------------------------------------------------
1476 *
1477 * WmDeiconifyCmd --
1478 *
1479 *	This procedure is invoked to process the "wm deiconify" Tcl command.
1480 *	See the user documentation for details on what it does.
1481 *
1482 * Results:
1483 *	A standard Tcl result.
1484 *
1485 * Side effects:
1486 *	See the user documentation.
1487 *
1488 *----------------------------------------------------------------------
1489 */
1490
1491static int
1492WmDeiconifyCmd(tkwin, winPtr, interp, objc, objv)
1493    Tk_Window tkwin;		/* Main window of the application. */
1494    TkWindow *winPtr;           /* Toplevel to work with */
1495    Tcl_Interp *interp;		/* Current interpreter. */
1496    int objc;			/* Number of arguments. */
1497    Tcl_Obj *CONST objv[];	/* Argument objects. */
1498{
1499    register WmInfo *wmPtr = winPtr->wmInfoPtr;
1500
1501    if (objc != 3) {
1502	Tcl_WrongNumArgs(interp, 2, objv, "window");
1503	return TCL_ERROR;
1504    }
1505    if (wmPtr->iconFor != NULL) {
1506	Tcl_AppendResult(interp, "can't deiconify ", Tcl_GetString(objv[2]),
1507		": it is an icon for ", Tk_PathName(wmPtr->iconFor),
1508		(char *) NULL);
1509	return TCL_ERROR;
1510    }
1511    if (winPtr->flags & TK_EMBEDDED) {
1512	Tcl_AppendResult(interp, "can't deiconify ", winPtr->pathName,
1513		": it is an embedded window", (char *) NULL);
1514	return TCL_ERROR;
1515    }
1516    wmPtr->flags &= ~WM_WITHDRAWN;
1517    TkpWmSetState(winPtr, NormalState);
1518    return TCL_OK;
1519}
1520
1521/*
1522 *----------------------------------------------------------------------
1523 *
1524 * WmFocusmodelCmd --
1525 *
1526 *	This procedure is invoked to process the "wm focusmodel" Tcl command.
1527 *	See the user documentation for details on what it does.
1528 *
1529 * Results:
1530 *	A standard Tcl result.
1531 *
1532 * Side effects:
1533 *	See the user documentation.
1534 *
1535 *----------------------------------------------------------------------
1536 */
1537
1538static int
1539WmFocusmodelCmd(tkwin, winPtr, interp, objc, objv)
1540    Tk_Window tkwin;		/* Main window of the application. */
1541    TkWindow *winPtr;           /* Toplevel to work with */
1542    Tcl_Interp *interp;		/* Current interpreter. */
1543    int objc;			/* Number of arguments. */
1544    Tcl_Obj *CONST objv[];	/* Argument objects. */
1545{
1546    register WmInfo *wmPtr = winPtr->wmInfoPtr;
1547    static CONST char *optionStrings[] = {
1548	"active", "passive", (char *) NULL };
1549    enum options {
1550	OPT_ACTIVE, OPT_PASSIVE };
1551    int index;
1552
1553    if ((objc != 3) && (objc != 4)) {
1554	Tcl_WrongNumArgs(interp, 2, objv, "window ?active|passive?");
1555	return TCL_ERROR;
1556    }
1557    if (objc == 3) {
1558	Tcl_SetResult(interp, (wmPtr->hints.input ? "passive" : "active"),
1559		TCL_STATIC);
1560	return TCL_OK;
1561    }
1562
1563    if (Tcl_GetIndexFromObj(interp, objv[3], optionStrings, "argument", 0,
1564	    &index) != TCL_OK) {
1565	return TCL_ERROR;
1566    }
1567    if (index == OPT_ACTIVE) {
1568	wmPtr->hints.input = False;
1569    } else { /* OPT_PASSIVE */
1570	wmPtr->hints.input = True;
1571    }
1572    UpdateHints(winPtr);
1573    return TCL_OK;
1574}
1575
1576/*
1577 *----------------------------------------------------------------------
1578 *
1579 * WmFrameCmd --
1580 *
1581 *	This procedure is invoked to process the "wm frame" Tcl command.
1582 *	See the user documentation for details on what it does.
1583 *
1584 * Results:
1585 *	A standard Tcl result.
1586 *
1587 * Side effects:
1588 *	See the user documentation.
1589 *
1590 *----------------------------------------------------------------------
1591 */
1592
1593static int
1594WmFrameCmd(tkwin, winPtr, interp, objc, objv)
1595    Tk_Window tkwin;		/* Main window of the application. */
1596    TkWindow *winPtr;           /* Toplevel to work with */
1597    Tcl_Interp *interp;		/* Current interpreter. */
1598    int objc;			/* Number of arguments. */
1599    Tcl_Obj *CONST objv[];	/* Argument objects. */
1600{
1601    register WmInfo *wmPtr = winPtr->wmInfoPtr;
1602    Window window;
1603    char buf[TCL_INTEGER_SPACE];
1604
1605    if (objc != 3) {
1606	Tcl_WrongNumArgs(interp, 2, objv, "window");
1607	return TCL_ERROR;
1608    }
1609    window = wmPtr->reparent;
1610    if (window == None) {
1611	window = Tk_WindowId((Tk_Window) winPtr);
1612    }
1613    sprintf(buf, "0x%x", (unsigned int) window);
1614    Tcl_SetResult(interp, buf, TCL_VOLATILE);
1615    return TCL_OK;
1616}
1617
1618/*
1619 *----------------------------------------------------------------------
1620 *
1621 * WmGeometryCmd --
1622 *
1623 *	This procedure is invoked to process the "wm geometry" Tcl command.
1624 *	See the user documentation for details on what it does.
1625 *
1626 * Results:
1627 *	A standard Tcl result.
1628 *
1629 * Side effects:
1630 *	See the user documentation.
1631 *
1632 *----------------------------------------------------------------------
1633 */
1634
1635static int
1636WmGeometryCmd(tkwin, winPtr, interp, objc, objv)
1637    Tk_Window tkwin;		/* Main window of the application. */
1638    TkWindow *winPtr;           /* Toplevel to work with */
1639    Tcl_Interp *interp;		/* Current interpreter. */
1640    int objc;			/* Number of arguments. */
1641    Tcl_Obj *CONST objv[];	/* Argument objects. */
1642{
1643    register WmInfo *wmPtr = winPtr->wmInfoPtr;
1644    char xSign, ySign;
1645    int width, height;
1646    char *argv3;
1647
1648    if ((objc != 3) && (objc != 4)) {
1649	Tcl_WrongNumArgs(interp, 2, objv, "window ?newGeometry?");
1650	return TCL_ERROR;
1651    }
1652    if (objc == 3) {
1653	char buf[16 + TCL_INTEGER_SPACE * 4];
1654
1655	xSign = (wmPtr->flags & WM_NEGATIVE_X) ? '-' : '+';
1656	ySign = (wmPtr->flags & WM_NEGATIVE_Y) ? '-' : '+';
1657	if (wmPtr->gridWin != NULL) {
1658	    width = wmPtr->reqGridWidth + (winPtr->changes.width
1659		    - winPtr->reqWidth)/wmPtr->widthInc;
1660	    height = wmPtr->reqGridHeight + (winPtr->changes.height
1661		    - winPtr->reqHeight)/wmPtr->heightInc;
1662	} else {
1663	    width = winPtr->changes.width;
1664	    height = winPtr->changes.height;
1665	}
1666	sprintf(buf, "%dx%d%c%d%c%d", width, height, xSign, wmPtr->x,
1667		ySign, wmPtr->y);
1668	Tcl_SetResult(interp, buf, TCL_VOLATILE);
1669	return TCL_OK;
1670    }
1671    argv3 = Tcl_GetString(objv[3]);
1672    if (*argv3 == '\0') {
1673	wmPtr->width = -1;
1674	wmPtr->height = -1;
1675	WmUpdateGeom(wmPtr, winPtr);
1676	return TCL_OK;
1677    }
1678    return ParseGeometry(interp, argv3, winPtr);
1679}
1680
1681/*
1682 *----------------------------------------------------------------------
1683 *
1684 * WmGridCmd --
1685 *
1686 *	This procedure is invoked to process the "wm grid" Tcl command.
1687 *	See the user documentation for details on what it does.
1688 *
1689 * Results:
1690 *	A standard Tcl result.
1691 *
1692 * Side effects:
1693 *	See the user documentation.
1694 *
1695 *----------------------------------------------------------------------
1696 */
1697
1698static int
1699WmGridCmd(tkwin, winPtr, interp, objc, objv)
1700    Tk_Window tkwin;		/* Main window of the application. */
1701    TkWindow *winPtr;           /* Toplevel to work with */
1702    Tcl_Interp *interp;		/* Current interpreter. */
1703    int objc;			/* Number of arguments. */
1704    Tcl_Obj *CONST objv[];	/* Argument objects. */
1705{
1706    register WmInfo *wmPtr = winPtr->wmInfoPtr;
1707    int reqWidth, reqHeight, widthInc, heightInc;
1708
1709    if ((objc != 3) && (objc != 7)) {
1710	Tcl_WrongNumArgs(interp, 2, objv,
1711		"window ?baseWidth baseHeight widthInc heightInc?");
1712	return TCL_ERROR;
1713    }
1714    if (objc == 3) {
1715	if (wmPtr->sizeHintsFlags & PBaseSize) {
1716	    char buf[TCL_INTEGER_SPACE * 4];
1717
1718	    sprintf(buf, "%d %d %d %d", wmPtr->reqGridWidth,
1719		    wmPtr->reqGridHeight, wmPtr->widthInc,
1720		    wmPtr->heightInc);
1721	    Tcl_SetResult(interp, buf, TCL_VOLATILE);
1722	}
1723	return TCL_OK;
1724    }
1725    if (*Tcl_GetString(objv[3]) == '\0') {
1726	/*
1727	 * Turn off gridding and reset the width and height
1728	 * to make sense as ungridded numbers.
1729	 */
1730
1731	wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc);
1732	if (wmPtr->width != -1) {
1733	    wmPtr->width = winPtr->reqWidth + (wmPtr->width
1734		    - wmPtr->reqGridWidth)*wmPtr->widthInc;
1735	    wmPtr->height = winPtr->reqHeight + (wmPtr->height
1736		    - wmPtr->reqGridHeight)*wmPtr->heightInc;
1737	}
1738	wmPtr->widthInc = 1;
1739	wmPtr->heightInc = 1;
1740    } else {
1741	if ((Tcl_GetIntFromObj(interp, objv[3], &reqWidth) != TCL_OK)
1742		|| (Tcl_GetIntFromObj(interp, objv[4], &reqHeight) != TCL_OK)
1743		|| (Tcl_GetIntFromObj(interp, objv[5], &widthInc) != TCL_OK)
1744		|| (Tcl_GetIntFromObj(interp, objv[6], &heightInc) != TCL_OK)) {
1745	    return TCL_ERROR;
1746	}
1747	if (reqWidth < 0) {
1748	    Tcl_SetResult(interp, "baseWidth can't be < 0", TCL_STATIC);
1749	    return TCL_ERROR;
1750	}
1751	if (reqHeight < 0) {
1752	    Tcl_SetResult(interp, "baseHeight can't be < 0", TCL_STATIC);
1753	    return TCL_ERROR;
1754	}
1755	if (widthInc <= 0) {
1756	    Tcl_SetResult(interp, "widthInc can't be <= 0", TCL_STATIC);
1757	    return TCL_ERROR;
1758	}
1759	if (heightInc <= 0) {
1760	    Tcl_SetResult(interp, "heightInc can't be <= 0", TCL_STATIC);
1761	    return TCL_ERROR;
1762	}
1763	Tk_SetGrid((Tk_Window) winPtr, reqWidth, reqHeight, widthInc,
1764		heightInc);
1765    }
1766    wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
1767    WmUpdateGeom(wmPtr, winPtr);
1768    return TCL_OK;
1769}
1770
1771/*
1772 *----------------------------------------------------------------------
1773 *
1774 * WmGroupCmd --
1775 *
1776 *	This procedure is invoked to process the "wm group" Tcl command.
1777 *	See the user documentation for details on what it does.
1778 *
1779 * Results:
1780 *	A standard Tcl result.
1781 *
1782 * Side effects:
1783 *	See the user documentation.
1784 *
1785 *----------------------------------------------------------------------
1786 */
1787
1788static int
1789WmGroupCmd(tkwin, winPtr, interp, objc, objv)
1790    Tk_Window tkwin;		/* Main window of the application. */
1791    TkWindow *winPtr;           /* Toplevel to work with */
1792    Tcl_Interp *interp;		/* Current interpreter. */
1793    int objc;			/* Number of arguments. */
1794    Tcl_Obj *CONST objv[];	/* Argument objects. */
1795{
1796    register WmInfo *wmPtr = winPtr->wmInfoPtr;
1797    Tk_Window tkwin2;
1798    WmInfo *wmPtr2;
1799    char *argv3;
1800    int length;
1801
1802    if ((objc != 3) && (objc != 4)) {
1803	Tcl_WrongNumArgs(interp, 2, objv, "window ?pathName?");
1804	return TCL_ERROR;
1805    }
1806    if (objc == 3) {
1807	if (wmPtr->hints.flags & WindowGroupHint) {
1808	    Tcl_SetResult(interp, wmPtr->leaderName, TCL_STATIC);
1809	}
1810	return TCL_OK;
1811    }
1812    argv3 = Tcl_GetStringFromObj(objv[3], &length);
1813    if (*argv3 == '\0') {
1814	wmPtr->hints.flags &= ~WindowGroupHint;
1815	if (wmPtr->leaderName != NULL) {
1816	    ckfree(wmPtr->leaderName);
1817	}
1818	wmPtr->leaderName = NULL;
1819    } else {
1820	if (TkGetWindowFromObj(interp, tkwin, objv[3], &tkwin2) != TCL_OK) {
1821	    return TCL_ERROR;
1822	}
1823	while (!Tk_TopWinHierarchy(tkwin2)) {
1824	    /*
1825	     * Ensure that the group leader is actually a Tk toplevel.
1826	     */
1827
1828	    tkwin2 = Tk_Parent(tkwin2);
1829	}
1830	Tk_MakeWindowExist(tkwin2);
1831	wmPtr2 = ((TkWindow *) tkwin2)->wmInfoPtr;
1832	if (wmPtr2->wrapperPtr == NULL) {
1833	    CreateWrapper(wmPtr2);
1834	}
1835	if (wmPtr->leaderName != NULL) {
1836	    ckfree(wmPtr->leaderName);
1837	}
1838	wmPtr->hints.window_group = Tk_WindowId(wmPtr2->wrapperPtr);
1839	wmPtr->hints.flags |= WindowGroupHint;
1840	wmPtr->leaderName = ckalloc((unsigned) (length + 1));
1841	strcpy(wmPtr->leaderName, argv3);
1842    }
1843    UpdateHints(winPtr);
1844    return TCL_OK;
1845}
1846
1847/*
1848 *----------------------------------------------------------------------
1849 *
1850 * WmIconbitmapCmd --
1851 *
1852 *	This procedure is invoked to process the "wm iconbitmap" Tcl command.
1853 *	See the user documentation for details on what it does.
1854 *
1855 * Results:
1856 *	A standard Tcl result.
1857 *
1858 * Side effects:
1859 *	See the user documentation.
1860 *
1861 *----------------------------------------------------------------------
1862 */
1863
1864static int
1865WmIconbitmapCmd(tkwin, winPtr, interp, objc, objv)
1866    Tk_Window tkwin;		/* Main window of the application. */
1867    TkWindow *winPtr;           /* Toplevel to work with */
1868    Tcl_Interp *interp;		/* Current interpreter. */
1869    int objc;			/* Number of arguments. */
1870    Tcl_Obj *CONST objv[];	/* Argument objects. */
1871{
1872    register WmInfo *wmPtr = winPtr->wmInfoPtr;
1873    Pixmap pixmap;
1874    char *argv3;
1875
1876    if ((objc < 3) || (objc > 4)) {
1877	Tcl_WrongNumArgs(interp, 2, objv, "window ?bitmap?");
1878	return TCL_ERROR;
1879    }
1880    if (objc == 3) {
1881	if (wmPtr->hints.flags & IconPixmapHint) {
1882	    Tcl_SetResult(interp, (char *)
1883		    Tk_NameOfBitmap(winPtr->display, wmPtr->hints.icon_pixmap),
1884		    TCL_STATIC);
1885	}
1886	return TCL_OK;
1887    }
1888    argv3 = Tcl_GetString(objv[3]);
1889    if (*argv3 == '\0') {
1890	if (wmPtr->hints.icon_pixmap != None) {
1891	    Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap);
1892	    wmPtr->hints.icon_pixmap = None;
1893	}
1894	wmPtr->hints.flags &= ~IconPixmapHint;
1895    } else {
1896	pixmap = Tk_GetBitmap(interp, (Tk_Window) winPtr, argv3);
1897	if (pixmap == None) {
1898	    return TCL_ERROR;
1899	}
1900	wmPtr->hints.icon_pixmap = pixmap;
1901	wmPtr->hints.flags |= IconPixmapHint;
1902    }
1903    UpdateHints(winPtr);
1904    return TCL_OK;
1905}
1906
1907/*
1908 *----------------------------------------------------------------------
1909 *
1910 * WmIconifyCmd --
1911 *
1912 *	This procedure is invoked to process the "wm iconify" Tcl command.
1913 *	See the user documentation for details on what it does.
1914 *
1915 * Results:
1916 *	A standard Tcl result.
1917 *
1918 * Side effects:
1919 *	See the user documentation.
1920 *
1921 *----------------------------------------------------------------------
1922 */
1923
1924static int
1925WmIconifyCmd(tkwin, winPtr, interp, objc, objv)
1926    Tk_Window tkwin;		/* Main window of the application. */
1927    TkWindow *winPtr;           /* Toplevel to work with */
1928    Tcl_Interp *interp;		/* Current interpreter. */
1929    int objc;			/* Number of arguments. */
1930    Tcl_Obj *CONST objv[];	/* Argument objects. */
1931{
1932    register WmInfo *wmPtr = winPtr->wmInfoPtr;
1933    if (objc != 3) {
1934	Tcl_WrongNumArgs(interp, 2, objv, "window");
1935	return TCL_ERROR;
1936    }
1937    if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) {
1938	Tcl_AppendResult(interp, "can't iconify \"", winPtr->pathName,
1939		"\": override-redirect flag is set", (char *) NULL);
1940	return TCL_ERROR;
1941    }
1942    if (wmPtr->masterPtr != NULL) {
1943	Tcl_AppendResult(interp, "can't iconify \"", winPtr->pathName,
1944		"\": it is a transient", (char *) NULL);
1945	return TCL_ERROR;
1946    }
1947    if (wmPtr->iconFor != NULL) {
1948	Tcl_AppendResult(interp, "can't iconify ", winPtr->pathName,
1949		": it is an icon for ", Tk_PathName(wmPtr->iconFor),
1950		(char *) NULL);
1951	return TCL_ERROR;
1952    }
1953    if (winPtr->flags & TK_EMBEDDED) {
1954	Tcl_AppendResult(interp, "can't iconify ", winPtr->pathName,
1955		": it is an embedded window", (char *) NULL);
1956	return TCL_ERROR;
1957    }
1958    if (TkpWmSetState(winPtr, IconicState) == 0) {
1959	Tcl_SetResult(interp,
1960		"couldn't send iconify message to window manager",
1961		TCL_STATIC);
1962	return TCL_ERROR;
1963    }
1964    return TCL_OK;
1965}
1966
1967/*
1968 *----------------------------------------------------------------------
1969 *
1970 * WmIconmaskCmd --
1971 *
1972 *	This procedure is invoked to process the "wm iconmask" Tcl command.
1973 *	See the user documentation for details on what it does.
1974 *
1975 * Results:
1976 *	A standard Tcl result.
1977 *
1978 * Side effects:
1979 *	See the user documentation.
1980 *
1981 *----------------------------------------------------------------------
1982 */
1983
1984static int
1985WmIconmaskCmd(tkwin, winPtr, interp, objc, objv)
1986    Tk_Window tkwin;		/* Main window of the application. */
1987    TkWindow *winPtr;           /* Toplevel to work with */
1988    Tcl_Interp *interp;		/* Current interpreter. */
1989    int objc;			/* Number of arguments. */
1990    Tcl_Obj *CONST objv[];	/* Argument objects. */
1991{
1992    register WmInfo *wmPtr = winPtr->wmInfoPtr;
1993    Pixmap pixmap;
1994    char *argv3;
1995
1996    if ((objc != 3) && (objc != 4)) {
1997	Tcl_WrongNumArgs(interp, 2, objv, "window ?bitmap?");
1998	return TCL_ERROR;
1999    }
2000    if (objc == 3) {
2001	if (wmPtr->hints.flags & IconMaskHint) {
2002	    Tcl_SetResult(interp, (char *)
2003		    Tk_NameOfBitmap(winPtr->display, wmPtr->hints.icon_mask),
2004		    TCL_STATIC);
2005	}
2006	return TCL_OK;
2007    }
2008    argv3 = Tcl_GetString(objv[3]);
2009    if (*argv3 == '\0') {
2010	if (wmPtr->hints.icon_mask != None) {
2011	    Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_mask);
2012	}
2013	wmPtr->hints.flags &= ~IconMaskHint;
2014    } else {
2015	pixmap = Tk_GetBitmap(interp, tkwin, argv3);
2016	if (pixmap == None) {
2017	    return TCL_ERROR;
2018	}
2019	wmPtr->hints.icon_mask = pixmap;
2020	wmPtr->hints.flags |= IconMaskHint;
2021    }
2022    UpdateHints(winPtr);
2023    return TCL_OK;
2024}
2025
2026/*
2027 *----------------------------------------------------------------------
2028 *
2029 * WmIconnameCmd --
2030 *
2031 *	This procedure is invoked to process the "wm iconname" Tcl command.
2032 *	See the user documentation for details on what it does.
2033 *
2034 * Results:
2035 *	A standard Tcl result.
2036 *
2037 * Side effects:
2038 *	See the user documentation.
2039 *
2040 *----------------------------------------------------------------------
2041 */
2042
2043static int
2044WmIconnameCmd(tkwin, winPtr, interp, objc, objv)
2045    Tk_Window tkwin;		/* Main window of the application. */
2046    TkWindow *winPtr;           /* Toplevel to work with */
2047    Tcl_Interp *interp;		/* Current interpreter. */
2048    int objc;			/* Number of arguments. */
2049    Tcl_Obj *CONST objv[];	/* Argument objects. */
2050{
2051    register WmInfo *wmPtr = winPtr->wmInfoPtr;
2052    char *argv3;
2053    int length;
2054
2055    if (objc > 4) {
2056	Tcl_WrongNumArgs(interp, 2, objv, "window ?newName?");
2057	return TCL_ERROR;
2058    }
2059    if (objc == 3) {
2060	Tcl_SetResult(interp,
2061		((wmPtr->iconName != NULL) ? wmPtr->iconName : ""),
2062		TCL_STATIC);
2063	return TCL_OK;
2064    } else {
2065	if (wmPtr->iconName != NULL) {
2066	    ckfree((char *) wmPtr->iconName);
2067	}
2068	argv3 = Tcl_GetStringFromObj(objv[3], &length);
2069	wmPtr->iconName = ckalloc((unsigned) (length + 1));
2070	strcpy(wmPtr->iconName, argv3);
2071	if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
2072	    UpdateTitle(winPtr);
2073	}
2074    }
2075    return TCL_OK;
2076}
2077
2078/*
2079 *----------------------------------------------------------------------
2080 *
2081 * WmIconphotoCmd --
2082 *
2083 *	This procedure is invoked to process the "wm iconphoto"
2084 *	Tcl command.
2085 *	See the user documentation for details on what it does.
2086 *
2087 * Results:
2088 *	A standard Tcl result.
2089 *
2090 * Side effects:
2091 *	See the user documentation.
2092 *
2093 *----------------------------------------------------------------------
2094 */
2095
2096static int
2097WmIconphotoCmd(tkwin, winPtr, interp, objc, objv)
2098    Tk_Window tkwin;		/* Main window of the application. */
2099    TkWindow *winPtr;           /* Toplevel to work with */
2100    Tcl_Interp *interp;		/* Current interpreter. */
2101    int objc;			/* Number of arguments. */
2102    Tcl_Obj *CONST objv[];	/* Argument objects. */
2103{
2104    register WmInfo *wmPtr = winPtr->wmInfoPtr;
2105    Tk_PhotoHandle photo;
2106    Tk_PhotoImageBlock block;
2107    int i, size = 0, width, height, index = 0, x, y, isDefault = 0;
2108    long R, G, B, A;
2109    long *iconPropertyData;
2110    unsigned char *pixelByte;
2111
2112    if (objc < 4) {
2113	Tcl_WrongNumArgs(interp, 2, objv,
2114		"window ?-default? image1 ?image2 ...?");
2115	return TCL_ERROR;
2116    }
2117    if (strcmp(Tcl_GetString(objv[3]), "-default") == 0) {
2118	isDefault = 1;
2119	if (objc == 4) {
2120	    Tcl_WrongNumArgs(interp, 2, objv,
2121		    "window ?-default? image1 ?image2 ...?");
2122	    return TCL_ERROR;
2123	}
2124    }
2125    /*
2126     * Iterate over all images to retrieve their sizes, in order to allocate a
2127     * buffer large enough to hold all images.
2128     */
2129    for (i = 3 + isDefault; i < objc; i++) {
2130	photo = Tk_FindPhoto(interp, Tcl_GetString(objv[i]));
2131	if (photo == NULL) {
2132	    Tcl_AppendResult(interp, "can't use \"", Tcl_GetString(objv[i]),
2133		    "\" as iconphoto: not a photo image", (char *) NULL);
2134	    return TCL_ERROR;
2135	}
2136	Tk_PhotoGetSize(photo, &width, &height);
2137	/* We need to cardinals for width & height and one cardinal for each
2138	 * image pixel. */
2139	size += 2 + width * height;
2140    }
2141    /* We have calculated the size of the data. Try to allocate the needed
2142     * memory space. */
2143    iconPropertyData = (long *) Tcl_AttemptAlloc(sizeof(long)*size);
2144    if (iconPropertyData == NULL) {
2145	return TCL_ERROR;
2146    }
2147    memset(iconPropertyData, 0, sizeof(long)*size);
2148
2149    for (i = 3 + isDefault; i < objc; i++) {
2150	photo = Tk_FindPhoto(interp, Tcl_GetString(objv[i]));
2151	if (photo == NULL) {
2152	    Tcl_Free((char *) iconPropertyData);
2153	    return TCL_ERROR;
2154	}
2155	Tk_PhotoGetSize(photo, &width, &height);
2156	Tk_PhotoGetImage(photo, &block);
2157	/*
2158	 * Each image data will be placed as an array of 32bit packed
2159	 * CARDINAL, in a window property named "_NET_WM_ICON":
2160	 * _NET_WM_ICON
2161	 *
2162	 * _NET_WM_ICON CARDINAL[][2+n]/32
2163	 *
2164	 * This is an array of possible icons for the client.
2165	 * This specification does not stipulate what size these icons should
2166	 * be, but individual desktop environments or toolkits may do so.
2167	 * The Window Manager MAY scale any of these icons to an appropriate
2168	 * size.
2169	 *
2170	 * This is an array of 32bit packed CARDINAL ARGB with high byte being
2171	 * A, low byte being B. The first two cardinals are width, height.
2172	 * Data is in rows, left to right and top to bottom.
2173	 */
2174
2175	/*
2176	 * Encode the image data in the iconPropertyData array.
2177	 */
2178	iconPropertyData[index++] = width;
2179	iconPropertyData[index++] = height;
2180	for (y = 0; y < height; y++) {
2181	    for (x = 0; x < width; x++) {
2182		R = *(block.pixelPtr + x*block.pixelSize +
2183			y*block.pitch + block.offset[0]);
2184		G = *(block.pixelPtr + x*block.pixelSize +
2185			y*block.pitch + block.offset[1]);
2186		B = *(block.pixelPtr + x*block.pixelSize +
2187			y*block.pitch + block.offset[2]);
2188		A = *(block.pixelPtr + x*block.pixelSize +
2189			y*block.pitch + block.offset[3]);
2190		pixelByte = (unsigned char *) &iconPropertyData[index];
2191		pixelByte[3] = A;
2192		pixelByte[2] = R;
2193		pixelByte[1] = G;
2194		pixelByte[0] = B;
2195		index++;
2196	    }
2197	}
2198    }
2199    if (wmPtr->iconDataPtr != NULL) {
2200	ckfree((char *)wmPtr->iconDataPtr);
2201	wmPtr->iconDataPtr = NULL;
2202    }
2203    if (isDefault) {
2204	if (winPtr->dispPtr->iconDataPtr != NULL) {
2205	    ckfree((char *) winPtr->dispPtr->iconDataPtr);
2206	}
2207	winPtr->dispPtr->iconDataPtr  = (unsigned char *) iconPropertyData;
2208	winPtr->dispPtr->iconDataSize = size;
2209    } else {
2210	wmPtr->iconDataPtr  = (unsigned char *) iconPropertyData;
2211	wmPtr->iconDataSize = size;
2212    }
2213    if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
2214	UpdatePhotoIcon(winPtr);
2215    }
2216    return TCL_OK;
2217}
2218
2219/*
2220 *----------------------------------------------------------------------
2221 *
2222 * WmIconpositionCmd --
2223 *
2224 *	This procedure is invoked to process the "wm iconposition"
2225 *	Tcl command.
2226 *	See the user documentation for details on what it does.
2227 *
2228 * Results:
2229 *	A standard Tcl result.
2230 *
2231 * Side effects:
2232 *	See the user documentation.
2233 *
2234 *----------------------------------------------------------------------
2235 */
2236
2237static int
2238WmIconpositionCmd(tkwin, winPtr, interp, objc, objv)
2239    Tk_Window tkwin;		/* Main window of the application. */
2240    TkWindow *winPtr;           /* Toplevel to work with */
2241    Tcl_Interp *interp;		/* Current interpreter. */
2242    int objc;			/* Number of arguments. */
2243    Tcl_Obj *CONST objv[];	/* Argument objects. */
2244{
2245    register WmInfo *wmPtr = winPtr->wmInfoPtr;
2246    int x, y;
2247
2248    if ((objc != 3) && (objc != 5)) {
2249	Tcl_WrongNumArgs(interp, 2, objv, "window ?x y?");
2250	return TCL_ERROR;
2251    }
2252    if (objc == 3) {
2253	if (wmPtr->hints.flags & IconPositionHint) {
2254	    char buf[TCL_INTEGER_SPACE * 2];
2255
2256	    sprintf(buf, "%d %d", wmPtr->hints.icon_x,
2257		    wmPtr->hints.icon_y);
2258	    Tcl_SetResult(interp, buf, TCL_VOLATILE);
2259	}
2260	return TCL_OK;
2261    }
2262    if (*Tcl_GetString(objv[3]) == '\0') {
2263	wmPtr->hints.flags &= ~IconPositionHint;
2264    } else {
2265	if ((Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK)
2266		|| (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK)){
2267	    return TCL_ERROR;
2268	}
2269	wmPtr->hints.icon_x = x;
2270	wmPtr->hints.icon_y = y;
2271	wmPtr->hints.flags |= IconPositionHint;
2272    }
2273    UpdateHints(winPtr);
2274    return TCL_OK;
2275}
2276
2277/*
2278 *----------------------------------------------------------------------
2279 *
2280 * WmIconwindowCmd --
2281 *
2282 *	This procedure is invoked to process the "wm iconwindow" Tcl command.
2283 *	See the user documentation for details on what it does.
2284 *
2285 * Results:
2286 *	A standard Tcl result.
2287 *
2288 * Side effects:
2289 *	See the user documentation.
2290 *
2291 *----------------------------------------------------------------------
2292 */
2293
2294static int
2295WmIconwindowCmd(tkwin, winPtr, interp, objc, objv)
2296    Tk_Window tkwin;		/* Main window of the application. */
2297    TkWindow *winPtr;           /* Toplevel to work with */
2298    Tcl_Interp *interp;		/* Current interpreter. */
2299    int objc;			/* Number of arguments. */
2300    Tcl_Obj *CONST objv[];	/* Argument objects. */
2301{
2302    register WmInfo *wmPtr = winPtr->wmInfoPtr;
2303    Tk_Window tkwin2;
2304    WmInfo *wmPtr2;
2305    XSetWindowAttributes atts;
2306
2307    if ((objc != 3) && (objc != 4)) {
2308	Tcl_WrongNumArgs(interp, 2, objv, "window ?pathName?");
2309	return TCL_ERROR;
2310    }
2311    if (objc == 3) {
2312	if (wmPtr->icon != NULL) {
2313	    Tcl_SetResult(interp, Tk_PathName(wmPtr->icon), TCL_STATIC);
2314	}
2315	return TCL_OK;
2316    }
2317    if (*Tcl_GetString(objv[3]) == '\0') {
2318	wmPtr->hints.flags &= ~IconWindowHint;
2319	if (wmPtr->icon != NULL) {
2320	    /*
2321	     * Remove the icon window relationship.  In principle we
2322	     * should also re-enable button events for the window, but
2323	     * this doesn't work in general because the window manager
2324	     * is probably selecting on them (we'll get an error if
2325	     * we try to re-enable the events).  So, just leave the
2326	     * icon window event-challenged;  the user will have to
2327	     * recreate it if they want button events.
2328	     */
2329
2330	    wmPtr2 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
2331	    wmPtr2->iconFor = NULL;
2332	    wmPtr2->withdrawn = 1;
2333	    wmPtr2->hints.initial_state = WithdrawnState;
2334	}
2335	wmPtr->icon = NULL;
2336    } else {
2337	if (TkGetWindowFromObj(interp, tkwin, objv[3], &tkwin2) != TCL_OK) {
2338	    return TCL_ERROR;
2339	}
2340	if (!Tk_IsTopLevel(tkwin2)) {
2341	    Tcl_AppendResult(interp, "can't use ", Tcl_GetString(objv[3]),
2342		    " as icon window: not at top level", (char *) NULL);
2343	    return TCL_ERROR;
2344	}
2345	wmPtr2 = ((TkWindow *) tkwin2)->wmInfoPtr;
2346	if (wmPtr2->iconFor != NULL) {
2347	    Tcl_AppendResult(interp, Tcl_GetString(objv[3]),
2348		    " is already an icon for ",
2349		    Tk_PathName(wmPtr2->iconFor), (char *) NULL);
2350	    return TCL_ERROR;
2351	}
2352	if (wmPtr->icon != NULL) {
2353	    WmInfo *wmPtr3 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
2354	    wmPtr3->iconFor = NULL;
2355	    wmPtr3->withdrawn = 1;
2356	    wmPtr3->hints.initial_state = WithdrawnState;
2357	}
2358
2359	/*
2360	 * Disable button events in the icon window:  some window
2361	 * managers (like olvwm) want to get the events themselves,
2362	 * but X only allows one application at a time to receive
2363	 * button events for a window.
2364	 */
2365
2366	atts.event_mask = Tk_Attributes(tkwin2)->event_mask
2367		& ~ButtonPressMask;
2368	Tk_ChangeWindowAttributes(tkwin2, CWEventMask, &atts);
2369	Tk_MakeWindowExist(tkwin2);
2370	if (wmPtr2->wrapperPtr == NULL) {
2371	    CreateWrapper(wmPtr2);
2372	}
2373	wmPtr->hints.icon_window = Tk_WindowId(wmPtr2->wrapperPtr);
2374	wmPtr->hints.flags |= IconWindowHint;
2375	wmPtr->icon = tkwin2;
2376	wmPtr2->iconFor = (Tk_Window) winPtr;
2377	if (!wmPtr2->withdrawn && !(wmPtr2->flags & WM_NEVER_MAPPED)) {
2378	    wmPtr2->withdrawn = 0;
2379	    if (XWithdrawWindow(Tk_Display(tkwin2),
2380		    Tk_WindowId(wmPtr2->wrapperPtr),
2381		    Tk_ScreenNumber(tkwin2)) == 0) {
2382		Tcl_SetResult(interp,
2383			"couldn't send withdraw message to window manager",
2384			TCL_STATIC);
2385		return TCL_ERROR;
2386	    }
2387	    WaitForMapNotify((TkWindow *) tkwin2, 0);
2388	}
2389    }
2390    UpdateHints(winPtr);
2391    return TCL_OK;
2392}
2393
2394/*
2395 *----------------------------------------------------------------------
2396 *
2397 * WmMaxsizeCmd --
2398 *
2399 *	This procedure is invoked to process the "wm maxsize" Tcl command.
2400 *	See the user documentation for details on what it does.
2401 *
2402 * Results:
2403 *	A standard Tcl result.
2404 *
2405 * Side effects:
2406 *	See the user documentation.
2407 *
2408 *----------------------------------------------------------------------
2409 */
2410
2411static int
2412WmMaxsizeCmd(tkwin, winPtr, interp, objc, objv)
2413    Tk_Window tkwin;		/* Main window of the application. */
2414    TkWindow *winPtr;           /* Toplevel to work with */
2415    Tcl_Interp *interp;		/* Current interpreter. */
2416    int objc;			/* Number of arguments. */
2417    Tcl_Obj *CONST objv[];	/* Argument objects. */
2418{
2419    register WmInfo *wmPtr = winPtr->wmInfoPtr;
2420    int width, height;
2421
2422    if ((objc != 3) && (objc != 5)) {
2423	Tcl_WrongNumArgs(interp, 2, objv, "window ?width height?");
2424	return TCL_ERROR;
2425    }
2426    if (objc == 3) {
2427	char buf[TCL_INTEGER_SPACE * 2];
2428
2429	GetMaxSize(wmPtr, &width, &height);
2430	sprintf(buf, "%d %d", width, height);
2431	Tcl_SetResult(interp, buf, TCL_VOLATILE);
2432	return TCL_OK;
2433    }
2434    if ((Tcl_GetIntFromObj(interp, objv[3], &width) != TCL_OK)
2435	    || (Tcl_GetIntFromObj(interp, objv[4], &height) != TCL_OK)) {
2436	return TCL_ERROR;
2437    }
2438    wmPtr->maxWidth = width;
2439    wmPtr->maxHeight = height;
2440    wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
2441
2442    if (width <= 0 && height <= 0) {
2443	wmPtr->sizeHintsFlags &= ~PMaxSize;
2444    } else {
2445	wmPtr->sizeHintsFlags |= PMaxSize;
2446    }
2447
2448    WmUpdateGeom(wmPtr, winPtr);
2449    return TCL_OK;
2450}
2451
2452/*
2453 *----------------------------------------------------------------------
2454 *
2455 * WmMinsizeCmd --
2456 *
2457 *	This procedure is invoked to process the "wm minsize" Tcl command.
2458 *	See the user documentation for details on what it does.
2459 *
2460 * Results:
2461 *	A standard Tcl result.
2462 *
2463 * Side effects:
2464 *	See the user documentation.
2465 *
2466 *----------------------------------------------------------------------
2467 */
2468
2469static int
2470WmMinsizeCmd(tkwin, winPtr, interp, objc, objv)
2471    Tk_Window tkwin;		/* Main window of the application. */
2472    TkWindow *winPtr;           /* Toplevel to work with */
2473    Tcl_Interp *interp;		/* Current interpreter. */
2474    int objc;			/* Number of arguments. */
2475    Tcl_Obj *CONST objv[];	/* Argument objects. */
2476{
2477    register WmInfo *wmPtr = winPtr->wmInfoPtr;
2478    int width, height;
2479
2480    if ((objc != 3) && (objc != 5)) {
2481	Tcl_WrongNumArgs(interp, 2, objv, "window ?width height?");
2482	return TCL_ERROR;
2483    }
2484    if (objc == 3) {
2485	char buf[TCL_INTEGER_SPACE * 2];
2486
2487	sprintf(buf, "%d %d", wmPtr->minWidth, wmPtr->minHeight);
2488	Tcl_SetResult(interp, buf, TCL_VOLATILE);
2489	return TCL_OK;
2490    }
2491    if ((Tcl_GetIntFromObj(interp, objv[3], &width) != TCL_OK)
2492	    || (Tcl_GetIntFromObj(interp, objv[4], &height) != TCL_OK)) {
2493	return TCL_ERROR;
2494    }
2495    wmPtr->minWidth = width;
2496    wmPtr->minHeight = height;
2497    wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
2498    WmUpdateGeom(wmPtr, winPtr);
2499    return TCL_OK;
2500}
2501
2502/*
2503 *----------------------------------------------------------------------
2504 *
2505 * WmOverrideredirectCmd --
2506 *
2507 *	This procedure is invoked to process the "wm overrideredirect"
2508 *	Tcl command.
2509 *	See the user documentation for details on what it does.
2510 *
2511 * Results:
2512 *	A standard Tcl result.
2513 *
2514 * Side effects:
2515 *	See the user documentation.
2516 *
2517 *----------------------------------------------------------------------
2518 */
2519
2520static int
2521WmOverrideredirectCmd(tkwin, winPtr, interp, objc, objv)
2522    Tk_Window tkwin;		/* Main window of the application. */
2523    TkWindow *winPtr;           /* Toplevel to work with */
2524    Tcl_Interp *interp;		/* Current interpreter. */
2525    int objc;			/* Number of arguments. */
2526    Tcl_Obj *CONST objv[];	/* Argument objects. */
2527{
2528    int boolean, curValue;
2529    XSetWindowAttributes atts;
2530
2531    if ((objc != 3) && (objc != 4)) {
2532	Tcl_WrongNumArgs(interp, 2, objv, "window ?boolean?");
2533	return TCL_ERROR;
2534    }
2535    curValue = Tk_Attributes((Tk_Window) winPtr)->override_redirect;
2536    if (objc == 3) {
2537	Tcl_SetBooleanObj(Tcl_GetObjResult(interp), curValue);
2538	return TCL_OK;
2539    }
2540    if (Tcl_GetBooleanFromObj(interp, objv[3], &boolean) != TCL_OK) {
2541	return TCL_ERROR;
2542    }
2543    if (curValue != boolean) {
2544	/*
2545	 * Only do this if we are really changing value, because it
2546	 * causes some funky stuff to occur
2547	 */
2548	atts.override_redirect = (boolean) ? True : False;
2549	Tk_ChangeWindowAttributes((Tk_Window) winPtr, CWOverrideRedirect,
2550		&atts);
2551	if (winPtr->wmInfoPtr->wrapperPtr != NULL) {
2552	    Tk_ChangeWindowAttributes(
2553		    (Tk_Window) winPtr->wmInfoPtr->wrapperPtr,
2554		    CWOverrideRedirect, &atts);
2555	}
2556    }
2557    return TCL_OK;
2558}
2559
2560/*
2561 *----------------------------------------------------------------------
2562 *
2563 * WmPositionfromCmd --
2564 *
2565 *	This procedure is invoked to process the "wm positionfrom"
2566 *	Tcl command.
2567 *	See the user documentation for details on what it does.
2568 *
2569 * Results:
2570 *	A standard Tcl result.
2571 *
2572 * Side effects:
2573 *	See the user documentation.
2574 *
2575 *----------------------------------------------------------------------
2576 */
2577
2578static int
2579WmPositionfromCmd(tkwin, winPtr, interp, objc, objv)
2580    Tk_Window tkwin;		/* Main window of the application. */
2581    TkWindow *winPtr;           /* Toplevel to work with */
2582    Tcl_Interp *interp;		/* Current interpreter. */
2583    int objc;			/* Number of arguments. */
2584    Tcl_Obj *CONST objv[];	/* Argument objects. */
2585{
2586    register WmInfo *wmPtr = winPtr->wmInfoPtr;
2587    static CONST char *optionStrings[] = {
2588	"program", "user", (char *) NULL };
2589    enum options {
2590	OPT_PROGRAM, OPT_USER };
2591    int index;
2592
2593    if ((objc != 3) && (objc != 4)) {
2594	Tcl_WrongNumArgs(interp, 2, objv, "window ?user/program?");
2595	return TCL_ERROR;
2596    }
2597    if (objc == 3) {
2598	if (wmPtr->sizeHintsFlags & USPosition) {
2599	    Tcl_SetResult(interp, "user", TCL_STATIC);
2600	} else if (wmPtr->sizeHintsFlags & PPosition) {
2601	    Tcl_SetResult(interp, "program", TCL_STATIC);
2602	}
2603	return TCL_OK;
2604    }
2605    if (*Tcl_GetString(objv[3]) == '\0') {
2606	wmPtr->sizeHintsFlags &= ~(USPosition|PPosition);
2607    } else {
2608	if (Tcl_GetIndexFromObj(interp, objv[3], optionStrings, "argument", 0,
2609		&index) != TCL_OK) {
2610	    return TCL_ERROR;
2611	}
2612	if (index == OPT_USER) {
2613	    wmPtr->sizeHintsFlags &= ~PPosition;
2614	    wmPtr->sizeHintsFlags |= USPosition;
2615	} else {
2616	    wmPtr->sizeHintsFlags &= ~USPosition;
2617	    wmPtr->sizeHintsFlags |= PPosition;
2618	}
2619    }
2620    wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
2621    WmUpdateGeom(wmPtr, winPtr);
2622    return TCL_OK;
2623}
2624
2625/*
2626 *----------------------------------------------------------------------
2627 *
2628 * WmProtocolCmd --
2629 *
2630 *	This procedure is invoked to process the "wm protocol" Tcl command.
2631 *	See the user documentation for details on what it does.
2632 *
2633 * Results:
2634 *	A standard Tcl result.
2635 *
2636 * Side effects:
2637 *	See the user documentation.
2638 *
2639 *----------------------------------------------------------------------
2640 */
2641
2642static int
2643WmProtocolCmd(tkwin, winPtr, interp, objc, objv)
2644    Tk_Window tkwin;		/* Main window of the application. */
2645    TkWindow *winPtr;           /* Toplevel to work with */
2646    Tcl_Interp *interp;		/* Current interpreter. */
2647    int objc;			/* Number of arguments. */
2648    Tcl_Obj *CONST objv[];	/* Argument objects. */
2649{
2650    register WmInfo *wmPtr = winPtr->wmInfoPtr;
2651    register ProtocolHandler *protPtr, *prevPtr;
2652    Atom protocol;
2653    char *cmd;
2654    int cmdLength;
2655
2656    if ((objc < 3) || (objc > 5)) {
2657	Tcl_WrongNumArgs(interp, 2, objv, "window ?name? ?command?");
2658	return TCL_ERROR;
2659    }
2660    if (objc == 3) {
2661	/*
2662	 * Return a list of all defined protocols for the window.
2663	 */
2664	for (protPtr = wmPtr->protPtr; protPtr != NULL;
2665	     protPtr = protPtr->nextPtr) {
2666	    Tcl_AppendElement(interp,
2667		    Tk_GetAtomName((Tk_Window) winPtr, protPtr->protocol));
2668	}
2669	return TCL_OK;
2670    }
2671    protocol = Tk_InternAtom((Tk_Window) winPtr, Tcl_GetString(objv[3]));
2672    if (objc == 4) {
2673	/*
2674	 * Return the command to handle a given protocol.
2675	 */
2676	for (protPtr = wmPtr->protPtr; protPtr != NULL;
2677	     protPtr = protPtr->nextPtr) {
2678	    if (protPtr->protocol == protocol) {
2679		Tcl_SetResult(interp, protPtr->command, TCL_STATIC);
2680		return TCL_OK;
2681	    }
2682	}
2683	return TCL_OK;
2684    }
2685
2686    /*
2687     * Delete any current protocol handler, then create a new
2688     * one with the specified command, unless the command is
2689     * empty.
2690     */
2691
2692    for (protPtr = wmPtr->protPtr, prevPtr = NULL; protPtr != NULL;
2693	 prevPtr = protPtr, protPtr = protPtr->nextPtr) {
2694	if (protPtr->protocol == protocol) {
2695	    if (prevPtr == NULL) {
2696		wmPtr->protPtr = protPtr->nextPtr;
2697	    } else {
2698		prevPtr->nextPtr = protPtr->nextPtr;
2699	    }
2700	    Tcl_EventuallyFree((ClientData) protPtr, TCL_DYNAMIC);
2701	    break;
2702	}
2703    }
2704    cmd = Tcl_GetStringFromObj(objv[4], &cmdLength);
2705    if (cmdLength > 0) {
2706	protPtr = (ProtocolHandler *) ckalloc(HANDLER_SIZE(cmdLength));
2707	protPtr->protocol = protocol;
2708	protPtr->nextPtr = wmPtr->protPtr;
2709	wmPtr->protPtr = protPtr;
2710	protPtr->interp = interp;
2711	strcpy(protPtr->command, cmd);
2712    }
2713    if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
2714	UpdateWmProtocols(wmPtr);
2715    }
2716    return TCL_OK;
2717}
2718
2719/*
2720 *----------------------------------------------------------------------
2721 *
2722 * WmResizableCmd --
2723 *
2724 *	This procedure is invoked to process the "wm resizable" Tcl command.
2725 *	See the user documentation for details on what it does.
2726 *
2727 * Results:
2728 *	A standard Tcl result.
2729 *
2730 * Side effects:
2731 *	See the user documentation.
2732 *
2733 *----------------------------------------------------------------------
2734 */
2735
2736static int
2737WmResizableCmd(tkwin, winPtr, interp, objc, objv)
2738    Tk_Window tkwin;		/* Main window of the application. */
2739    TkWindow *winPtr;           /* Toplevel to work with */
2740    Tcl_Interp *interp;		/* Current interpreter. */
2741    int objc;			/* Number of arguments. */
2742    Tcl_Obj *CONST objv[];	/* Argument objects. */
2743{
2744    register WmInfo *wmPtr = winPtr->wmInfoPtr;
2745    int width, height;
2746
2747    if ((objc != 3) && (objc != 5)) {
2748	Tcl_WrongNumArgs(interp, 2, objv, "window ?width height?");
2749	return TCL_ERROR;
2750    }
2751    if (objc == 3) {
2752	char buf[TCL_INTEGER_SPACE * 2];
2753
2754	sprintf(buf, "%d %d",
2755		(wmPtr->flags  & WM_WIDTH_NOT_RESIZABLE) ? 0 : 1,
2756		(wmPtr->flags  & WM_HEIGHT_NOT_RESIZABLE) ? 0 : 1);
2757	Tcl_SetResult(interp, buf, TCL_VOLATILE);
2758	return TCL_OK;
2759    }
2760    if ((Tcl_GetBooleanFromObj(interp, objv[3], &width) != TCL_OK)
2761	    || (Tcl_GetBooleanFromObj(interp, objv[4], &height) != TCL_OK)) {
2762	return TCL_ERROR;
2763    }
2764    if (width) {
2765	wmPtr->flags &= ~WM_WIDTH_NOT_RESIZABLE;
2766    } else {
2767	wmPtr->flags |= WM_WIDTH_NOT_RESIZABLE;
2768    }
2769    if (height) {
2770	wmPtr->flags &= ~WM_HEIGHT_NOT_RESIZABLE;
2771    } else {
2772	wmPtr->flags |= WM_HEIGHT_NOT_RESIZABLE;
2773    }
2774    wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
2775    WmUpdateGeom(wmPtr, winPtr);
2776    return TCL_OK;
2777}
2778
2779/*
2780 *----------------------------------------------------------------------
2781 *
2782 * WmSizefromCmd --
2783 *
2784 *	This procedure is invoked to process the "wm sizefrom" Tcl command.
2785 *	See the user documentation for details on what it does.
2786 *
2787 * Results:
2788 *	A standard Tcl result.
2789 *
2790 * Side effects:
2791 *	See the user documentation.
2792 *
2793 *----------------------------------------------------------------------
2794 */
2795
2796static int
2797WmSizefromCmd(tkwin, winPtr, interp, objc, objv)
2798    Tk_Window tkwin;		/* Main window of the application. */
2799    TkWindow *winPtr;           /* Toplevel to work with */
2800    Tcl_Interp *interp;		/* Current interpreter. */
2801    int objc;			/* Number of arguments. */
2802    Tcl_Obj *CONST objv[];	/* Argument objects. */
2803{
2804    register WmInfo *wmPtr = winPtr->wmInfoPtr;
2805    static CONST char *optionStrings[] = {
2806	"program", "user", (char *) NULL };
2807    enum options {
2808	OPT_PROGRAM, OPT_USER };
2809    int index;
2810
2811    if ((objc != 3) && (objc != 4)) {
2812	Tcl_WrongNumArgs(interp, 2, objv, "window ?user|program?");
2813	return TCL_ERROR;
2814    }
2815    if (objc == 3) {
2816	if (wmPtr->sizeHintsFlags & USSize) {
2817	    Tcl_SetResult(interp, "user", TCL_STATIC);
2818	} else if (wmPtr->sizeHintsFlags & PSize) {
2819	    Tcl_SetResult(interp, "program", TCL_STATIC);
2820	}
2821	return TCL_OK;
2822    }
2823
2824    if (*Tcl_GetString(objv[3]) == '\0') {
2825	wmPtr->sizeHintsFlags &= ~(USSize|PSize);
2826    } else {
2827	if (Tcl_GetIndexFromObj(interp, objv[3], optionStrings, "argument", 0,
2828		&index) != TCL_OK) {
2829	    return TCL_ERROR;
2830	}
2831	if (index == OPT_USER) {
2832	    wmPtr->sizeHintsFlags &= ~PSize;
2833	    wmPtr->sizeHintsFlags |= USSize;
2834	} else { /* OPT_PROGRAM */
2835	    wmPtr->sizeHintsFlags &= ~USSize;
2836	    wmPtr->sizeHintsFlags |= PSize;
2837	}
2838    }
2839    wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
2840    WmUpdateGeom(wmPtr, winPtr);
2841    return TCL_OK;
2842}
2843
2844/*
2845 *----------------------------------------------------------------------
2846 *
2847 * WmStackorderCmd --
2848 *
2849 *	This procedure is invoked to process the "wm stackorder" Tcl command.
2850 *	See the user documentation for details on what it does.
2851 *
2852 * Results:
2853 *	A standard Tcl result.
2854 *
2855 * Side effects:
2856 *	See the user documentation.
2857 *
2858 *----------------------------------------------------------------------
2859 */
2860
2861static int
2862WmStackorderCmd(tkwin, winPtr, interp, objc, objv)
2863    Tk_Window tkwin;		/* Main window of the application. */
2864    TkWindow *winPtr;           /* Toplevel to work with */
2865    Tcl_Interp *interp;		/* Current interpreter. */
2866    int objc;			/* Number of arguments. */
2867    Tcl_Obj *CONST objv[];	/* Argument objects. */
2868{
2869    TkWindow **windows, **window_ptr;
2870    static CONST char *optionStrings[] = {
2871	"isabove", "isbelow", (char *) NULL };
2872    enum options {
2873	OPT_ISABOVE, OPT_ISBELOW };
2874    int index;
2875
2876    if ((objc != 3) && (objc != 5)) {
2877	Tcl_WrongNumArgs(interp, 2, objv, "window ?isabove|isbelow window?");
2878	return TCL_ERROR;
2879    }
2880
2881    if (objc == 3) {
2882	windows = TkWmStackorderToplevel(winPtr);
2883	if (windows == NULL) {
2884	    Tcl_Panic("TkWmStackorderToplevel failed");
2885	} else {
2886	    for (window_ptr = windows; *window_ptr ; window_ptr++) {
2887		Tcl_AppendElement(interp, (*window_ptr)->pathName);
2888	    }
2889	    ckfree((char *) windows);
2890	    return TCL_OK;
2891	}
2892    } else {
2893	TkWindow *winPtr2;
2894	int index1=-1, index2=-1, result;
2895
2896	if (TkGetWindowFromObj(interp, tkwin, objv[4], (Tk_Window *) &winPtr2)
2897		!= TCL_OK) {
2898	    return TCL_ERROR;
2899	}
2900
2901	if (!Tk_IsTopLevel(winPtr2)) {
2902	    Tcl_AppendResult(interp, "window \"", winPtr2->pathName,
2903		    "\" isn't a top-level window", (char *) NULL);
2904	    return TCL_ERROR;
2905	}
2906
2907	if (!Tk_IsMapped(winPtr)) {
2908	    Tcl_AppendResult(interp, "window \"", winPtr->pathName,
2909		    "\" isn't mapped", (char *) NULL);
2910	    return TCL_ERROR;
2911	}
2912
2913	if (!Tk_IsMapped(winPtr2)) {
2914	    Tcl_AppendResult(interp, "window \"", winPtr2->pathName,
2915		    "\" isn't mapped", (char *) NULL);
2916	    return TCL_ERROR;
2917	}
2918
2919	/*
2920	 * Lookup stacking order of all toplevels that are children
2921	 * of "." and find the position of winPtr and winPtr2
2922	 * in the stacking order.
2923	 */
2924
2925	windows = TkWmStackorderToplevel(winPtr->mainPtr->winPtr);
2926
2927	if (windows == NULL) {
2928	    Tcl_AppendResult(interp, "TkWmStackorderToplevel failed",
2929                    (char *) NULL);
2930	    return TCL_ERROR;
2931	} else {
2932	    for (window_ptr = windows; *window_ptr ; window_ptr++) {
2933		if (*window_ptr == winPtr)
2934		    index1 = (window_ptr - windows);
2935		if (*window_ptr == winPtr2)
2936		    index2 = (window_ptr - windows);
2937	    }
2938	    if (index1 == -1)
2939		Tcl_Panic("winPtr window not found");
2940	    if (index2 == -1)
2941		Tcl_Panic("winPtr2 window not found");
2942
2943	    ckfree((char *) windows);
2944	}
2945
2946	if (Tcl_GetIndexFromObj(interp, objv[3], optionStrings, "argument", 0,
2947		&index) != TCL_OK) {
2948	    return TCL_ERROR;
2949	}
2950	if (index == OPT_ISABOVE) {
2951	    result = index1 > index2;
2952	} else { /* OPT_ISBELOW */
2953	    result = index1 < index2;
2954	}
2955	Tcl_SetIntObj(Tcl_GetObjResult(interp), result);
2956	return TCL_OK;
2957    }
2958    return TCL_OK;
2959}
2960
2961/*
2962 *----------------------------------------------------------------------
2963 *
2964 * WmStateCmd --
2965 *
2966 *	This procedure is invoked to process the "wm state" Tcl command.
2967 *	See the user documentation for details on what it does.
2968 *
2969 * Results:
2970 *	A standard Tcl result.
2971 *
2972 * Side effects:
2973 *	See the user documentation.
2974 *
2975 *----------------------------------------------------------------------
2976 */
2977
2978static int
2979WmStateCmd(tkwin, winPtr, interp, objc, objv)
2980    Tk_Window tkwin;		/* Main window of the application. */
2981    TkWindow *winPtr;           /* Toplevel to work with */
2982    Tcl_Interp *interp;		/* Current interpreter. */
2983    int objc;			/* Number of arguments. */
2984    Tcl_Obj *CONST objv[];	/* Argument objects. */
2985{
2986    register WmInfo *wmPtr = winPtr->wmInfoPtr;
2987    static CONST char *optionStrings[] = {
2988	"normal", "iconic", "withdrawn", (char *) NULL };
2989    enum options {
2990	OPT_NORMAL, OPT_ICONIC, OPT_WITHDRAWN };
2991    int index;
2992
2993    if ((objc < 3) || (objc > 4)) {
2994	Tcl_WrongNumArgs(interp, 2, objv, "window ?state?");
2995	return TCL_ERROR;
2996    }
2997    if (objc == 4) {
2998	if (wmPtr->iconFor != NULL) {
2999	    Tcl_AppendResult(interp, "can't change state of ",
3000		    Tcl_GetString(objv[2]),
3001		    ": it is an icon for ", Tk_PathName(wmPtr->iconFor),
3002		    (char *) NULL);
3003	    return TCL_ERROR;
3004	}
3005
3006	if (Tcl_GetIndexFromObj(interp, objv[3], optionStrings, "argument", 0,
3007		&index) != TCL_OK) {
3008	    return TCL_ERROR;
3009	}
3010
3011	if (index == OPT_NORMAL) {
3012	    wmPtr->flags &= ~WM_WITHDRAWN;
3013	    (void) TkpWmSetState(winPtr, NormalState);
3014	} else if (index == OPT_ICONIC) {
3015	    if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) {
3016		Tcl_AppendResult(interp, "can't iconify \"",
3017			winPtr->pathName,
3018			"\": override-redirect flag is set",
3019			(char *) NULL);
3020		return TCL_ERROR;
3021	    }
3022	    if (wmPtr->masterPtr != NULL) {
3023		Tcl_AppendResult(interp, "can't iconify \"",
3024			winPtr->pathName,
3025			"\": it is a transient", (char *) NULL);
3026		return TCL_ERROR;
3027	    }
3028	    if (TkpWmSetState(winPtr, IconicState) == 0) {
3029		Tcl_SetResult(interp,
3030			"couldn't send iconify message to window manager",
3031			TCL_STATIC);
3032		return TCL_ERROR;
3033	    }
3034	} else { /* OPT_WITHDRAWN */
3035	    wmPtr->flags |= WM_WITHDRAWN;
3036	    if (TkpWmSetState(winPtr, WithdrawnState) == 0) {
3037		Tcl_SetResult(interp,
3038			"couldn't send withdraw message to window manager",
3039			TCL_STATIC);
3040		return TCL_ERROR;
3041	    }
3042	}
3043    } else {
3044	if (wmPtr->iconFor != NULL) {
3045	    Tcl_SetResult(interp, "icon", TCL_STATIC);
3046	} else if (wmPtr->withdrawn) {
3047	    Tcl_SetResult(interp, "withdrawn", TCL_STATIC);
3048	} else if (Tk_IsMapped((Tk_Window) winPtr)
3049		|| ((wmPtr->flags & WM_NEVER_MAPPED)
3050			&& (wmPtr->hints.initial_state == NormalState))) {
3051	    Tcl_SetResult(interp, "normal", TCL_STATIC);
3052	} else {
3053	    Tcl_SetResult(interp, "iconic", TCL_STATIC);
3054	}
3055    }
3056    return TCL_OK;
3057}
3058
3059/*
3060 *----------------------------------------------------------------------
3061 *
3062 * WmTitleCmd --
3063 *
3064 *	This procedure is invoked to process the "wm title" 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
3077WmTitleCmd(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 > 4) {
3089	Tcl_WrongNumArgs(interp, 2, objv, "window ?newTitle?");
3090	return TCL_ERROR;
3091    }
3092    if (objc == 3) {
3093	Tcl_SetResult(interp, (char *)
3094		((wmPtr->title != NULL) ? wmPtr->title : winPtr->nameUid),
3095		TCL_STATIC);
3096	return TCL_OK;
3097    } else {
3098	if (wmPtr->title != NULL) {
3099	    ckfree((char *) wmPtr->title);
3100	}
3101	argv3 = Tcl_GetStringFromObj(objv[3], &length);
3102	wmPtr->title = ckalloc((unsigned) (length + 1));
3103	strcpy(wmPtr->title, argv3);
3104
3105	if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
3106	    UpdateTitle(winPtr);
3107	}
3108    }
3109    return TCL_OK;
3110}
3111
3112/*
3113 *----------------------------------------------------------------------
3114 *
3115 * WmTransientCmd --
3116 *
3117 *	This procedure is invoked to process the "wm transient" Tcl command.
3118 *	See the user documentation for details on what it does.
3119 *
3120 * Results:
3121 *	A standard Tcl result.
3122 *
3123 * Side effects:
3124 *	See the user documentation.
3125 *
3126 *----------------------------------------------------------------------
3127 */
3128
3129static int
3130WmTransientCmd(tkwin, winPtr, interp, objc, objv)
3131    Tk_Window tkwin;		/* Main window of the application. */
3132    TkWindow *winPtr;           /* Toplevel to work with */
3133    Tcl_Interp *interp;		/* Current interpreter. */
3134    int objc;			/* Number of arguments. */
3135    Tcl_Obj *CONST objv[];	/* Argument objects. */
3136{
3137    register WmInfo *wmPtr = winPtr->wmInfoPtr;
3138    TkWindow *masterPtr = wmPtr->masterPtr;
3139    WmInfo *wmPtr2;
3140
3141    if ((objc != 3) && (objc != 4)) {
3142	Tcl_WrongNumArgs(interp, 2, objv, "window ?master?");
3143	return TCL_ERROR;
3144    }
3145    if (objc == 3) {
3146	if (masterPtr != NULL) {
3147	    Tcl_SetResult(interp, Tk_PathName(masterPtr), TCL_STATIC);
3148	}
3149	return TCL_OK;
3150    }
3151    if (Tcl_GetString(objv[3])[0] == '\0') {
3152	if (masterPtr != NULL) {
3153	    /*
3154	     * If we had a master, tell them that we aren't tied
3155	     * to them anymore
3156	     */
3157
3158	    masterPtr->wmInfoPtr->numTransients--;
3159	    Tk_DeleteEventHandler((Tk_Window) masterPtr,
3160		    StructureNotifyMask,
3161		    WmWaitMapProc, (ClientData) winPtr);
3162
3163	    /* FIXME: Need a call like Win32's UpdateWrapper() so
3164	       we can recreate the wrapper and get rid of the
3165	       transient window decorations. */
3166	}
3167
3168	wmPtr->masterPtr = NULL;
3169    } else {
3170	if (TkGetWindowFromObj(interp, tkwin, objv[3],
3171		(Tk_Window *) &masterPtr) != TCL_OK) {
3172	    return TCL_ERROR;
3173	}
3174	while (!Tk_TopWinHierarchy(masterPtr)) {
3175	    /*
3176	     * Ensure that the master window is actually a Tk toplevel.
3177	     */
3178
3179	    masterPtr = masterPtr->parentPtr;
3180	}
3181	Tk_MakeWindowExist((Tk_Window) masterPtr);
3182
3183	if (wmPtr->iconFor != NULL) {
3184	    Tcl_AppendResult(interp, "can't make \"",
3185		    Tcl_GetString(objv[2]),
3186		    "\" a transient: it is an icon for ",
3187		    Tk_PathName(wmPtr->iconFor),
3188		    (char *) NULL);
3189	    return TCL_ERROR;
3190	}
3191
3192	wmPtr2 = masterPtr->wmInfoPtr;
3193	if (wmPtr2->wrapperPtr == NULL) {
3194	    CreateWrapper(wmPtr2);
3195	}
3196
3197	if (wmPtr2->iconFor != NULL) {
3198	    Tcl_AppendResult(interp, "can't make \"",
3199		    Tcl_GetString(objv[3]),
3200		    "\" a master: it is an icon for ",
3201		    Tk_PathName(wmPtr2->iconFor),
3202		    (char *) NULL);
3203	    return TCL_ERROR;
3204	}
3205
3206	if (masterPtr == winPtr) {
3207	    Tcl_AppendResult(interp, "can't make \"", Tk_PathName(winPtr),
3208		    "\" its own master",
3209		    (char *) NULL);
3210	    return TCL_ERROR;
3211	} else if (masterPtr != wmPtr->masterPtr) {
3212	    /*
3213	     * Remove old master map/unmap binding before setting
3214	     * the new master. The event handler will ensure that
3215	     * transient states reflect the state of the master.
3216	     */
3217
3218	    if (wmPtr->masterPtr != NULL) {
3219		wmPtr->masterPtr->wmInfoPtr->numTransients--;
3220		Tk_DeleteEventHandler((Tk_Window) wmPtr->masterPtr,
3221			StructureNotifyMask,
3222			WmWaitMapProc, (ClientData) winPtr);
3223	    }
3224
3225	    masterPtr->wmInfoPtr->numTransients++;
3226	    Tk_CreateEventHandler((Tk_Window) masterPtr,
3227		    StructureNotifyMask,
3228		    WmWaitMapProc, (ClientData) winPtr);
3229
3230	    wmPtr->masterPtr = masterPtr;
3231	}
3232    }
3233    if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
3234	if (wmPtr->masterPtr != NULL && !Tk_IsMapped(wmPtr->masterPtr)) {
3235	    if (TkpWmSetState(winPtr, WithdrawnState) == 0) {
3236		Tcl_SetResult(interp,
3237			"couldn't send withdraw message to window manager",
3238			TCL_STATIC);
3239		return TCL_ERROR;
3240	    }
3241	} else {
3242	    if (wmPtr->masterPtr != NULL) {
3243		XSetTransientForHint(winPtr->display, wmPtr->wrapperPtr->window,
3244			wmPtr->masterPtr->wmInfoPtr->wrapperPtr->window);
3245	    } else {
3246		XDeleteProperty(winPtr->display, wmPtr->wrapperPtr->window,
3247			Tk_InternAtom((Tk_Window)winPtr, "WM_TRANSIENT_FOR"));
3248	    }
3249	}
3250    }
3251    return TCL_OK;
3252}
3253
3254/*
3255 *----------------------------------------------------------------------
3256 *
3257 * WmWithdrawCmd --
3258 *
3259 *	This procedure is invoked to process the "wm withdraw" Tcl command.
3260 *	See the user documentation for details on what it does.
3261 *
3262 * Results:
3263 *	A standard Tcl result.
3264 *
3265 * Side effects:
3266 *	See the user documentation.
3267 *
3268 *----------------------------------------------------------------------
3269 */
3270
3271static int
3272WmWithdrawCmd(tkwin, winPtr, interp, objc, objv)
3273    Tk_Window tkwin;		/* Main window of the application. */
3274    TkWindow *winPtr;           /* Toplevel to work with */
3275    Tcl_Interp *interp;		/* Current interpreter. */
3276    int objc;			/* Number of arguments. */
3277    Tcl_Obj *CONST objv[];	/* Argument objects. */
3278{
3279    register WmInfo *wmPtr = winPtr->wmInfoPtr;
3280
3281    if (objc != 3) {
3282	Tcl_WrongNumArgs(interp, 2, objv, "window");
3283	return TCL_ERROR;
3284    }
3285    if (wmPtr->iconFor != NULL) {
3286	Tcl_AppendResult(interp, "can't withdraw ", Tcl_GetString(objv[2]),
3287		": it is an icon for ", Tk_PathName(wmPtr->iconFor),
3288		(char *) NULL);
3289	return TCL_ERROR;
3290    }
3291    wmPtr->flags |= WM_WITHDRAWN;
3292    if (TkpWmSetState(winPtr, WithdrawnState) == 0) {
3293	Tcl_SetResult(interp,
3294		"couldn't send withdraw message to window manager",
3295		TCL_STATIC);
3296	return TCL_ERROR;
3297    }
3298    return TCL_OK;
3299}
3300
3301/*
3302 * Invoked by those wm subcommands that affect geometry.
3303 * Schedules a geometry update.
3304 */
3305static void
3306WmUpdateGeom(wmPtr, winPtr)
3307    WmInfo *wmPtr;
3308    TkWindow *winPtr;
3309{
3310    if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
3311	Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
3312	wmPtr->flags |= WM_UPDATE_PENDING;
3313    }
3314}
3315
3316/*
3317 * Invoked when a MapNotify or UnmapNotify event is delivered for a
3318 * toplevel that is the master of a transient toplevel.
3319 */
3320static void
3321WmWaitMapProc(clientData, eventPtr)
3322    ClientData clientData;	/* Pointer to window. */
3323    XEvent *eventPtr;		/* Information about event. */
3324{
3325    TkWindow *winPtr = (TkWindow *) clientData;
3326    TkWindow *masterPtr = winPtr->wmInfoPtr->masterPtr;
3327
3328    if (masterPtr == NULL)
3329        return;
3330
3331    if (eventPtr->type == MapNotify) {
3332        if (!(winPtr->wmInfoPtr->flags & WM_WITHDRAWN))
3333            (void) TkpWmSetState(winPtr, NormalState);
3334    } else if (eventPtr->type == UnmapNotify) {
3335        (void) TkpWmSetState(winPtr, WithdrawnState);
3336    }
3337}
3338
3339/*
3340 *----------------------------------------------------------------------
3341 *
3342 * Tk_SetGrid --
3343 *
3344 *	This procedure is invoked by a widget when it wishes to set a grid
3345 *	coordinate system that controls the size of a top-level window.
3346 *	It provides a C interface equivalent to the "wm grid" command and
3347 *	is usually asscoiated with the -setgrid option.
3348 *
3349 * Results:
3350 *	None.
3351 *
3352 * Side effects:
3353 *	Grid-related information will be passed to the window manager, so
3354 *	that the top-level window associated with tkwin will resize on
3355 *	even grid units.  If some other window already controls gridding
3356 *	for the top-level window then this procedure call has no effect.
3357 *
3358 *----------------------------------------------------------------------
3359 */
3360
3361void
3362Tk_SetGrid(tkwin, reqWidth, reqHeight, widthInc, heightInc)
3363    Tk_Window tkwin;		/* Token for window.  New window mgr info
3364				 * will be posted for the top-level window
3365				 * associated with this window. */
3366    int reqWidth;		/* Width (in grid units) corresponding to
3367				 * the requested geometry for tkwin. */
3368    int reqHeight;		/* Height (in grid units) corresponding to
3369				 * the requested geometry for tkwin. */
3370    int widthInc, heightInc;	/* Pixel increments corresponding to a
3371				 * change of one grid unit. */
3372{
3373    TkWindow *winPtr = (TkWindow *) tkwin;
3374    register WmInfo *wmPtr;
3375
3376    /*
3377     * Ensure widthInc and heightInc are greater than 0
3378     */
3379    if (widthInc <= 0) {
3380	widthInc = 1;
3381    }
3382    if (heightInc <= 0) {
3383	heightInc = 1;
3384    }
3385
3386    /*
3387     * Find the top-level window for tkwin, plus the window manager
3388     * information.
3389     */
3390
3391    while (!(winPtr->flags & TK_TOP_HIERARCHY)) {
3392	winPtr = winPtr->parentPtr;
3393	if (winPtr == NULL) {
3394	    /*
3395	     * The window is being deleted... just skip this operation.
3396	     */
3397
3398	    return;
3399	}
3400    }
3401    wmPtr = winPtr->wmInfoPtr;
3402    if (wmPtr == NULL) {
3403	return;
3404    }
3405
3406    if ((wmPtr->gridWin != NULL) && (wmPtr->gridWin != tkwin)) {
3407	return;
3408    }
3409
3410    if ((wmPtr->reqGridWidth == reqWidth)
3411	    && (wmPtr->reqGridHeight == reqHeight)
3412	    && (wmPtr->widthInc == widthInc)
3413	    && (wmPtr->heightInc == heightInc)
3414	    && ((wmPtr->sizeHintsFlags & (PBaseSize|PResizeInc))
3415		    == (PBaseSize|PResizeInc))) {
3416	return;
3417    }
3418
3419    /*
3420     * If gridding was previously off, then forget about any window
3421     * size requests made by the user or via "wm geometry":  these are
3422     * in pixel units and there's no easy way to translate them to
3423     * grid units since the new requested size of the top-level window in
3424     * pixels may not yet have been registered yet (it may filter up
3425     * the hierarchy in DoWhenIdle handlers).  However, if the window
3426     * has never been mapped yet then just leave the window size alone:
3427     * assume that it is intended to be in grid units but just happened
3428     * to have been specified before this procedure was called.
3429     */
3430
3431    if ((wmPtr->gridWin == NULL) && !(wmPtr->flags & WM_NEVER_MAPPED)) {
3432	wmPtr->width = -1;
3433	wmPtr->height = -1;
3434    }
3435
3436    /*
3437     * Set the new gridding information, and start the process of passing
3438     * all of this information to the window manager.
3439     */
3440
3441    wmPtr->gridWin = tkwin;
3442    wmPtr->reqGridWidth = reqWidth;
3443    wmPtr->reqGridHeight = reqHeight;
3444    wmPtr->widthInc = widthInc;
3445    wmPtr->heightInc = heightInc;
3446    wmPtr->sizeHintsFlags |= PBaseSize|PResizeInc;
3447    wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
3448    if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
3449	Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
3450	wmPtr->flags |= WM_UPDATE_PENDING;
3451    }
3452}
3453
3454/*
3455 *----------------------------------------------------------------------
3456 *
3457 * Tk_UnsetGrid --
3458 *
3459 *	This procedure cancels the effect of a previous call
3460 *	to Tk_SetGrid.
3461 *
3462 * Results:
3463 *	None.
3464 *
3465 * Side effects:
3466 *	If tkwin currently controls gridding for its top-level window,
3467 *	gridding is cancelled for that top-level window;  if some other
3468 *	window controls gridding then this procedure has no effect.
3469 *
3470 *----------------------------------------------------------------------
3471 */
3472
3473void
3474Tk_UnsetGrid(tkwin)
3475    Tk_Window tkwin;		/* Token for window that is currently
3476				 * controlling gridding. */
3477{
3478    TkWindow *winPtr = (TkWindow *) tkwin;
3479    register WmInfo *wmPtr;
3480
3481    /*
3482     * Find the top-level window for tkwin, plus the window manager
3483     * information.
3484     */
3485
3486    while (!(winPtr->flags & TK_TOP_HIERARCHY)) {
3487	winPtr = winPtr->parentPtr;
3488	if (winPtr == NULL) {
3489	    /*
3490	     * The window is being deleted... just skip this operation.
3491	     */
3492
3493	    return;
3494	}
3495    }
3496    wmPtr = winPtr->wmInfoPtr;
3497    if (wmPtr == NULL) {
3498	return;
3499    }
3500
3501    if (tkwin != wmPtr->gridWin) {
3502	return;
3503    }
3504
3505    wmPtr->gridWin = NULL;
3506    wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc);
3507    if (wmPtr->width != -1) {
3508	wmPtr->width = winPtr->reqWidth + (wmPtr->width
3509		- wmPtr->reqGridWidth)*wmPtr->widthInc;
3510	wmPtr->height = winPtr->reqHeight + (wmPtr->height
3511		- wmPtr->reqGridHeight)*wmPtr->heightInc;
3512    }
3513    wmPtr->widthInc = 1;
3514    wmPtr->heightInc = 1;
3515
3516    wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
3517    if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
3518	Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
3519	wmPtr->flags |= WM_UPDATE_PENDING;
3520    }
3521}
3522
3523/*
3524 *----------------------------------------------------------------------
3525 *
3526 * ConfigureEvent --
3527 *
3528 *	This procedure is called to handle ConfigureNotify events on
3529 *	wrapper windows.
3530 *
3531 * Results:
3532 *	None.
3533 *
3534 * Side effects:
3535 *	Information gets updated in the WmInfo structure for the window
3536 *	and the toplevel itself gets repositioned within the wrapper.
3537 *
3538 *----------------------------------------------------------------------
3539 */
3540
3541static void
3542ConfigureEvent(wmPtr, configEventPtr)
3543    WmInfo *wmPtr;			/* Information about toplevel window. */
3544    XConfigureEvent *configEventPtr;	/* Event that just occurred for
3545					 * wmPtr->wrapperPtr. */
3546{
3547    TkWindow *wrapperPtr = wmPtr->wrapperPtr;
3548    TkWindow *winPtr = wmPtr->winPtr;
3549    TkDisplay *dispPtr = wmPtr->winPtr->dispPtr;
3550    Tk_ErrorHandler handler;
3551
3552    /*
3553     * Update size information from the event.  There are a couple of
3554     * tricky points here:
3555     *
3556     * 1. If the user changed the size externally then set wmPtr->width
3557     *    and wmPtr->height just as if a "wm geometry" command had been
3558     *    invoked with the same information.
3559     * 2. However, if the size is changing in response to a request
3560     *    coming from us (WM_SYNC_PENDING is set), then don't set wmPtr->width
3561     *    or wmPtr->height if they were previously -1 (otherwise the
3562     *    window will stop tracking geometry manager requests).
3563     */
3564
3565    if (((wrapperPtr->changes.width != configEventPtr->width)
3566	    || (wrapperPtr->changes.height != configEventPtr->height))
3567	    && !(wmPtr->flags & WM_SYNC_PENDING)){
3568	if (dispPtr->flags & TK_DISPLAY_WM_TRACING) {
3569	    printf("TopLevelEventProc: user changed %s size to %dx%d\n",
3570		    winPtr->pathName, configEventPtr->width,
3571		    configEventPtr->height);
3572	}
3573	if ((wmPtr->width == -1)
3574		&& (configEventPtr->width == winPtr->reqWidth)) {
3575	    /*
3576	     * Don't set external width, since the user didn't change it
3577	     * from what the widgets asked for.
3578	     */
3579	} else {
3580	    /*
3581	     * Note: if this window is embedded then don't set the external
3582	     * size, since it came from the containing application, not the
3583	     * user.  In this case we want to keep sending our size requests
3584	     * to the containing application;  if the user fixes the size
3585	     * of that application then it will still percolate down to us
3586	     * in the right way.
3587	     */
3588
3589	    if (!(winPtr->flags & TK_EMBEDDED)) {
3590		if (wmPtr->gridWin != NULL) {
3591		    wmPtr->width = wmPtr->reqGridWidth
3592			    + (configEventPtr->width
3593			    - winPtr->reqWidth)/wmPtr->widthInc;
3594		    if (wmPtr->width < 0) {
3595			wmPtr->width = 0;
3596		    }
3597		} else {
3598		    wmPtr->width = configEventPtr->width;
3599		}
3600	    }
3601	}
3602	if ((wmPtr->height == -1)
3603		&& (configEventPtr->height ==
3604			(winPtr->reqHeight + wmPtr->menuHeight))) {
3605	    /*
3606	     * Don't set external height, since the user didn't change it
3607	     * from what the widgets asked for.
3608	     */
3609	} else {
3610	    /*
3611	     * See note for wmPtr->width about not setting external size
3612	     * for embedded windows.
3613	     */
3614
3615	    if (!(winPtr->flags & TK_EMBEDDED)) {
3616		if (wmPtr->gridWin != NULL) {
3617		    wmPtr->height = wmPtr->reqGridHeight
3618			    + (configEventPtr->height - wmPtr->menuHeight
3619			    - winPtr->reqHeight)/wmPtr->heightInc;
3620		    if (wmPtr->height < 0) {
3621			wmPtr->height = 0;
3622		    }
3623		} else {
3624		    wmPtr->height = configEventPtr->height - wmPtr->menuHeight;
3625		}
3626            }
3627	}
3628	wmPtr->configWidth = configEventPtr->width;
3629	wmPtr->configHeight = configEventPtr->height;
3630    }
3631
3632    if (dispPtr->flags & TK_DISPLAY_WM_TRACING) {
3633	printf("ConfigureEvent: %s x = %d y = %d, width = %d, height = %d\n",
3634		winPtr->pathName, configEventPtr->x, configEventPtr->y,
3635		configEventPtr->width, configEventPtr->height);
3636	printf("    send_event = %d, serial = %ld (win %p, wrapper %p)\n",
3637		configEventPtr->send_event, configEventPtr->serial,
3638		winPtr, wrapperPtr);
3639    }
3640    wrapperPtr->changes.width = configEventPtr->width;
3641    wrapperPtr->changes.height = configEventPtr->height;
3642    wrapperPtr->changes.border_width = configEventPtr->border_width;
3643    wrapperPtr->changes.sibling = configEventPtr->above;
3644    wrapperPtr->changes.stack_mode = Above;
3645
3646    /*
3647     * Reparenting window managers make life difficult.  If the
3648     * window manager reparents a top-level window then the x and y
3649     * information that comes in events for the window is wrong:
3650     * it gives the location of the window inside its decorative
3651     * parent, rather than the location of the window in root
3652     * coordinates, which is what we want.  Window managers
3653     * are supposed to send synthetic events with the correct
3654     * information, but ICCCM doesn't require them to do this
3655     * under all conditions, and the information provided doesn't
3656     * include everything we need here.  So, the code below
3657     * maintains a bunch of information about the parent window.
3658     * If the window hasn't been reparented, we pretend that
3659     * there is a parent shrink-wrapped around the window.
3660     */
3661
3662    if (dispPtr->flags & TK_DISPLAY_WM_TRACING) {
3663	printf("    %s parent == %p, above %p\n",
3664		winPtr->pathName, (void *) wmPtr->reparent,
3665		(void *) configEventPtr->above);
3666    }
3667
3668    if ((wmPtr->reparent == None) || !ComputeReparentGeometry(wmPtr)) {
3669	wmPtr->parentWidth = configEventPtr->width
3670		+ 2*configEventPtr->border_width;
3671	wmPtr->parentHeight = configEventPtr->height
3672		+ 2*configEventPtr->border_width;
3673	wrapperPtr->changes.x = wmPtr->x = configEventPtr->x;
3674	wrapperPtr->changes.y = wmPtr->y = configEventPtr->y;
3675	if (wmPtr->flags & WM_NEGATIVE_X) {
3676	    wmPtr->x = wmPtr->vRootWidth - (wmPtr->x + wmPtr->parentWidth);
3677	}
3678	if (wmPtr->flags & WM_NEGATIVE_Y) {
3679	    wmPtr->y = wmPtr->vRootHeight - (wmPtr->y + wmPtr->parentHeight);
3680	}
3681    }
3682
3683    /*
3684     * Make sure that the toplevel and menubar are properly positioned within
3685     * the wrapper.  If the menuHeight happens to be zero, we'll get a
3686     * BadValue X error that we want to ignore [Bug: 3377]
3687     */
3688    handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
3689	    (Tk_ErrorProc *) NULL, (ClientData) NULL);
3690    XMoveResizeWindow(winPtr->display, winPtr->window, 0,
3691	    wmPtr->menuHeight, (unsigned) wrapperPtr->changes.width,
3692	    (unsigned) (wrapperPtr->changes.height - wmPtr->menuHeight));
3693    Tk_DeleteErrorHandler(handler);
3694    if ((wmPtr->menubar != NULL)
3695	    && ((Tk_Width(wmPtr->menubar) != wrapperPtr->changes.width)
3696	    || (Tk_Height(wmPtr->menubar) != wmPtr->menuHeight))) {
3697	Tk_MoveResizeWindow(wmPtr->menubar, 0, 0, wrapperPtr->changes.width,
3698		wmPtr->menuHeight);
3699    }
3700
3701    /*
3702     * Update the coordinates in the toplevel (they should refer to the
3703     * position in root window coordinates, not the coordinates of the
3704     * wrapper window).  Then synthesize a ConfigureNotify event to tell
3705     * the application about the change.
3706     */
3707
3708    winPtr->changes.x = wrapperPtr->changes.x;
3709    winPtr->changes.y = wrapperPtr->changes.y + wmPtr->menuHeight;
3710    winPtr->changes.width = wrapperPtr->changes.width;
3711    winPtr->changes.height = wrapperPtr->changes.height - wmPtr->menuHeight;
3712    TkDoConfigureNotify(winPtr);
3713}
3714
3715/*
3716 *----------------------------------------------------------------------
3717 *
3718 * ReparentEvent --
3719 *
3720 *	This procedure is called to handle ReparentNotify events on
3721 *	wrapper windows.
3722 *
3723 * Results:
3724 *	None.
3725 *
3726 * Side effects:
3727 *	Information gets updated in the WmInfo structure for the window.
3728 *
3729 *----------------------------------------------------------------------
3730 */
3731
3732static void
3733ReparentEvent(wmPtr, reparentEventPtr)
3734    WmInfo *wmPtr;			/* Information about toplevel window. */
3735    XReparentEvent *reparentEventPtr;	/* Event that just occurred for
3736					 * wmPtr->wrapperPtr. */
3737{
3738    TkWindow *wrapperPtr = wmPtr->wrapperPtr;
3739    Window vRoot, ancestor, *children, dummy2, *virtualRootPtr;
3740    Atom actualType;
3741    int actualFormat;
3742    unsigned long numItems, bytesAfter;
3743    unsigned int dummy;
3744    Tk_ErrorHandler handler;
3745    TkDisplay *dispPtr = wmPtr->winPtr->dispPtr;
3746
3747    /*
3748     * Identify the root window for wrapperPtr.  This is tricky because of
3749     * virtual root window managers like tvtwm.  If the window has a
3750     * property named __SWM_ROOT or __WM_ROOT then this property gives
3751     * the id for a virtual root window that should be used instead of
3752     * the root window of the screen.
3753     */
3754
3755    vRoot = RootWindow(wrapperPtr->display, wrapperPtr->screenNum);
3756    wmPtr->vRoot = None;
3757    handler = Tk_CreateErrorHandler(wrapperPtr->display, -1, -1, -1,
3758	    (Tk_ErrorProc *) NULL, (ClientData) NULL);
3759    if (((XGetWindowProperty(wrapperPtr->display, wrapperPtr->window,
3760	    Tk_InternAtom((Tk_Window) wrapperPtr, "__WM_ROOT"), 0, (long) 1,
3761	    False, XA_WINDOW, &actualType, &actualFormat, &numItems,
3762	    &bytesAfter, (unsigned char **) &virtualRootPtr) == Success)
3763	    && (actualType == XA_WINDOW))
3764	    || ((XGetWindowProperty(wrapperPtr->display, wrapperPtr->window,
3765	    Tk_InternAtom((Tk_Window) wrapperPtr, "__SWM_ROOT"), 0, (long) 1,
3766	    False, XA_WINDOW, &actualType, &actualFormat, &numItems,
3767	    &bytesAfter, (unsigned char **) &virtualRootPtr) == Success)
3768	    && (actualType == XA_WINDOW))) {
3769	if ((actualFormat == 32) && (numItems == 1)) {
3770	    vRoot = wmPtr->vRoot = *virtualRootPtr;
3771	} else if (dispPtr->flags & TK_DISPLAY_WM_TRACING) {
3772	    printf("%s format %d numItems %ld\n",
3773		    "ReparentEvent got bogus VROOT property:", actualFormat,
3774		    numItems);
3775	}
3776	XFree((char *) virtualRootPtr);
3777    }
3778    Tk_DeleteErrorHandler(handler);
3779
3780    if (dispPtr->flags & TK_DISPLAY_WM_TRACING) {
3781	printf("ReparentEvent: %s (%p) reparented to 0x%x, vRoot = 0x%x\n",
3782		wmPtr->winPtr->pathName, wmPtr->winPtr,
3783		(unsigned int) reparentEventPtr->parent, (unsigned int) vRoot);
3784    }
3785
3786    /*
3787     * Fetch correct geometry information for the new virtual root.
3788     */
3789
3790    UpdateVRootGeometry(wmPtr);
3791
3792    /*
3793     * If the window's new parent is the root window, then mark it as
3794     * no longer reparented.
3795     */
3796
3797    if (reparentEventPtr->parent == vRoot) {
3798	noReparent:
3799	wmPtr->reparent = None;
3800	wmPtr->parentWidth = wrapperPtr->changes.width;
3801	wmPtr->parentHeight = wrapperPtr->changes.height;
3802	wmPtr->xInParent = wmPtr->yInParent = 0;
3803	wrapperPtr->changes.x = reparentEventPtr->x;
3804	wrapperPtr->changes.y = reparentEventPtr->y;
3805	wmPtr->winPtr->changes.x = reparentEventPtr->x;
3806	wmPtr->winPtr->changes.y = reparentEventPtr->y + wmPtr->menuHeight;
3807	return;
3808    }
3809
3810    /*
3811     * Search up the window hierarchy to find the ancestor of this
3812     * window that is just below the (virtual) root.  This is tricky
3813     * because it's possible that things have changed since the event
3814     * was generated so that the ancestry indicated by the event no
3815     * longer exists.  If this happens then an error will occur and
3816     * we just discard the event (there will be a more up-to-date
3817     * ReparentNotify event coming later).
3818     */
3819
3820    handler = Tk_CreateErrorHandler(wrapperPtr->display, -1, -1, -1,
3821	    (Tk_ErrorProc *) NULL, (ClientData) NULL);
3822    wmPtr->reparent = reparentEventPtr->parent;
3823    while (1) {
3824	if (XQueryTree(wrapperPtr->display, wmPtr->reparent, &dummy2,
3825		&ancestor, &children, &dummy) == 0) {
3826	    Tk_DeleteErrorHandler(handler);
3827	    goto noReparent;
3828	}
3829	XFree((char *) children);
3830	if ((ancestor == vRoot) ||
3831		(ancestor == RootWindow(wrapperPtr->display,
3832		wrapperPtr->screenNum))) {
3833	    break;
3834	}
3835	wmPtr->reparent = ancestor;
3836    }
3837    Tk_DeleteErrorHandler(handler);
3838
3839    if (!ComputeReparentGeometry(wmPtr)) {
3840	goto noReparent;
3841    }
3842}
3843
3844/*
3845 *----------------------------------------------------------------------
3846 *
3847 * ComputeReparentGeometry --
3848 *
3849 *	This procedure is invoked to recompute geometry information
3850 *	related to a reparented top-level window, such as the position
3851 *	and total size of the parent and the position within it of
3852 *	the top-level window.
3853 *
3854 * Results:
3855 *	The return value is 1 if everything completed successfully
3856 *	and 0 if an error occurred while querying information about
3857 *	winPtr's parents.  In this case winPtr is marked as no longer
3858 *	being reparented.
3859 *
3860 * Side effects:
3861 *	Geometry information in wmPtr, wmPtr->winPtr, and
3862 *	wmPtr->wrapperPtr gets updated.
3863 *
3864 *----------------------------------------------------------------------
3865 */
3866
3867static int
3868ComputeReparentGeometry(wmPtr)
3869    WmInfo *wmPtr;		/* Information about toplevel window
3870				 * whose reparent info is to be recomputed. */
3871{
3872    TkWindow *wrapperPtr = wmPtr->wrapperPtr;
3873    int width, height, bd;
3874    unsigned int dummy;
3875    int xOffset, yOffset, x, y;
3876    Window dummy2;
3877    Status status;
3878    Tk_ErrorHandler handler;
3879    TkDisplay *dispPtr = wmPtr->winPtr->dispPtr;
3880
3881    handler = Tk_CreateErrorHandler(wrapperPtr->display, -1, -1, -1,
3882	    (Tk_ErrorProc *) NULL, (ClientData) NULL);
3883    (void) XTranslateCoordinates(wrapperPtr->display, wrapperPtr->window,
3884	    wmPtr->reparent, 0, 0, &xOffset, &yOffset, &dummy2);
3885    status = XGetGeometry(wrapperPtr->display, wmPtr->reparent,
3886	    &dummy2, &x, &y, (unsigned int *) &width,
3887	    (unsigned int *) &height, (unsigned int *) &bd, &dummy);
3888    Tk_DeleteErrorHandler(handler);
3889    if (status == 0) {
3890	/*
3891	 * It appears that the reparented parent went away and
3892	 * no-one told us.  Reset the window to indicate that
3893	 * it's not reparented.
3894	 */
3895	wmPtr->reparent = None;
3896	wmPtr->xInParent = wmPtr->yInParent = 0;
3897	return 0;
3898    }
3899    wmPtr->xInParent = xOffset + bd;
3900    wmPtr->yInParent = yOffset + bd;
3901    wmPtr->parentWidth = width + 2*bd;
3902    wmPtr->parentHeight = height + 2*bd;
3903
3904    /*
3905     * Some tricky issues in updating wmPtr->x and wmPtr->y:
3906     *
3907     * 1. Don't update them if the event occurred because of something
3908     * we did (i.e. WM_SYNC_PENDING and WM_MOVE_PENDING are both set).
3909     * This is because window managers treat coords differently than Tk,
3910     * and no two window managers are alike. If the window manager moved
3911     * the window because we told it to, remember the coordinates we told
3912     * it, not the ones it actually moved it to.  This allows us to move
3913     * the window back to the same coordinates later and get the same
3914     * result. Without this check, windows can "walk" across the screen
3915     * under some conditions.
3916     *
3917     * 2. Don't update wmPtr->x and wmPtr->y unless wrapperPtr->changes.x
3918     * or wrapperPtr->changes.y has changed (otherwise a size change can
3919     * spoof us into thinking that the position changed too and defeat
3920     * the intent of (1) above.
3921     *
3922     * (As of 9/96 the above 2 comments appear to be stale.  They're
3923     * being left in place as a reminder of what was once true (and
3924     * perhaps should still be true?)).
3925     *
3926     * 3. Ignore size changes coming from the window system if we're
3927     * about to change the size ourselves but haven't seen the event for
3928     * it yet:  our size change is supposed to take priority.
3929     */
3930
3931    if (!(wmPtr->flags & WM_MOVE_PENDING)
3932	    && ((wrapperPtr->changes.x != (x + wmPtr->xInParent))
3933	    || (wrapperPtr->changes.y != (y + wmPtr->yInParent)))) {
3934	wmPtr->x = x;
3935	if (wmPtr->flags & WM_NEGATIVE_X) {
3936	    wmPtr->x = wmPtr->vRootWidth - (wmPtr->x + wmPtr->parentWidth);
3937	}
3938	wmPtr->y = y;
3939	if (wmPtr->flags & WM_NEGATIVE_Y) {
3940	    wmPtr->y = wmPtr->vRootHeight - (wmPtr->y + wmPtr->parentHeight);
3941	}
3942    }
3943
3944    wrapperPtr->changes.x = x + wmPtr->xInParent;
3945    wrapperPtr->changes.y = y + wmPtr->yInParent;
3946    if (dispPtr->flags & TK_DISPLAY_WM_TRACING) {
3947	printf("wrapperPtr %p coords %d,%d\n",
3948		wrapperPtr, wrapperPtr->changes.x, wrapperPtr->changes.y);
3949	printf("     wmPtr %p coords %d,%d, offsets %d %d\n",
3950		wmPtr, wmPtr->x, wmPtr->y, wmPtr->xInParent, wmPtr->yInParent);
3951    }
3952    return 1;
3953}
3954
3955/*
3956 *----------------------------------------------------------------------
3957 *
3958 * WrapperEventProc --
3959 *
3960 *	This procedure is invoked by the event loop when a wrapper window
3961 *	is restructured.
3962 *
3963 * Results:
3964 *	None.
3965 *
3966 * Side effects:
3967 *	Tk's internal data structures for the window get modified to
3968 *	reflect the structural change.
3969 *
3970 *----------------------------------------------------------------------
3971 */
3972
3973static void
3974WrapperEventProc(clientData, eventPtr)
3975    ClientData clientData;		/* Information about toplevel window. */
3976    XEvent *eventPtr;			/* Event that just happened. */
3977{
3978    WmInfo *wmPtr = (WmInfo *) clientData;
3979    XEvent mapEvent;
3980    TkDisplay *dispPtr = wmPtr->winPtr->dispPtr;
3981
3982    wmPtr->flags |= WM_VROOT_OFFSET_STALE;
3983    if (eventPtr->type == DestroyNotify) {
3984	Tk_ErrorHandler handler;
3985
3986	if (!(wmPtr->wrapperPtr->flags & TK_ALREADY_DEAD)) {
3987	    /*
3988	     * A top-level window was deleted externally (e.g., by the window
3989	     * manager).  This is probably not a good thing, but cleanup as
3990	     * best we can.  The error handler is needed because
3991	     * Tk_DestroyWindow will try to destroy the window, but of course
3992	     * it's already gone.
3993	     */
3994
3995	    handler = Tk_CreateErrorHandler(wmPtr->winPtr->display, -1, -1, -1,
3996		    (Tk_ErrorProc *) NULL, (ClientData) NULL);
3997	    Tk_DestroyWindow((Tk_Window) wmPtr->winPtr);
3998	    Tk_DeleteErrorHandler(handler);
3999	}
4000	if (dispPtr->flags & TK_DISPLAY_WM_TRACING) {
4001	    printf("TopLevelEventProc: %s deleted\n", wmPtr->winPtr->pathName);
4002	}
4003    } else if (eventPtr->type == ConfigureNotify) {
4004	/*
4005	 * Ignore the event if the window has never been mapped yet.
4006	 * Such an event occurs only in weird cases like changing the
4007	 * internal border width of a top-level window, which results
4008	 * in a synthetic Configure event.  These events are not relevant
4009	 * to us, and if we process them confusion may result (e.g. we
4010	 * may conclude erroneously that the user repositioned or resized
4011	 * the window).
4012	 */
4013
4014	if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
4015	    ConfigureEvent(wmPtr, &eventPtr->xconfigure);
4016	}
4017    } else if (eventPtr->type == MapNotify) {
4018	wmPtr->wrapperPtr->flags |= TK_MAPPED;
4019	wmPtr->winPtr->flags |= TK_MAPPED;
4020	XMapWindow(wmPtr->winPtr->display, wmPtr->winPtr->window);
4021	goto doMapEvent;
4022    } else if (eventPtr->type == UnmapNotify) {
4023	wmPtr->wrapperPtr->flags &= ~TK_MAPPED;
4024	wmPtr->winPtr->flags &= ~TK_MAPPED;
4025	XUnmapWindow(wmPtr->winPtr->display, wmPtr->winPtr->window);
4026	goto doMapEvent;
4027    } else if (eventPtr->type == ReparentNotify) {
4028	ReparentEvent(wmPtr, &eventPtr->xreparent);
4029    }
4030    return;
4031
4032    doMapEvent:
4033    mapEvent = *eventPtr;
4034    mapEvent.xmap.event = wmPtr->winPtr->window;
4035    mapEvent.xmap.window = wmPtr->winPtr->window;
4036    Tk_HandleEvent(&mapEvent);
4037}
4038
4039/*
4040 *----------------------------------------------------------------------
4041 *
4042 * TopLevelReqProc --
4043 *
4044 *	This procedure is invoked by the geometry manager whenever
4045 *	the requested size for a top-level window is changed.
4046 *
4047 * Results:
4048 *	None.
4049 *
4050 * Side effects:
4051 *	Arrange for the window to be resized to satisfy the request
4052 *	(this happens as a when-idle action).
4053 *
4054 *----------------------------------------------------------------------
4055 */
4056
4057	/* ARGSUSED */
4058static void
4059TopLevelReqProc(dummy, tkwin)
4060    ClientData dummy;			/* Not used. */
4061    Tk_Window tkwin;			/* Information about window. */
4062{
4063    TkWindow *winPtr = (TkWindow *) tkwin;
4064    WmInfo *wmPtr;
4065
4066    wmPtr = winPtr->wmInfoPtr;
4067
4068    if ((wmPtr->width >= 0) && (wmPtr->height >= 0)) {
4069	/*
4070	 * Explicit dimensions have been set for this window, so we
4071	 * should ignore the geometry request.  It's actually important
4072	 * to ignore the geometry request because, due to quirks in
4073	 * window managers, invoking UpdateGeometryInfo may cause the
4074	 * window to move.  For example, if "wm geometry -10-20" was
4075	 * invoked, the window may be positioned incorrectly the first
4076	 * time it appears (because we didn't know the proper width of
4077	 * the window manager borders); if we invoke UpdateGeometryInfo
4078	 * again, the window will be positioned correctly, which may
4079	 * cause it to jump on the screen.
4080	 */
4081
4082	return;
4083    }
4084
4085    wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
4086    if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
4087	Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
4088	wmPtr->flags |= WM_UPDATE_PENDING;
4089    }
4090
4091    /*
4092     * If the window isn't being positioned by its upper left corner
4093     * then we have to move it as well.
4094     */
4095
4096    if (wmPtr->flags & (WM_NEGATIVE_X | WM_NEGATIVE_Y)) {
4097	wmPtr->flags |= WM_MOVE_PENDING;
4098    }
4099}
4100
4101/*
4102 *----------------------------------------------------------------------
4103 *
4104 * UpdateGeometryInfo --
4105 *
4106 *	This procedure is invoked when a top-level window is first
4107 *	mapped, and also as a when-idle procedure, to bring the
4108 *	geometry and/or position of a top-level window back into
4109 *	line with what has been requested by the user and/or widgets.
4110 *	This procedure doesn't return until the window manager has
4111 *	responded to the geometry change.
4112 *
4113 * Results:
4114 *	None.
4115 *
4116 * Side effects:
4117 *	The size and location of both the toplevel window and its wrapper
4118 *	may change, unless the WM prevents that from happening.
4119 *
4120 *----------------------------------------------------------------------
4121 */
4122
4123static void
4124UpdateGeometryInfo(clientData)
4125    ClientData clientData;		/* Pointer to the window's record. */
4126{
4127    register TkWindow *winPtr = (TkWindow *) clientData;
4128    register WmInfo *wmPtr = winPtr->wmInfoPtr;
4129    int x, y, width, height, min, max;
4130    unsigned long serial;
4131
4132    wmPtr->flags &= ~WM_UPDATE_PENDING;
4133
4134    /*
4135     * Compute the new size for the top-level window.  See the
4136     * user documentation for details on this, but the size
4137     * requested depends on (a) the size requested internally
4138     * by the window's widgets, (b) the size requested by the
4139     * user in a "wm geometry" command or via wm-based interactive
4140     * resizing (if any), (c) whether or not the window is
4141     * gridded, and (d) the current min or max size for
4142     * the toplevel. Don't permit sizes <= 0 because this
4143     * upsets the X server.
4144     */
4145
4146    if (wmPtr->width == -1) {
4147	width = winPtr->reqWidth;
4148    } else if (wmPtr->gridWin != NULL) {
4149	width = winPtr->reqWidth
4150		+ (wmPtr->width - wmPtr->reqGridWidth)*wmPtr->widthInc;
4151    } else {
4152	width = wmPtr->width;
4153    }
4154    if (width <= 0) {
4155	width = 1;
4156    }
4157    /*
4158     * Account for window max/min width
4159     */
4160    if (wmPtr->gridWin != NULL) {
4161	min = winPtr->reqWidth
4162		+ (wmPtr->minWidth - wmPtr->reqGridWidth)*wmPtr->widthInc;
4163	if (wmPtr->maxWidth > 0) {
4164	    max = winPtr->reqWidth
4165		+ (wmPtr->maxWidth - wmPtr->reqGridWidth)*wmPtr->widthInc;
4166	} else {
4167	    max = 0;
4168	}
4169    } else {
4170	min = wmPtr->minWidth;
4171	max = wmPtr->maxWidth;
4172    }
4173    if (width < min) {
4174	width = min;
4175    } else if ((max > 0) && (width > max)) {
4176	width = max;
4177    }
4178
4179    if (wmPtr->height == -1) {
4180	height = winPtr->reqHeight;
4181    } else if (wmPtr->gridWin != NULL) {
4182	height = winPtr->reqHeight
4183		+ (wmPtr->height - wmPtr->reqGridHeight)*wmPtr->heightInc;
4184    } else {
4185	height = wmPtr->height;
4186    }
4187    if (height <= 0) {
4188	height = 1;
4189    }
4190    /*
4191     * Account for window max/min height
4192     */
4193    if (wmPtr->gridWin != NULL) {
4194	min = winPtr->reqHeight
4195		+ (wmPtr->minHeight - wmPtr->reqGridHeight)*wmPtr->heightInc;
4196	if (wmPtr->maxHeight > 0) {
4197	    max = winPtr->reqHeight
4198		+ (wmPtr->maxHeight - wmPtr->reqGridHeight)*wmPtr->heightInc;
4199	} else {
4200	    max = 0;
4201	}
4202    } else {
4203	min = wmPtr->minHeight;
4204	max = wmPtr->maxHeight;
4205    }
4206    if (height < min) {
4207	height = min;
4208    } else if ((max > 0) && (height > max)) {
4209	height = max;
4210    }
4211
4212    /*
4213     * Compute the new position for the upper-left pixel of the window's
4214     * decorative frame.  This is tricky, because we need to include the
4215     * border widths supplied by a reparented parent in this calculation,
4216     * but can't use the parent's current overall size since that may
4217     * change as a result of this code.
4218     */
4219
4220    if (wmPtr->flags & WM_NEGATIVE_X) {
4221	x = wmPtr->vRootWidth - wmPtr->x
4222		- (width + (wmPtr->parentWidth - winPtr->changes.width));
4223    } else {
4224	x =  wmPtr->x;
4225    }
4226    if (wmPtr->flags & WM_NEGATIVE_Y) {
4227	y = wmPtr->vRootHeight - wmPtr->y
4228		- (height + (wmPtr->parentHeight - winPtr->changes.height));
4229    } else {
4230	y =  wmPtr->y;
4231    }
4232
4233    /*
4234     * If the window's size is going to change and the window is
4235     * supposed to not be resizable by the user, then we have to
4236     * update the size hints.  There may also be a size-hint-update
4237     * request pending from somewhere else, too.
4238     */
4239
4240    if (((width != winPtr->changes.width)
4241	    || (height != winPtr->changes.height))
4242	    && (wmPtr->gridWin == NULL)
4243	    && ((wmPtr->sizeHintsFlags & (PMinSize|PMaxSize)) == 0)) {
4244	wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
4245    }
4246    if (wmPtr->flags & WM_UPDATE_SIZE_HINTS) {
4247	UpdateSizeHints(winPtr, width, height);
4248    }
4249
4250    /*
4251     * Reconfigure the wrapper if it isn't already configured correctly.
4252     * A few tricky points:
4253     *
4254     * 1. If the window is embeddedand the container is also in this
4255     *    process, don't actually reconfigure the window; just pass the
4256     *    desired size on to the container.  Also, zero out any position
4257     *    information, since embedded windows are not allowed to move.
4258     * 2. Sometimes the window manager will give us a different size
4259     *    than we asked for (e.g. mwm has a minimum size for windows), so
4260     *    base the size check on what we *asked for* last time, not what we
4261     *    got.
4262     * 3. Can't just reconfigure always, because we may not get a
4263     *    ConfigureNotify event back if nothing changed, so
4264     *    WaitForConfigureNotify will hang a long time.
4265     * 4. Don't move window unless a new position has been requested for
4266     *	  it.  This is because of "features" in some window managers (e.g.
4267     *    twm, as of 4/24/91) where they don't interpret coordinates
4268     *    according to ICCCM.  Moving a window to its current location may
4269     *    cause it to shift position on the screen.
4270     */
4271
4272    if ((winPtr->flags & (TK_EMBEDDED|TK_BOTH_HALVES))
4273	    == (TK_EMBEDDED|TK_BOTH_HALVES)) {
4274	TkWindow *childPtr = TkpGetOtherWindow(winPtr);
4275	/*
4276	 * This window is embedded and the container is also in this
4277	 * process, so we don't need to do anything special about the
4278	 * geometry, except to make sure that the desired size is known
4279	 * by the container.  Also, zero out any position information,
4280	 * since embedded windows are not allowed to move.
4281	 */
4282
4283	wmPtr->x = wmPtr->y = 0;
4284	wmPtr->flags &= ~(WM_NEGATIVE_X|WM_NEGATIVE_Y);
4285	if (childPtr != NULL) {
4286	    height += wmPtr->menuHeight;
4287	    Tk_GeometryRequest((Tk_Window) childPtr, width, height);
4288	}
4289	return;
4290    }
4291
4292    serial = NextRequest(winPtr->display);
4293    height += wmPtr->menuHeight;
4294    if (wmPtr->flags & WM_MOVE_PENDING) {
4295	if ((x + wmPtr->xInParent == winPtr->changes.x) &&
4296		(y + wmPtr->yInParent + wmPtr->menuHeight == winPtr->changes.y)
4297		&& (width == wmPtr->wrapperPtr->changes.width)
4298		&& (height == wmPtr->wrapperPtr->changes.height)) {
4299	    /*
4300	     * The window already has the correct geometry, so don't bother
4301	     * to configure it;  the X server appears to ignore these
4302	     * requests, so we won't get back a ConfigureNotify and the
4303	     * WaitForConfigureNotify call below will hang for a while.
4304	     */
4305
4306	    wmPtr->flags &= ~WM_MOVE_PENDING;
4307	    return;
4308	}
4309	wmPtr->configWidth = width;
4310	wmPtr->configHeight = height;
4311	if (winPtr->dispPtr->flags & TK_DISPLAY_WM_TRACING) {
4312	   printf("UpdateGeometryInfo moving to %d %d, resizing to %d x %d,\n",
4313                   x, y, width, height);
4314	}
4315	XMoveResizeWindow(winPtr->display, wmPtr->wrapperPtr->window, x, y,
4316		(unsigned) width, (unsigned) height);
4317    } else if ((width != wmPtr->configWidth)
4318	    || (height != wmPtr->configHeight)) {
4319	if ((width == wmPtr->wrapperPtr->changes.width)
4320		&& (height == wmPtr->wrapperPtr->changes.height)) {
4321	    /*
4322	     * The window is already just the size we want, so don't bother
4323	     * to configure it;  the X server appears to ignore these
4324	     * requests, so we won't get back a ConfigureNotify and the
4325	     * WaitForConfigureNotify call below will hang for a while.
4326	     */
4327
4328	    return;
4329	}
4330	wmPtr->configWidth = width;
4331	wmPtr->configHeight = height;
4332	if (winPtr->dispPtr->flags & TK_DISPLAY_WM_TRACING) {
4333	    printf("UpdateGeometryInfo resizing %p to %d x %d\n",
4334		    (void *)wmPtr->wrapperPtr->window, width, height);
4335	}
4336	XResizeWindow(winPtr->display, wmPtr->wrapperPtr->window,
4337		(unsigned) width, (unsigned) height);
4338    } else if ((wmPtr->menubar != NULL)
4339	    && ((Tk_Width(wmPtr->menubar) != wmPtr->wrapperPtr->changes.width)
4340	    || (Tk_Height(wmPtr->menubar) != wmPtr->menuHeight))) {
4341	/*
4342	 * It is possible that the window's overall size has not changed
4343	 * but the menu size has.
4344	 */
4345
4346	Tk_MoveResizeWindow(wmPtr->menubar, 0, 0,
4347		wmPtr->wrapperPtr->changes.width, wmPtr->menuHeight);
4348	XResizeWindow(winPtr->display, wmPtr->wrapperPtr->window,
4349		(unsigned) width, (unsigned) height);
4350    } else {
4351	return;
4352    }
4353
4354    /*
4355     * Wait for the configure operation to complete.  Don't need to do
4356     * this, however, if the window is about to be mapped:  it will be
4357     * taken care of elsewhere.
4358     */
4359
4360    if (!(wmPtr->flags & WM_ABOUT_TO_MAP)) {
4361	WaitForConfigureNotify(winPtr, serial);
4362    }
4363}
4364
4365/*
4366 *--------------------------------------------------------------
4367 *
4368 * UpdateSizeHints --
4369 *
4370 *	This procedure is called to update the window manager's
4371 *	size hints information from the information in a WmInfo
4372 *	structure.
4373 *
4374 * Results:
4375 *	None.
4376 *
4377 * Side effects:
4378 *	Properties get changed for winPtr.
4379 *
4380 *--------------------------------------------------------------
4381 */
4382
4383static void
4384UpdateSizeHints(winPtr, newWidth, newHeight)
4385    TkWindow *winPtr;
4386    int newWidth;
4387    int newHeight;
4388{
4389    register WmInfo *wmPtr = winPtr->wmInfoPtr;
4390    XSizeHints *hintsPtr;
4391    int maxWidth, maxHeight;
4392
4393    wmPtr->flags &= ~WM_UPDATE_SIZE_HINTS;
4394
4395    hintsPtr = XAllocSizeHints();
4396    if (hintsPtr == NULL) {
4397	return;
4398    }
4399
4400    /*
4401     * Compute the pixel-based sizes for the various fields in the
4402     * size hints structure, based on the grid-based sizes in
4403     * our structure.
4404     */
4405
4406    GetMaxSize(wmPtr, &maxWidth, &maxHeight);
4407    if (wmPtr->gridWin != NULL) {
4408	hintsPtr->base_width = winPtr->reqWidth
4409		- (wmPtr->reqGridWidth * wmPtr->widthInc);
4410	if (hintsPtr->base_width < 0) {
4411	    hintsPtr->base_width = 0;
4412	}
4413	hintsPtr->base_height = winPtr->reqHeight + wmPtr->menuHeight
4414		- (wmPtr->reqGridHeight * wmPtr->heightInc);
4415	if (hintsPtr->base_height < 0) {
4416	    hintsPtr->base_height = 0;
4417	}
4418	hintsPtr->min_width = hintsPtr->base_width
4419		+ (wmPtr->minWidth * wmPtr->widthInc);
4420	hintsPtr->min_height = hintsPtr->base_height
4421		+ (wmPtr->minHeight * wmPtr->heightInc);
4422	hintsPtr->max_width = hintsPtr->base_width
4423		+ (maxWidth * wmPtr->widthInc);
4424	hintsPtr->max_height = hintsPtr->base_height
4425		+ (maxHeight * wmPtr->heightInc);
4426    } else {
4427	hintsPtr->min_width = wmPtr->minWidth;
4428	hintsPtr->min_height = wmPtr->minHeight;
4429	hintsPtr->max_width = maxWidth;
4430	hintsPtr->max_height = maxHeight;
4431	hintsPtr->base_width = 0;
4432	hintsPtr->base_height = 0;
4433    }
4434    hintsPtr->width_inc = wmPtr->widthInc;
4435    hintsPtr->height_inc = wmPtr->heightInc;
4436    hintsPtr->min_aspect.x = wmPtr->minAspect.x;
4437    hintsPtr->min_aspect.y = wmPtr->minAspect.y;
4438    hintsPtr->max_aspect.x = wmPtr->maxAspect.x;
4439    hintsPtr->max_aspect.y = wmPtr->maxAspect.y;
4440    hintsPtr->win_gravity = wmPtr->gravity;
4441    hintsPtr->flags = wmPtr->sizeHintsFlags | PMinSize;
4442
4443    /*
4444     * If the window isn't supposed to be resizable, then set the
4445     * minimum and maximum dimensions to be the same.
4446     */
4447
4448    if (wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) {
4449	hintsPtr->max_width = hintsPtr->min_width = newWidth;
4450    	hintsPtr->flags |= PMaxSize;
4451    }
4452    if (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE) {
4453	hintsPtr->max_height = hintsPtr->min_height =
4454	    newHeight + wmPtr->menuHeight;
4455    	hintsPtr->flags |= PMaxSize;
4456    }
4457
4458    XSetWMNormalHints(winPtr->display, wmPtr->wrapperPtr->window, hintsPtr);
4459
4460    XFree((char *) hintsPtr);
4461}
4462
4463/*
4464 *--------------------------------------------------------------
4465 *
4466 * UpdateTitle --
4467 *
4468 *	This procedure is called to update the window title and
4469 *	icon name.  It sets the ICCCM-defined properties WM_NAME
4470 *	and WM_ICON_NAME for older window managers, and the
4471 *	freedesktop.org-defined _NET_WM_NAME and _NET_WM_ICON_NAME
4472 *	properties for newer ones.  The ICCCM properties are
4473 *	stored in the system encoding, the newer properties
4474 *	are stored in UTF-8.
4475 *
4476 *	NOTE: the ICCCM specifies that WM_NAME and WM_ICON_NAME are
4477 *	stored in ISO-Latin-1.  Tk has historically used the default
4478 *	system encoding (since 8.1).  It's not clear whether this is
4479 *	correct or not.
4480 *
4481 * Side effects:
4482 *	Properties get changed for winPtr.
4483 *
4484 *--------------------------------------------------------------
4485 */
4486static void
4487UpdateTitle(winPtr)
4488    TkWindow *winPtr;
4489{
4490    WmInfo *wmPtr = winPtr->wmInfoPtr;
4491    Atom XA_UTF8_STRING = Tk_InternAtom((Tk_Window) winPtr, "UTF8_STRING");
4492    const char *string;
4493    Tcl_DString ds;
4494
4495    /*
4496     * Set window title:
4497     */
4498    string = (wmPtr->title != NULL) ? wmPtr->title : winPtr->nameUid;
4499    Tcl_UtfToExternalDString(NULL, string, -1, &ds);
4500    XStoreName(winPtr->display, wmPtr->wrapperPtr->window,
4501	    Tcl_DStringValue(&ds));
4502    Tcl_DStringFree(&ds);
4503
4504    XChangeProperty(winPtr->display, wmPtr->wrapperPtr->window,
4505	    Tk_InternAtom((Tk_Window) winPtr, "_NET_WM_NAME"),
4506	    XA_UTF8_STRING, 8, PropModeReplace,
4507	    (const unsigned char*)string, (signed int)strlen(string));
4508
4509    /*
4510     * Set icon name:
4511     */
4512    if (wmPtr->iconName != NULL) {
4513	Tcl_UtfToExternalDString(NULL, wmPtr->iconName, -1, &ds);
4514	XSetIconName(winPtr->display, wmPtr->wrapperPtr->window,
4515		Tcl_DStringValue(&ds));
4516	Tcl_DStringFree(&ds);
4517
4518	XChangeProperty(winPtr->display, wmPtr->wrapperPtr->window,
4519		Tk_InternAtom((Tk_Window) winPtr, "_NET_WM_ICON_NAME"),
4520		XA_UTF8_STRING, 8, PropModeReplace,
4521		(const unsigned char*)wmPtr->iconName,
4522		(signed int)strlen(wmPtr->iconName));
4523    }
4524}
4525
4526/*
4527 *--------------------------------------------------------------
4528 *
4529 * UpdatePhotoIcon --
4530 *
4531 *	This procedure is called to update the window ohoto icon.
4532 *	It sets the EWMH-defined properties _NET_WM_ICON.
4533 *
4534 * Side effects:
4535 *	Properties get changed for winPtr.
4536 *
4537 *--------------------------------------------------------------
4538 */
4539static void
4540UpdatePhotoIcon(winPtr)
4541    TkWindow *winPtr;
4542{
4543    WmInfo *wmPtr = winPtr->wmInfoPtr;
4544    unsigned char *data = wmPtr->iconDataPtr;
4545    int size = wmPtr->iconDataSize;
4546
4547    if (data == NULL) {
4548	data = winPtr->dispPtr->iconDataPtr;
4549	size = winPtr->dispPtr->iconDataSize;
4550    }
4551    if (data != NULL) {
4552	/*
4553	 * Set icon:
4554	 */
4555	XChangeProperty(winPtr->display, wmPtr->wrapperPtr->window,
4556		Tk_InternAtom((Tk_Window) winPtr, "_NET_WM_ICON"),
4557		XA_CARDINAL, 32, PropModeReplace,
4558		(unsigned char *) data, size);
4559    }
4560}
4561
4562/*
4563 *----------------------------------------------------------------------
4564 *
4565 * WaitForConfigureNotify --
4566 *
4567 *	This procedure is invoked in order to synchronize with the
4568 *	window manager.  It waits for a ConfigureNotify event to
4569 *	arrive, signalling that the window manager has seen an attempt
4570 *	on our part to move or resize a top-level window.
4571 *
4572 * Results:
4573 *	None.
4574 *
4575 * Side effects:
4576 *	Delays the execution of the process until a ConfigureNotify event
4577 *	arrives with serial number at least as great as serial.  This
4578 *	is useful for two reasons:
4579 *
4580 *	1. It's important to distinguish ConfigureNotify events that are
4581 *	   coming in response to a request we've made from those generated
4582 *	   spontaneously by the user.  The reason for this is that if the
4583 *	   user resizes the window we take that as an order to ignore
4584 *	   geometry requests coming from inside the window hierarchy.  If
4585 *	   we accidentally interpret a response to our request as a
4586 *	   user-initiated action, the window will stop responding to
4587 *	   new geometry requests.  To make this distinction, (a) this
4588 *	   procedure sets a flag for TopLevelEventProc to indicate that
4589 *	   we're waiting to sync with the wm, and (b) all changes to
4590 *	   the size of a top-level window are followed by calls to this
4591 *	   procedure.
4592 *	2. Races and confusion can come about if there are multiple
4593 *	   operations outstanding at a time (e.g. two different resizes
4594 *	   of the top-level window:  it's hard to tell which of the
4595 *	   ConfigureNotify events coming back is for which request).
4596 *	While waiting, some events covered by StructureNotifyMask are
4597 *	processed (ConfigureNotify, MapNotify, and UnmapNotify)
4598 *	and all others are deferred.
4599 *
4600 *----------------------------------------------------------------------
4601 */
4602
4603static void
4604WaitForConfigureNotify(winPtr, serial)
4605    TkWindow *winPtr;		/* Top-level window for which we want
4606				 * to see a ConfigureNotify. */
4607    unsigned long serial;	/* Serial number of resize request.  Want to
4608				 * be sure wm has seen this. */
4609{
4610    WmInfo *wmPtr = winPtr->wmInfoPtr;
4611    XEvent event;
4612    int diff, code;
4613    int gotConfig = 0;
4614
4615    /*
4616     * One more tricky detail about this procedure.  In some cases the
4617     * window manager will decide to ignore a configure request (e.g.
4618     * because it thinks the window is already in the right place).
4619     * To avoid hanging in this situation, only wait for a few seconds,
4620     * then give up.
4621     */
4622
4623    while (!gotConfig) {
4624	wmPtr->flags |= WM_SYNC_PENDING;
4625	code = WaitForEvent(winPtr->display, wmPtr, ConfigureNotify, &event);
4626	wmPtr->flags &= ~WM_SYNC_PENDING;
4627	if (code != TCL_OK) {
4628	    if (winPtr->dispPtr->flags & TK_DISPLAY_WM_TRACING) {
4629		printf("WaitForConfigureNotify giving up on %s\n",
4630			winPtr->pathName);
4631	    }
4632	    break;
4633	}
4634	diff = event.xconfigure.serial - serial;
4635	if (diff >= 0) {
4636	    gotConfig = 1;
4637	}
4638    }
4639    wmPtr->flags &= ~WM_MOVE_PENDING;
4640    if (winPtr->dispPtr->flags & TK_DISPLAY_WM_TRACING) {
4641	printf("WaitForConfigureNotify finished with %s, serial %ld\n",
4642		winPtr->pathName, serial);
4643    }
4644}
4645
4646/*
4647 *----------------------------------------------------------------------
4648 *
4649 * WaitForEvent --
4650 *
4651 *	This procedure is used by WaitForConfigureNotify and
4652 *	WaitForMapNotify to wait for an event of a certain type
4653 *	to arrive.
4654 *
4655 * Results:
4656 *	Under normal conditions, TCL_OK is returned and an event for
4657 *	display and window that matches "mask" is stored in *eventPtr.
4658 *	This event  has already been processed by Tk before this procedure
4659 *	returns.  If a long time goes by with no event of the right type
4660 *	arriving, or if an error occurs while waiting for the event to
4661 *	arrive, then TCL_ERROR is returned.
4662 *
4663 * Side effects:
4664 *	While waiting for the desired event to occur, Configurenotify,
4665 *	MapNotify, and UnmapNotify events for window are processed,
4666 *	as are all ReparentNotify events.
4667 *
4668 *----------------------------------------------------------------------
4669 */
4670
4671static int
4672WaitForEvent(display, wmInfoPtr, type, eventPtr)
4673    Display *display;		/* Display event is coming from. */
4674    WmInfo *wmInfoPtr;		/* Window for which event is desired. */
4675    int type;			/* Type of event that is wanted. */
4676    XEvent *eventPtr;		/* Place to store event. */
4677{
4678    WaitRestrictInfo info;
4679    Tk_RestrictProc *oldRestrictProc;
4680    ClientData oldRestrictData;
4681    Tcl_Time timeout;
4682
4683    /*
4684     * Set up an event filter to select just the events we want, and
4685     * a timer handler, then wait for events until we get the event
4686     * we want or a timeout happens.
4687     */
4688
4689    info.display = display;
4690    info.wmInfoPtr = wmInfoPtr;
4691    info.type = type;
4692    info.eventPtr = eventPtr;
4693    info.foundEvent = 0;
4694    oldRestrictProc = Tk_RestrictEvents(WaitRestrictProc, (ClientData) &info,
4695	    &oldRestrictData);
4696
4697    Tcl_GetTime(&timeout);
4698    timeout.sec += 2;
4699
4700    while (!info.foundEvent) {
4701	if (!TkUnixDoOneXEvent(&timeout)) {
4702	    break;
4703	}
4704    }
4705    (void) Tk_RestrictEvents(oldRestrictProc, oldRestrictData,
4706	    &oldRestrictData);
4707    if (info.foundEvent) {
4708	return TCL_OK;
4709    }
4710    return TCL_ERROR;
4711}
4712
4713/*
4714 *----------------------------------------------------------------------
4715 *
4716 * WaitRestrictProc --
4717 *
4718 *	This procedure is a Tk_RestrictProc that is used to filter
4719 *	events while WaitForEvent is active.
4720 *
4721 * Results:
4722 *	Returns TK_PROCESS_EVENT if the right event is found.  Also
4723 *	returns TK_PROCESS_EVENT if any ReparentNotify event is found
4724 *	or if the event is a ConfigureNotify, MapNotify, or UnmapNotify
4725 *	for window.  Otherwise returns TK_DEFER_EVENT.
4726 *
4727 * Side effects:
4728 *	An event may get stored in the area indicated by the caller
4729 *	of WaitForEvent.
4730 *
4731 *----------------------------------------------------------------------
4732 */
4733
4734static Tk_RestrictAction
4735WaitRestrictProc(clientData, eventPtr)
4736    ClientData clientData;	/* Pointer to WaitRestrictInfo structure. */
4737    XEvent *eventPtr;		/* Event that is about to be handled. */
4738{
4739    WaitRestrictInfo *infoPtr = (WaitRestrictInfo *) clientData;
4740
4741    if (eventPtr->type == ReparentNotify) {
4742	return TK_PROCESS_EVENT;
4743    }
4744    if (((eventPtr->xany.window != infoPtr->wmInfoPtr->wrapperPtr->window)
4745	    && (eventPtr->xany.window != infoPtr->wmInfoPtr->reparent))
4746	    || (eventPtr->xany.display != infoPtr->display)) {
4747	return TK_DEFER_EVENT;
4748    }
4749    if (eventPtr->type == infoPtr->type) {
4750	*infoPtr->eventPtr = *eventPtr;
4751	infoPtr->foundEvent = 1;
4752	return TK_PROCESS_EVENT;
4753    }
4754    if (eventPtr->type == ConfigureNotify
4755	   || eventPtr->type == MapNotify
4756	   || eventPtr->type == UnmapNotify) {
4757	return TK_PROCESS_EVENT;
4758    }
4759    return TK_DEFER_EVENT;
4760}
4761
4762/*
4763 *----------------------------------------------------------------------
4764 *
4765 * WaitForMapNotify --
4766 *
4767 *	This procedure is invoked in order to synchronize with the
4768 *	window manager.  It waits for the window's mapped state to
4769 *	reach the value given by mapped.
4770 *
4771 * Results:
4772 *	None.
4773 *
4774 * Side effects:
4775 *	Delays the execution of the process until winPtr becomes mapped
4776 *	or unmapped, depending on the "mapped" argument.  This allows us
4777 *	to synchronize with the window manager, and allows us to
4778 *	identify changes in window size that come about when the window
4779 *	manager first starts managing the window (as opposed to those
4780 *	requested interactively by the user later).  See the comments
4781 *	for WaitForConfigureNotify and WM_SYNC_PENDING.  While waiting,
4782 *	some events covered by StructureNotifyMask are processed and all
4783 *	others are deferred.
4784 *
4785 *----------------------------------------------------------------------
4786 */
4787
4788static void
4789WaitForMapNotify(winPtr, mapped)
4790    TkWindow *winPtr;		/* Top-level window for which we want
4791				 * to see a particular mapping state. */
4792    int mapped;			/* If non-zero, wait for window to become
4793				 * mapped, otherwise wait for it to become
4794				 * unmapped. */
4795{
4796    WmInfo *wmPtr = winPtr->wmInfoPtr;
4797    XEvent event;
4798    int code;
4799
4800    while (1) {
4801	if (mapped) {
4802	    if (winPtr->flags & TK_MAPPED) {
4803		break;
4804	    }
4805	} else if (!(winPtr->flags & TK_MAPPED)) {
4806	    break;
4807	}
4808	wmPtr->flags |= WM_SYNC_PENDING;
4809	code = WaitForEvent(winPtr->display, wmPtr,
4810		mapped ? MapNotify : UnmapNotify, &event);
4811	wmPtr->flags &= ~WM_SYNC_PENDING;
4812	if (code != TCL_OK) {
4813	    /*
4814	     * There are some bizarre situations in which the window
4815	     * manager can't respond or chooses not to (e.g. if we've
4816	     * got a grab set it can't respond).  If this happens then
4817	     * just quit.
4818	     */
4819
4820	    if (winPtr->dispPtr->flags & TK_DISPLAY_WM_TRACING) {
4821		printf("WaitForMapNotify giving up on %s\n", winPtr->pathName);
4822	    }
4823	    break;
4824	}
4825    }
4826    wmPtr->flags &= ~WM_MOVE_PENDING;
4827    if (winPtr->dispPtr->flags & TK_DISPLAY_WM_TRACING) {
4828	printf("WaitForMapNotify finished with %s (winPtr %p, wmPtr %p)\n",
4829		winPtr->pathName, winPtr, wmPtr);
4830    }
4831}
4832
4833/*
4834 *--------------------------------------------------------------
4835 *
4836 * UpdateHints --
4837 *
4838 *	This procedure is called to update the window manager's
4839 *	hints information from the information in a WmInfo
4840 *	structure.
4841 *
4842 * Results:
4843 *	None.
4844 *
4845 * Side effects:
4846 *	Properties get changed for winPtr.
4847 *
4848 *--------------------------------------------------------------
4849 */
4850
4851static void
4852UpdateHints(winPtr)
4853    TkWindow *winPtr;
4854{
4855    WmInfo *wmPtr = winPtr->wmInfoPtr;
4856
4857    if (wmPtr->flags & WM_NEVER_MAPPED) {
4858	return;
4859    }
4860    XSetWMHints(winPtr->display, wmPtr->wrapperPtr->window, &wmPtr->hints);
4861}
4862
4863/*
4864 *----------------------------------------------------------------------
4865 *
4866 * SetNetWmType --
4867 *
4868 *	Set the extended window manager hints for a toplevel window
4869 *	to the types provided. The specification states that this
4870 *	may be a list of window types in preferred order. To permit
4871 *	for future type definitions, the set of names is unconstrained
4872 *	and names are converted to upper-case and appended to
4873 *	"_NET_WM_WINDOW_TYPE_" before being converted to an Atom.
4874 *
4875 *----------------------------------------------------------------------
4876 */
4877
4878static int
4879SetNetWmType(winPtr, typePtr)
4880    TkWindow *winPtr;
4881    Tcl_Obj *typePtr;
4882{
4883    Atom typeAtom, *atoms = NULL;
4884    WmInfo *wmPtr;
4885    TkWindow *wrapperPtr;
4886    Tcl_Obj **objv;
4887    int objc, n;
4888    Tk_Window tkwin = (Tk_Window)winPtr;
4889    Tcl_Interp *interp = winPtr->mainPtr->interp;
4890
4891    if (TCL_OK != Tcl_ListObjGetElements(interp, typePtr, &objc, &objv)) {
4892	return TCL_ERROR;
4893    }
4894
4895    if (!Tk_HasWrapper(tkwin)) {
4896	return TCL_OK; /* error?? */
4897    }
4898
4899    if (objc > 0) {
4900	atoms = (Atom *)ckalloc(sizeof(Atom) * objc);
4901    }
4902
4903    for (n = 0; n < objc; ++n) {
4904	Tcl_DString ds, dsName;
4905	int len;
4906	char *name = Tcl_GetStringFromObj(objv[n], &len);
4907	Tcl_UtfToUpper(name);
4908	Tcl_UtfToExternalDString(NULL, name, len, &dsName);
4909	Tcl_DStringInit(&ds);
4910	Tcl_DStringAppend(&ds, "_NET_WM_WINDOW_TYPE_", 20);
4911	Tcl_DStringAppend(&ds, Tcl_DStringValue(&dsName),
4912		Tcl_DStringLength(&dsName));
4913	Tcl_DStringFree(&dsName);
4914	atoms[n] = Tk_InternAtom(tkwin, Tcl_DStringValue(&ds));
4915	Tcl_DStringFree(&ds);
4916    }
4917
4918    wmPtr = winPtr->wmInfoPtr;
4919    if (wmPtr->wrapperPtr == NULL) {
4920	CreateWrapper(wmPtr);
4921    }
4922    wrapperPtr = wmPtr->wrapperPtr;
4923
4924    typeAtom = Tk_InternAtom(tkwin, "_NET_WM_WINDOW_TYPE");
4925    XChangeProperty(Tk_Display(tkwin), wrapperPtr->window, typeAtom,
4926	XA_ATOM, 32, PropModeReplace, (unsigned char *) atoms, objc);
4927
4928    ckfree((char *)atoms);
4929    return TCL_OK;
4930}
4931
4932/*
4933 *----------------------------------------------------------------------
4934 *
4935 * GetNetWmType --
4936 *
4937 *	Read the extended window manager type hint from a window
4938 *	and return as a list of names suitable for use with
4939 *	SetNetWmType.
4940 *
4941 *----------------------------------------------------------------------
4942 */
4943
4944static Tcl_Obj *
4945GetNetWmType(winPtr)
4946    TkWindow *winPtr;
4947{
4948    Atom typeAtom, actualType, *atoms;
4949    int actualFormat;
4950    unsigned long n, count, bytesAfter;
4951    unsigned char *propertyValue = NULL;
4952    long maxLength = 1024;
4953    Tk_Window tkwin = (Tk_Window)winPtr;
4954    TkWindow *wrapperPtr;
4955    Tcl_Obj *typePtr;
4956    Tcl_Interp *interp;
4957    Tcl_DString ds;
4958
4959    interp = winPtr->mainPtr->interp;
4960    typePtr = Tcl_NewListObj(0, NULL);
4961
4962    if (winPtr->wmInfoPtr->wrapperPtr == NULL) {
4963	CreateWrapper(winPtr->wmInfoPtr);
4964    }
4965    wrapperPtr = winPtr->wmInfoPtr->wrapperPtr;
4966
4967    typeAtom = Tk_InternAtom(tkwin, "_NET_WM_WINDOW_TYPE");
4968    if (Success == XGetWindowProperty(wrapperPtr->display,
4969	    wrapperPtr->window, typeAtom, 0L, maxLength, False,
4970	    XA_ATOM, &actualType, &actualFormat, &count,
4971	    &bytesAfter, &propertyValue)) {
4972	atoms = (Atom *)propertyValue;
4973	for (n = 0; n < count; ++n) {
4974	    const char *name = Tk_GetAtomName(tkwin, atoms[n]);
4975	    if (strncmp("_NET_WM_WINDOW_TYPE_", name, 20) == 0) {
4976		Tcl_ExternalToUtfDString(NULL, name+20, -1, &ds);
4977		Tcl_UtfToLower(Tcl_DStringValue(&ds));
4978		Tcl_ListObjAppendElement(interp, typePtr,
4979			Tcl_NewStringObj(Tcl_DStringValue(&ds),
4980				Tcl_DStringLength(&ds)));
4981		Tcl_DStringFree(&ds);
4982	    }
4983	}
4984	XFree(propertyValue);
4985    }
4986
4987    return typePtr;
4988}
4989
4990/*
4991 *--------------------------------------------------------------
4992 *
4993 * ParseGeometry --
4994 *
4995 *	This procedure parses a geometry string and updates
4996 *	information used to control the geometry of a top-level
4997 *	window.
4998 *
4999 * Results:
5000 *	A standard Tcl return value, plus an error message in
5001 *	the interp's result if an error occurs.
5002 *
5003 * Side effects:
5004 *	The size and/or location of winPtr may change.
5005 *
5006 *--------------------------------------------------------------
5007 */
5008
5009static int
5010ParseGeometry(interp, string, winPtr)
5011    Tcl_Interp *interp;		/* Used for error reporting. */
5012    char *string;		/* String containing new geometry.  Has the
5013				 * standard form "=wxh+x+y". */
5014    TkWindow *winPtr;		/* Pointer to top-level window whose
5015				 * geometry is to be changed. */
5016{
5017    register WmInfo *wmPtr = winPtr->wmInfoPtr;
5018    int x, y, width, height, flags;
5019    char *end;
5020    register char *p = string;
5021
5022    /*
5023     * The leading "=" is optional.
5024     */
5025
5026    if (*p == '=') {
5027	p++;
5028    }
5029
5030    /*
5031     * Parse the width and height, if they are present.  Don't
5032     * actually update any of the fields of wmPtr until we've
5033     * successfully parsed the entire geometry string.
5034     */
5035
5036    width = wmPtr->width;
5037    height = wmPtr->height;
5038    x = wmPtr->x;
5039    y = wmPtr->y;
5040    flags = wmPtr->flags;
5041    if (isdigit(UCHAR(*p))) {
5042	width = strtoul(p, &end, 10);
5043	p = end;
5044	if (*p != 'x') {
5045	    goto error;
5046	}
5047	p++;
5048	if (!isdigit(UCHAR(*p))) {
5049	    goto error;
5050	}
5051	height = strtoul(p, &end, 10);
5052	p = end;
5053    }
5054
5055    /*
5056     * Parse the X and Y coordinates, if they are present.
5057     */
5058
5059    if (*p != '\0') {
5060	flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y);
5061	if (*p == '-') {
5062	    flags |= WM_NEGATIVE_X;
5063	} else if (*p != '+') {
5064	    goto error;
5065	}
5066	p++;
5067	if (!isdigit(UCHAR(*p)) && (*p != '-')) {
5068	    goto error;
5069	}
5070	x = strtol(p, &end, 10);
5071	p = end;
5072	if (*p == '-') {
5073	    flags |= WM_NEGATIVE_Y;
5074	} else if (*p != '+') {
5075	    goto error;
5076	}
5077	p++;
5078	if (!isdigit(UCHAR(*p)) && (*p != '-')) {
5079	    goto error;
5080	}
5081	y = strtol(p, &end, 10);
5082	if (*end != '\0') {
5083	    goto error;
5084	}
5085
5086	/*
5087	 * Assume that the geometry information came from the user,
5088	 * unless an explicit source has been specified.  Otherwise
5089	 * most window managers assume that the size hints were
5090	 * program-specified and they ignore them.
5091	 */
5092
5093	if ((wmPtr->sizeHintsFlags & (USPosition|PPosition)) == 0) {
5094	    wmPtr->sizeHintsFlags |= USPosition;
5095	    flags |= WM_UPDATE_SIZE_HINTS;
5096	}
5097    }
5098
5099    /*
5100     * Everything was parsed OK.  Update the fields of *wmPtr and
5101     * arrange for the appropriate information to be percolated out
5102     * to the window manager at the next idle moment.
5103     */
5104
5105    wmPtr->width = width;
5106    wmPtr->height = height;
5107    wmPtr->x = x;
5108    wmPtr->y = y;
5109    flags |= WM_MOVE_PENDING;
5110    wmPtr->flags = flags;
5111
5112    if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
5113	Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
5114	wmPtr->flags |= WM_UPDATE_PENDING;
5115    }
5116    return TCL_OK;
5117
5118    error:
5119    Tcl_AppendResult(interp, "bad geometry specifier \"",
5120	    string, "\"", (char *) NULL);
5121    return TCL_ERROR;
5122}
5123
5124/*
5125 *----------------------------------------------------------------------
5126 *
5127 * Tk_GetRootCoords --
5128 *
5129 *	Given a token for a window, this procedure traces through the
5130 *	window's lineage to find the (virtual) root-window coordinates
5131 *	corresponding to point (0,0) in the window.
5132 *
5133 * Results:
5134 *	The locations pointed to by xPtr and yPtr are filled in with
5135 *	the root coordinates of the (0,0) point in tkwin.  If a virtual
5136 *	root window is in effect for the window, then the coordinates
5137 *	in the virtual root are returned.
5138 *
5139 * Side effects:
5140 *	None.
5141 *
5142 *----------------------------------------------------------------------
5143 */
5144
5145void
5146Tk_GetRootCoords(tkwin, xPtr, yPtr)
5147    Tk_Window tkwin;		/* Token for window. */
5148    int *xPtr;			/* Where to store x-displacement of (0,0). */
5149    int *yPtr;			/* Where to store y-displacement of (0,0). */
5150{
5151    int x, y;
5152    register TkWindow *winPtr = (TkWindow *) tkwin;
5153
5154    /*
5155     * Search back through this window's parents all the way to a
5156     * top-level window, combining the offsets of each window within
5157     * its parent.
5158     */
5159
5160    x = y = 0;
5161    while (1) {
5162	x += winPtr->changes.x + winPtr->changes.border_width;
5163	y += winPtr->changes.y + winPtr->changes.border_width;
5164	if ((winPtr->wmInfoPtr != NULL)
5165		&& (winPtr->wmInfoPtr->menubar == (Tk_Window) winPtr)) {
5166	    /*
5167	     * This window is a special menubar; switch over to its
5168	     * associated toplevel, compensate for their differences in
5169	     * y coordinates, then continue with the toplevel (in case
5170	     * it's embedded).
5171	     */
5172
5173	    y -= winPtr->wmInfoPtr->menuHeight;
5174	    winPtr = winPtr->wmInfoPtr->winPtr;
5175	    continue;
5176	}
5177	if (winPtr->flags & TK_TOP_LEVEL) {
5178	    TkWindow *otherPtr;
5179
5180	    if (!(winPtr->flags & TK_EMBEDDED)) {
5181		break;
5182	    }
5183	    otherPtr = TkpGetOtherWindow(winPtr);
5184	    if (otherPtr == NULL) {
5185		/*
5186		 * The container window is not in the same application.
5187		 * Query the X server.
5188		 */
5189
5190		Window root, dummyChild;
5191		int rootX, rootY;
5192
5193		root = winPtr->wmInfoPtr->vRoot;
5194		if (root == None) {
5195		    root = RootWindowOfScreen(Tk_Screen((Tk_Window)winPtr));
5196		}
5197		XTranslateCoordinates(winPtr->display, winPtr->window,
5198		    root, 0, 0, &rootX, &rootY, &dummyChild);
5199		x += rootX;
5200		y += rootY;
5201		break;
5202	    } else {
5203		/*
5204		 * The container window is in the same application.
5205		 * Let's query its coordinates.
5206		 */
5207
5208		winPtr = otherPtr;
5209		continue;
5210	    }
5211	}
5212	winPtr = winPtr->parentPtr;
5213	if (winPtr == NULL) {
5214	    break;
5215	}
5216    }
5217    *xPtr = x;
5218    *yPtr = y;
5219}
5220
5221/*
5222 *----------------------------------------------------------------------
5223 *
5224 * Tk_CoordsToWindow --
5225 *
5226 *	Given the (virtual) root coordinates of a point, this procedure
5227 *	returns the token for the top-most window covering that point,
5228 *	if there exists such a window in this application.
5229 *
5230 * Results:
5231 *	The return result is either a token for the window corresponding
5232 *	to rootX and rootY, or else NULL to indicate that there is no such
5233 *	window.
5234 *
5235 * Side effects:
5236 *	None.
5237 *
5238 *----------------------------------------------------------------------
5239 */
5240
5241Tk_Window
5242Tk_CoordsToWindow(rootX, rootY, tkwin)
5243    int rootX, rootY;		/* Coordinates of point in root window.  If
5244				 * a virtual-root window manager is in use,
5245				 * these coordinates refer to the virtual
5246				 * root, not the real root. */
5247    Tk_Window tkwin;		/* Token for any window in application;
5248				 * used to identify the display. */
5249{
5250    Window window, parent, child;
5251    int x, y, childX, childY, tmpx, tmpy, bd;
5252    WmInfo *wmPtr;
5253    TkWindow *winPtr, *childPtr, *nextPtr;
5254    TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
5255    Tk_ErrorHandler handler = NULL;
5256
5257    /*
5258     * Step 1: scan the list of toplevel windows to see if there is a
5259     * virtual root for the screen we're interested in.  If so, we have
5260     * to translate the coordinates from virtual root to root
5261     * coordinates.
5262     */
5263
5264    parent = window = RootWindowOfScreen(Tk_Screen(tkwin));
5265    x = rootX;
5266    y = rootY;
5267    for (wmPtr = (WmInfo *) dispPtr->firstWmPtr; wmPtr != NULL; wmPtr = wmPtr->nextPtr) {
5268	if (Tk_Screen(wmPtr->winPtr) != Tk_Screen(tkwin)) {
5269	    continue;
5270	}
5271	if (wmPtr->vRoot == None) {
5272	    continue;
5273	}
5274	UpdateVRootGeometry(wmPtr);
5275	parent = wmPtr->vRoot;
5276	break;
5277    }
5278
5279    /*
5280     * Step 2: work down through the window hierarchy starting at the
5281     * root. For each window, find the child that contains the given
5282     * point and then see if this child is either a wrapper for one of
5283     * our toplevel windows or a window manager decoration window for
5284     * one of our toplevels.  This approach handles several tricky
5285     * cases:
5286     *
5287     * 1. There may be a virtual root window between the root and one of
5288     *    our toplevels.
5289     * 2. If a toplevel is embedded, we may have to search through the
5290     *    windows of the container application(s) before getting to
5291     *    the toplevel.
5292     */
5293
5294    handler = Tk_CreateErrorHandler(Tk_Display(tkwin), -1, -1, -1,
5295	    (Tk_ErrorProc *) NULL, (ClientData) NULL);
5296    while (1) {
5297	if (XTranslateCoordinates(Tk_Display(tkwin), parent, window,
5298		x, y, &childX, &childY, &child) == False) {
5299	    /*
5300	     * We can end up here when the window is in the middle of
5301	     * being deleted
5302	     */
5303	    Tk_DeleteErrorHandler(handler);
5304	    return NULL;
5305	}
5306	if (child == None) {
5307	    Tk_DeleteErrorHandler(handler);
5308	    return NULL;
5309	}
5310	for (wmPtr = (WmInfo *) dispPtr->firstWmPtr; wmPtr != NULL;
5311                wmPtr = wmPtr->nextPtr) {
5312	    if (wmPtr->reparent == child) {
5313		goto gotToplevel;
5314	    }
5315	    if (wmPtr->wrapperPtr != NULL) {
5316		if (child == wmPtr->wrapperPtr->window) {
5317		    goto gotToplevel;
5318		}
5319	    } else if (child == wmPtr->winPtr->window) {
5320		goto gotToplevel;
5321	    }
5322	}
5323	x = childX;
5324	y = childY;
5325	parent = window;
5326	window = child;
5327    }
5328
5329    gotToplevel:
5330    if (handler) {
5331	/*
5332	 * Check value of handler, because we can reach this label
5333	 * from above or below
5334	 */
5335	Tk_DeleteErrorHandler(handler);
5336	handler = NULL;
5337    }
5338    winPtr = wmPtr->winPtr;
5339    if (winPtr->mainPtr != ((TkWindow *) tkwin)->mainPtr) {
5340	return NULL;
5341    }
5342
5343    /*
5344     * Step 3: at this point winPtr and wmPtr refer to the toplevel that
5345     * contains the given coordinates, and childX and childY give the
5346     * translated coordinates in the *parent* of the toplevel.  Now
5347     * decide whether the coordinates are in the menubar or the actual
5348     * toplevel, and translate the coordinates into the coordinate
5349     * system of that window.
5350     */
5351
5352    x = childX - winPtr->changes.x;
5353    y = childY - winPtr->changes.y;
5354    if ((x < 0) || (x >= winPtr->changes.width)
5355	    || (y >= winPtr->changes.height)) {
5356	return NULL;
5357    }
5358    if (y < 0) {
5359	winPtr = (TkWindow *) wmPtr->menubar;
5360	if (winPtr == NULL) {
5361	    return NULL;
5362	}
5363	y += wmPtr->menuHeight;
5364	if (y < 0) {
5365	    return NULL;
5366	}
5367    }
5368
5369    /*
5370     * Step 4: work down through the hierarchy underneath the current
5371     * window. At each level, scan through all the children to find the
5372     * highest one in the stacking order that contains the point.  Then
5373     * repeat the whole process on that child.
5374     */
5375
5376    while (1) {
5377	nextPtr = NULL;
5378	for (childPtr = winPtr->childList; childPtr != NULL;
5379		childPtr = childPtr->nextPtr) {
5380	    if (!Tk_IsMapped(childPtr) || (childPtr->flags & TK_TOP_HIERARCHY)) {
5381		continue;
5382	    }
5383	    if (childPtr->flags & TK_REPARENTED) {
5384		continue;
5385	    }
5386	    tmpx = x - childPtr->changes.x;
5387	    tmpy = y - childPtr->changes.y;
5388	    bd = childPtr->changes.border_width;
5389	    if ((tmpx >= -bd) && (tmpy >= -bd)
5390		    && (tmpx < (childPtr->changes.width + bd))
5391		    && (tmpy < (childPtr->changes.height + bd))) {
5392		nextPtr = childPtr;
5393	    }
5394	}
5395	if (nextPtr == NULL) {
5396	    break;
5397	}
5398	winPtr = nextPtr;
5399	x -= winPtr->changes.x;
5400	y -= winPtr->changes.y;
5401	if ((winPtr->flags & TK_CONTAINER)
5402		&& (winPtr->flags & TK_BOTH_HALVES)) {
5403	    /*
5404	     * The window containing the point is a container, and the
5405	     * embedded application is in this same process.  Switch
5406	     * over to the toplevel for the embedded application and
5407	     * start processing that toplevel from scratch.
5408	     */
5409
5410	    winPtr = TkpGetOtherWindow(winPtr);
5411	    if (winPtr == NULL) {
5412		return NULL;
5413	    }
5414	    wmPtr = winPtr->wmInfoPtr;
5415	    childX = x;
5416	    childY = y;
5417	    goto gotToplevel;
5418	}
5419    }
5420    return (Tk_Window) winPtr;
5421}
5422
5423/*
5424 *----------------------------------------------------------------------
5425 *
5426 * UpdateVRootGeometry --
5427 *
5428 *	This procedure is called to update all the virtual root
5429 *	geometry information in wmPtr.
5430 *
5431 * Results:
5432 *	None.
5433 *
5434 * Side effects:
5435 *	The vRootX, vRootY, vRootWidth, and vRootHeight fields in
5436 *	wmPtr are filled with the most up-to-date information.
5437 *
5438 *----------------------------------------------------------------------
5439 */
5440
5441static void
5442UpdateVRootGeometry(wmPtr)
5443    WmInfo *wmPtr;		/* Window manager information to be
5444				 * updated.  The wmPtr->vRoot field must
5445				 * be valid. */
5446{
5447    TkWindow *winPtr = wmPtr->winPtr;
5448    int bd;
5449    unsigned int dummy;
5450    Window dummy2;
5451    Status status;
5452    Tk_ErrorHandler handler;
5453
5454    /*
5455     * If this isn't a virtual-root window manager, just return information
5456     * about the screen.
5457     */
5458
5459    wmPtr->flags &= ~WM_VROOT_OFFSET_STALE;
5460    if (wmPtr->vRoot == None) {
5461	noVRoot:
5462	wmPtr->vRootX = wmPtr->vRootY = 0;
5463	wmPtr->vRootWidth = DisplayWidth(winPtr->display, winPtr->screenNum);
5464	wmPtr->vRootHeight = DisplayHeight(winPtr->display, winPtr->screenNum);
5465	return;
5466    }
5467
5468    /*
5469     * Refresh the virtual root information if it's out of date.
5470     */
5471
5472    handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
5473	    (Tk_ErrorProc *) NULL, (ClientData) NULL);
5474    status = XGetGeometry(winPtr->display, wmPtr->vRoot,
5475	    &dummy2, &wmPtr->vRootX, &wmPtr->vRootY,
5476	    (unsigned int *) &wmPtr->vRootWidth,
5477	    (unsigned int *) &wmPtr->vRootHeight, (unsigned int *) &bd,
5478	    &dummy);
5479    if (winPtr->dispPtr->flags & TK_DISPLAY_WM_TRACING) {
5480	printf("UpdateVRootGeometry: x = %d, y = %d, width = %d, ",
5481		wmPtr->vRootX, wmPtr->vRootY, wmPtr->vRootWidth);
5482	printf("height = %d, status = %d\n", wmPtr->vRootHeight, status);
5483    }
5484    Tk_DeleteErrorHandler(handler);
5485    if (status == 0) {
5486	/*
5487	 * The virtual root is gone!  Pretend that it never existed.
5488	 */
5489
5490	wmPtr->vRoot = None;
5491	goto noVRoot;
5492    }
5493}
5494
5495/*
5496 *----------------------------------------------------------------------
5497 *
5498 * Tk_GetVRootGeometry --
5499 *
5500 *	This procedure returns information about the virtual root
5501 *	window corresponding to a particular Tk window.
5502 *
5503 * Results:
5504 *	The values at xPtr, yPtr, widthPtr, and heightPtr are set
5505 *	with the offset and dimensions of the root window corresponding
5506 *	to tkwin.  If tkwin is being managed by a virtual root window
5507 *	manager these values correspond to the virtual root window being
5508 *	used for tkwin;  otherwise the offsets will be 0 and the
5509 *	dimensions will be those of the screen.
5510 *
5511 * Side effects:
5512 *	Vroot window information is refreshed if it is out of date.
5513 *
5514 *----------------------------------------------------------------------
5515 */
5516
5517void
5518Tk_GetVRootGeometry(tkwin, xPtr, yPtr, widthPtr, heightPtr)
5519    Tk_Window tkwin;		/* Window whose virtual root is to be
5520				 * queried. */
5521    int *xPtr, *yPtr;		/* Store x and y offsets of virtual root
5522				 * here. */
5523    int *widthPtr, *heightPtr;	/* Store dimensions of virtual root here. */
5524{
5525    WmInfo *wmPtr;
5526    TkWindow *winPtr = (TkWindow *) tkwin;
5527
5528    /*
5529     * Find the top-level window for tkwin, and locate the window manager
5530     * information for that window.
5531     */
5532
5533    while (!(winPtr->flags & TK_TOP_HIERARCHY) && (winPtr->parentPtr != NULL)) {
5534	winPtr = winPtr->parentPtr;
5535    }
5536    wmPtr = winPtr->wmInfoPtr;
5537    if (wmPtr == NULL) {
5538	/* Punt. */
5539	*xPtr = 0;
5540	*yPtr = 0;
5541	*widthPtr = 0;
5542	*heightPtr = 0;
5543    }
5544
5545
5546    /*
5547     * Make sure that the geometry information is up-to-date, then copy
5548     * it out to the caller.
5549     */
5550
5551    if (wmPtr->flags & WM_VROOT_OFFSET_STALE) {
5552	UpdateVRootGeometry(wmPtr);
5553    }
5554    *xPtr = wmPtr->vRootX;
5555    *yPtr = wmPtr->vRootY;
5556    *widthPtr = wmPtr->vRootWidth;
5557    *heightPtr = wmPtr->vRootHeight;
5558}
5559
5560/*
5561 *----------------------------------------------------------------------
5562 *
5563 * Tk_MoveToplevelWindow --
5564 *
5565 *	This procedure is called instead of Tk_MoveWindow to adjust
5566 *	the x-y location of a top-level window.  It delays the actual
5567 *	move to a later time and keeps window-manager information
5568 *	up-to-date with the move
5569 *
5570 * Results:
5571 *	None.
5572 *
5573 * Side effects:
5574 *	The window is eventually moved so that its upper-left corner
5575 *	(actually, the upper-left corner of the window's decorative
5576 *	frame, if there is one) is at (x,y).
5577 *
5578 *----------------------------------------------------------------------
5579 */
5580
5581void
5582Tk_MoveToplevelWindow(tkwin, x, y)
5583    Tk_Window tkwin;		/* Window to move. */
5584    int x, y;			/* New location for window (within
5585				 * parent). */
5586{
5587    TkWindow *winPtr = (TkWindow *) tkwin;
5588    register WmInfo *wmPtr = winPtr->wmInfoPtr;
5589
5590    if (!(winPtr->flags & TK_TOP_LEVEL)) {
5591	Tcl_Panic("Tk_MoveToplevelWindow called with non-toplevel window");
5592    }
5593    wmPtr->x = x;
5594    wmPtr->y = y;
5595    wmPtr->flags |= WM_MOVE_PENDING;
5596    wmPtr->flags &= ~(WM_NEGATIVE_X|WM_NEGATIVE_Y);
5597    if ((wmPtr->sizeHintsFlags & (USPosition|PPosition)) == 0) {
5598	wmPtr->sizeHintsFlags |= USPosition;
5599	wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
5600    }
5601
5602    /*
5603     * If the window has already been mapped, must bring its geometry
5604     * up-to-date immediately, otherwise an event might arrive from the
5605     * server that would overwrite wmPtr->x and wmPtr->y and lose the
5606     * new position.
5607     */
5608
5609    if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
5610	if (wmPtr->flags & WM_UPDATE_PENDING) {
5611	    Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
5612	}
5613	UpdateGeometryInfo((ClientData) winPtr);
5614    }
5615}
5616
5617/*
5618 *----------------------------------------------------------------------
5619 *
5620 * UpdateWmProtocols --
5621 *
5622 *	This procedure transfers the most up-to-date information about
5623 *	window manager protocols from the WmInfo structure to the actual
5624 *	property on the top-level window.
5625 *
5626 * Results:
5627 *	None.
5628 *
5629 * Side effects:
5630 *	The WM_PROTOCOLS property gets changed for wmPtr's window.
5631 *
5632 *----------------------------------------------------------------------
5633 */
5634
5635static void
5636UpdateWmProtocols(wmPtr)
5637    register WmInfo *wmPtr;	/* Information about top-level window. */
5638{
5639    register ProtocolHandler *protPtr;
5640    Atom deleteWindowAtom;
5641    int count;
5642    Atom *arrayPtr, *atomPtr;
5643
5644    /*
5645     * There are only two tricky parts here.  First, there could be any
5646     * number of atoms for the window, so count them and malloc an array
5647     * to hold all of their atoms.  Second, we *always* want to respond
5648     * to the WM_DELETE_WINDOW protocol, even if no-one's officially asked.
5649     */
5650
5651    for (protPtr = wmPtr->protPtr, count = 1; protPtr != NULL;
5652	    protPtr = protPtr->nextPtr, count++) {
5653	/* Empty loop body;  we're just counting the handlers. */
5654    }
5655    arrayPtr = (Atom *) ckalloc((unsigned) (count * sizeof(Atom)));
5656    deleteWindowAtom = Tk_InternAtom((Tk_Window) wmPtr->winPtr,
5657	    "WM_DELETE_WINDOW");
5658    arrayPtr[0] = deleteWindowAtom;
5659    for (protPtr = wmPtr->protPtr, atomPtr = &arrayPtr[1];
5660	    protPtr != NULL; protPtr = protPtr->nextPtr) {
5661	if (protPtr->protocol != deleteWindowAtom) {
5662	    *atomPtr = protPtr->protocol;
5663	    atomPtr++;
5664	}
5665    }
5666    XChangeProperty(wmPtr->winPtr->display, wmPtr->wrapperPtr->window,
5667	    Tk_InternAtom((Tk_Window) wmPtr->winPtr, "WM_PROTOCOLS"),
5668	    XA_ATOM, 32, PropModeReplace, (unsigned char *) arrayPtr,
5669	    atomPtr-arrayPtr);
5670    ckfree((char *) arrayPtr);
5671}
5672
5673/*
5674 *----------------------------------------------------------------------
5675 *
5676 * TkWmProtocolEventProc --
5677 *
5678 *	This procedure is called by the Tk_HandleEvent whenever a
5679 *	ClientMessage event arrives whose type is "WM_PROTOCOLS".
5680 *	This procedure handles the message from the window manager
5681 *	in an appropriate fashion.
5682 *
5683 * Results:
5684 *	None.
5685 *
5686 * Side effects:
5687 *	Depends on what sort of handler, if any, was set up for the
5688 *	protocol.
5689 *
5690 *----------------------------------------------------------------------
5691 */
5692
5693void
5694TkWmProtocolEventProc(winPtr, eventPtr)
5695    TkWindow *winPtr;		/* Window to which the event was sent. */
5696    XEvent *eventPtr;		/* X event. */
5697{
5698    WmInfo *wmPtr;
5699    register ProtocolHandler *protPtr;
5700    Atom protocol;
5701    int result;
5702    CONST char *protocolName;
5703    Tcl_Interp *interp;
5704
5705    wmPtr = winPtr->wmInfoPtr;
5706    if (wmPtr == NULL) {
5707	return;
5708    }
5709    protocol = (Atom) eventPtr->xclient.data.l[0];
5710
5711    /*
5712     * Note: it's very important to retrieve the protocol name now,
5713     * before invoking the command, even though the name won't be used
5714     * until after the command returns.  This is because the command
5715     * could delete winPtr, making it impossible for us to use it
5716     * later in the call to Tk_GetAtomName.
5717     */
5718
5719    protocolName = Tk_GetAtomName((Tk_Window) winPtr, protocol);
5720    for (protPtr = wmPtr->protPtr; protPtr != NULL;
5721	    protPtr = protPtr->nextPtr) {
5722	if (protocol == protPtr->protocol) {
5723	    Tcl_Preserve((ClientData) protPtr);
5724            interp = protPtr->interp;
5725            Tcl_Preserve((ClientData) interp);
5726	    result = Tcl_GlobalEval(interp, protPtr->command);
5727	    if (result != TCL_OK) {
5728		Tcl_AddErrorInfo(interp, "\n    (command for \"");
5729		Tcl_AddErrorInfo(interp, protocolName);
5730		Tcl_AddErrorInfo(interp,
5731			"\" window manager protocol)");
5732		Tcl_BackgroundError(interp);
5733	    }
5734            Tcl_Release((ClientData) interp);
5735	    Tcl_Release((ClientData) protPtr);
5736	    return;
5737	}
5738    }
5739
5740    /*
5741     * No handler was present for this protocol.  If this is a
5742     * WM_DELETE_WINDOW message then just destroy the window.
5743     */
5744
5745    if (protocol == Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW")) {
5746	Tk_DestroyWindow((Tk_Window) wmPtr->winPtr);
5747    }
5748}
5749
5750/*
5751 *----------------------------------------------------------------------
5752 *
5753 * TkWmStackorderToplevelWrapperMap --
5754 *
5755 *	This procedure will create a table that maps the reparent wrapper
5756 *	X id for a toplevel to the TkWindow structure that is wraps.
5757 *	Tk keeps track of a mapping from the window X id to the TkWindow
5758 *	structure but that does us no good here since we only get the X
5759 *	id of the wrapper window. Only those toplevel windows that are
5760 *	mapped have a position in the stacking order.
5761 *
5762 * Results:
5763 *	None.
5764 *
5765 * Side effects:
5766 *	Adds entries to the passed hashtable.
5767 *
5768 *----------------------------------------------------------------------
5769 */
5770
5771static void
5772TkWmStackorderToplevelWrapperMap(winPtr, display, table)
5773    TkWindow *winPtr;				/* TkWindow to recurse on */
5774    Display *display;				/* X display of parent window */
5775    Tcl_HashTable *table;			/* Maps X id to TkWindow */
5776{
5777    TkWindow *childPtr;
5778    Tcl_HashEntry *hPtr;
5779    Window wrapper;
5780    int newEntry;
5781
5782    if (Tk_IsMapped(winPtr) && Tk_IsTopLevel(winPtr) &&
5783            !Tk_IsEmbedded(winPtr) && (winPtr->display == display)) {
5784        wrapper = (winPtr->wmInfoPtr->reparent != None)
5785            ? winPtr->wmInfoPtr->reparent
5786            : winPtr->wmInfoPtr->wrapperPtr->window;
5787
5788        hPtr = Tcl_CreateHashEntry(table,
5789            (char *) wrapper, &newEntry);
5790        Tcl_SetHashValue(hPtr, winPtr);
5791    }
5792
5793    for (childPtr = winPtr->childList; childPtr != NULL;
5794            childPtr = childPtr->nextPtr) {
5795        TkWmStackorderToplevelWrapperMap(childPtr, display, table);
5796    }
5797}
5798
5799/*
5800 *----------------------------------------------------------------------
5801 *
5802 * TkWmStackorderToplevel --
5803 *
5804 *	This procedure returns the stack order of toplevel windows.
5805 *
5806 * Results:
5807 *	An array of pointers to tk window objects in stacking order
5808 *	or else NULL if there was an error.
5809 *
5810 * Side effects:
5811 *	None.
5812 *
5813 *----------------------------------------------------------------------
5814 */
5815
5816TkWindow **
5817TkWmStackorderToplevel(parentPtr)
5818    TkWindow *parentPtr;		/* Parent toplevel window. */
5819{
5820    Window dummy1, dummy2, vRoot;
5821    Window *children;
5822    unsigned int numChildren, i;
5823    TkWindow *childWinPtr, **windows, **window_ptr;
5824    Tcl_HashTable table;
5825    Tcl_HashEntry *hPtr;
5826    Tcl_HashSearch search;
5827
5828    /*
5829     * Map X Window ids to a TkWindow of the wrapped toplevel.
5830     */
5831
5832    Tcl_InitHashTable(&table, TCL_ONE_WORD_KEYS);
5833    TkWmStackorderToplevelWrapperMap(parentPtr, parentPtr->display, &table);
5834
5835    window_ptr = windows = (TkWindow **) ckalloc((table.numEntries+1)
5836        * sizeof(TkWindow *));
5837
5838    /*
5839     * Special cases: If zero or one toplevels were mapped
5840     * there is no need to call XQueryTree.
5841     */
5842
5843    switch (table.numEntries) {
5844    case 0:
5845        windows[0] = NULL;
5846        goto done;
5847    case 1:
5848        hPtr = Tcl_FirstHashEntry(&table, &search);
5849        windows[0] = (TkWindow *) Tcl_GetHashValue(hPtr);
5850        windows[1] = NULL;
5851        goto done;
5852    }
5853
5854    vRoot = parentPtr->wmInfoPtr->vRoot;
5855    if (vRoot == None) {
5856        vRoot = RootWindowOfScreen(Tk_Screen((Tk_Window) parentPtr));
5857    }
5858
5859    if (XQueryTree(parentPtr->display, vRoot, &dummy1, &dummy2,
5860            &children, &numChildren) == 0) {
5861        ckfree((char *) windows);
5862        windows = NULL;
5863    } else {
5864        for (i = 0; i < numChildren; i++) {
5865            hPtr = Tcl_FindHashEntry(&table, (char *) children[i]);
5866            if (hPtr != NULL) {
5867                childWinPtr = (TkWindow *) Tcl_GetHashValue(hPtr);
5868                *window_ptr++ = childWinPtr;
5869            }
5870        }
5871        if ((window_ptr - windows) != table.numEntries)
5872            Tcl_Panic("num matched toplevel windows does not equal num children");
5873        *window_ptr = NULL;
5874	if (numChildren) {
5875	    XFree((char *) children);
5876	}
5877    }
5878
5879    done:
5880    Tcl_DeleteHashTable(&table);
5881    return windows;
5882}
5883
5884/*
5885 *----------------------------------------------------------------------
5886 *
5887 * TkWmRestackToplevel --
5888 *
5889 *	This procedure restacks a top-level window.
5890 *
5891 * Results:
5892 *	None.
5893 *
5894 * Side effects:
5895 *	WinPtr gets restacked  as specified by aboveBelow and otherPtr.
5896 *
5897 *----------------------------------------------------------------------
5898 */
5899
5900void
5901TkWmRestackToplevel(winPtr, aboveBelow, otherPtr)
5902    TkWindow *winPtr;		/* Window to restack. */
5903    int aboveBelow;		/* Gives relative position for restacking;
5904				 * must be Above or Below. */
5905    TkWindow *otherPtr;		/* Window relative to which to restack;
5906				 * if NULL, then winPtr gets restacked
5907				 * above or below *all* siblings. */
5908{
5909    XWindowChanges changes;
5910    unsigned int mask;
5911    TkWindow *wrapperPtr;
5912
5913    memset(&changes, 0, sizeof(XWindowChanges));
5914    changes.stack_mode = aboveBelow;
5915    mask = CWStackMode;
5916
5917    /*
5918     * Make sure that winPtr and its wrapper window have been created.
5919     */
5920    if (winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
5921	TkWmMapWindow(winPtr);
5922    }
5923    wrapperPtr = winPtr->wmInfoPtr->wrapperPtr;
5924
5925    if (otherPtr != NULL) {
5926	/*
5927	 * The window is to be restacked with respect to another toplevel.
5928	 * Make sure it has been created as well.
5929	 */
5930	if (otherPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
5931	    TkWmMapWindow(otherPtr);
5932	}
5933	changes.sibling = otherPtr->wmInfoPtr->wrapperPtr->window;
5934	mask |= CWSibling;
5935    }
5936
5937    /*
5938     * Reconfigure the window.  Note that we use XReconfigureWMWindow
5939     * instead of XConfigureWindow, in order to handle the case
5940     * where the window is to be restacked with respect to another toplevel.
5941     * See [ICCCM] 4.1.5 "Configuring the Window" and XReconfigureWMWindow(3)
5942     * for details.
5943     */
5944
5945    XReconfigureWMWindow(winPtr->display, wrapperPtr->window,
5946	    Tk_ScreenNumber((Tk_Window) winPtr), mask,  &changes);
5947}
5948
5949
5950/*
5951 *----------------------------------------------------------------------
5952 *
5953 * TkWmAddToColormapWindows --
5954 *
5955 *	This procedure is called to add a given window to the
5956 *	WM_COLORMAP_WINDOWS property for its top-level, if it
5957 *	isn't already there.  It is invoked by the Tk code that
5958 *	creates a new colormap, in order to make sure that colormap
5959 *	information is propagated to the window manager by default.
5960 *
5961 * Results:
5962 *	None.
5963 *
5964 * Side effects:
5965 *	WinPtr's window gets added to the WM_COLORMAP_WINDOWS
5966 *	property of its nearest top-level ancestor, unless the
5967 *	colormaps have been set explicitly with the
5968 *	"wm colormapwindows" command.
5969 *
5970 *----------------------------------------------------------------------
5971 */
5972
5973void
5974TkWmAddToColormapWindows(winPtr)
5975    TkWindow *winPtr;		/* Window with a non-default colormap.
5976				 * Should not be a top-level window. */
5977{
5978    TkWindow *wrapperPtr;
5979    TkWindow *topPtr;
5980    Window *oldPtr, *newPtr;
5981    int count, i;
5982
5983    if (winPtr->window == None) {
5984	return;
5985    }
5986
5987    for (topPtr = winPtr->parentPtr; ; topPtr = topPtr->parentPtr) {
5988	if (topPtr == NULL) {
5989	    /*
5990	     * Window is being deleted.  Skip the whole operation.
5991	     */
5992
5993	    return;
5994	}
5995	if (topPtr->flags & TK_TOP_HIERARCHY) {
5996	    break;
5997	}
5998    }
5999    if (topPtr->wmInfoPtr == NULL) {
6000	return;
6001    }
6002
6003    if (topPtr->wmInfoPtr->flags & WM_COLORMAPS_EXPLICIT) {
6004	return;
6005    }
6006    if (topPtr->wmInfoPtr->wrapperPtr == NULL) {
6007	CreateWrapper(topPtr->wmInfoPtr);
6008    }
6009    wrapperPtr = topPtr->wmInfoPtr->wrapperPtr;
6010
6011    /*
6012     * Fetch the old value of the property.
6013     */
6014
6015    if (XGetWMColormapWindows(topPtr->display, wrapperPtr->window,
6016	    &oldPtr, &count) == 0) {
6017	oldPtr = NULL;
6018	count = 0;
6019    }
6020
6021    /*
6022     * Make sure that the window isn't already in the list.
6023     */
6024
6025    for (i = 0; i < count; i++) {
6026	if (oldPtr[i] == winPtr->window) {
6027	    return;
6028	}
6029    }
6030
6031    /*
6032     * Make a new bigger array and use it to reset the property.
6033     * Automatically add the toplevel itself as the last element
6034     * of the list.
6035     */
6036
6037    newPtr = (Window *) ckalloc((unsigned) ((count+2)*sizeof(Window)));
6038    for (i = 0; i < count; i++) {
6039	newPtr[i] = oldPtr[i];
6040    }
6041    if (count == 0) {
6042	count++;
6043    }
6044    newPtr[count-1] = winPtr->window;
6045    newPtr[count] = topPtr->window;
6046    XSetWMColormapWindows(topPtr->display, wrapperPtr->window, newPtr,
6047	    count+1);
6048    ckfree((char *) newPtr);
6049    if (oldPtr != NULL) {
6050	XFree((char *) oldPtr);
6051    }
6052}
6053
6054/*
6055 *----------------------------------------------------------------------
6056 *
6057 * TkWmRemoveFromColormapWindows --
6058 *
6059 *	This procedure is called to remove a given window from the
6060 *	WM_COLORMAP_WINDOWS property for its top-level.  It is invoked
6061 *	when windows are deleted.
6062 *
6063 * Results:
6064 *	None.
6065 *
6066 * Side effects:
6067 *	WinPtr's window gets removed from the WM_COLORMAP_WINDOWS
6068 *	property of its nearest top-level ancestor, unless the
6069 *	top-level itself is being deleted too.
6070 *
6071 *----------------------------------------------------------------------
6072 */
6073
6074void
6075TkWmRemoveFromColormapWindows(winPtr)
6076    TkWindow *winPtr;		/* Window that may be present in
6077				 * WM_COLORMAP_WINDOWS property for its
6078				 * top-level.  Should not be a top-level
6079				 * window. */
6080{
6081    TkWindow *wrapperPtr;
6082    TkWindow *topPtr;
6083    Window *oldPtr;
6084    int count, i, j;
6085
6086    if (winPtr->window == None) {
6087	return;
6088    }
6089
6090    for (topPtr = winPtr->parentPtr; ; topPtr = topPtr->parentPtr) {
6091	if (topPtr == NULL) {
6092	    /*
6093	     * Ancestors have been deleted, so skip the whole operation.
6094	     * Seems like this can't ever happen?
6095	     */
6096
6097	    return;
6098	}
6099	if (topPtr->flags & TK_TOP_HIERARCHY) {
6100	    break;
6101	}
6102    }
6103    if (topPtr->flags & TK_ALREADY_DEAD) {
6104	/*
6105	 * Top-level is being deleted, so there's no need to cleanup
6106	 * the WM_COLORMAP_WINDOWS property.
6107	 */
6108
6109	return;
6110    }
6111    if (topPtr->wmInfoPtr == NULL) {
6112	return;
6113    }
6114
6115    if (topPtr->wmInfoPtr->wrapperPtr == NULL) {
6116	CreateWrapper(topPtr->wmInfoPtr);
6117    }
6118    wrapperPtr = topPtr->wmInfoPtr->wrapperPtr;
6119    if (wrapperPtr == NULL) {
6120	return;
6121    }
6122
6123    /*
6124     * Fetch the old value of the property.
6125     */
6126
6127    if (XGetWMColormapWindows(topPtr->display, wrapperPtr->window,
6128	    &oldPtr, &count) == 0) {
6129	return;
6130    }
6131
6132    /*
6133     * Find the window and slide the following ones down to cover
6134     * it up.
6135     */
6136
6137    for (i = 0; i < count; i++) {
6138	if (oldPtr[i] == winPtr->window) {
6139	    for (j = i ; j < count-1; j++) {
6140		oldPtr[j] = oldPtr[j+1];
6141	    }
6142	    XSetWMColormapWindows(topPtr->display, wrapperPtr->window,
6143		    oldPtr, count-1);
6144	    break;
6145	}
6146    }
6147    XFree((char *) oldPtr);
6148}
6149
6150/*
6151 *----------------------------------------------------------------------
6152 *
6153 * TkGetPointerCoords --
6154 *
6155 *	Fetch the position of the mouse pointer.
6156 *
6157 * Results:
6158 *	*xPtr and *yPtr are filled in with the (virtual) root coordinates
6159 *	of the mouse pointer for tkwin's display.  If the pointer isn't
6160 *	on tkwin's screen, then -1 values are returned for both
6161 *	coordinates.  The argument tkwin must be a toplevel window.
6162 *
6163 * Side effects:
6164 *	None.
6165 *
6166 *----------------------------------------------------------------------
6167 */
6168
6169void
6170TkGetPointerCoords(tkwin, xPtr, yPtr)
6171    Tk_Window tkwin;		/* Toplevel window that identifies screen
6172				 * on which lookup is to be done. */
6173    int *xPtr, *yPtr;		/* Store pointer coordinates here. */
6174{
6175    TkWindow *winPtr = (TkWindow *) tkwin;
6176    WmInfo *wmPtr;
6177    Window w, root, child;
6178    int rootX, rootY;
6179    unsigned int mask;
6180
6181    wmPtr = winPtr->wmInfoPtr;
6182
6183    w = wmPtr->vRoot;
6184    if (w == None) {
6185	w = RootWindow(winPtr->display, winPtr->screenNum);
6186    }
6187    if (XQueryPointer(winPtr->display, w, &root, &child, &rootX, &rootY,
6188	    xPtr, yPtr, &mask) != True) {
6189	*xPtr = -1;
6190	*yPtr = -1;
6191    }
6192}
6193
6194/*
6195 *----------------------------------------------------------------------
6196 *
6197 * GetMaxSize --
6198 *
6199 *	This procedure computes the current maxWidth and maxHeight
6200 *	values for a window, taking into account the possibility
6201 *	that they may be defaulted.
6202 *
6203 * Results:
6204 *	The values at *maxWidthPtr and *maxHeightPtr are filled
6205 *	in with the maximum allowable dimensions of wmPtr's window,
6206 *	in grid units.  If no maximum has been specified for the
6207 *	window, then this procedure computes the largest sizes that
6208 *	will fit on the screen.
6209 *
6210 * Side effects:
6211 *	None.
6212 *
6213 *----------------------------------------------------------------------
6214 */
6215
6216static void
6217GetMaxSize(wmPtr, maxWidthPtr, maxHeightPtr)
6218    WmInfo *wmPtr;		/* Window manager information for the
6219				 * window. */
6220    int *maxWidthPtr;		/* Where to store the current maximum
6221				 * width of the window. */
6222    int *maxHeightPtr;		/* Where to store the current maximum
6223				 * height of the window. */
6224{
6225    int tmp;
6226
6227    if (wmPtr->maxWidth > 0) {
6228	*maxWidthPtr = wmPtr->maxWidth;
6229    } else {
6230	/*
6231	 * Must compute a default width.  Fill up the display, leaving a
6232	 * bit of extra space for the window manager's borders.
6233	 */
6234
6235	tmp = DisplayWidth(wmPtr->winPtr->display, wmPtr->winPtr->screenNum)
6236	    - 15;
6237	if (wmPtr->gridWin != NULL) {
6238	    /*
6239	     * Gridding is turned on;  convert from pixels to grid units.
6240	     */
6241
6242	    tmp = wmPtr->reqGridWidth
6243		    + (tmp - wmPtr->winPtr->reqWidth)/wmPtr->widthInc;
6244	}
6245	*maxWidthPtr = tmp;
6246    }
6247    if (wmPtr->maxHeight > 0) {
6248	*maxHeightPtr = wmPtr->maxHeight;
6249    } else {
6250	tmp = DisplayHeight(wmPtr->winPtr->display, wmPtr->winPtr->screenNum)
6251	    - 30;
6252	if (wmPtr->gridWin != NULL) {
6253	    tmp = wmPtr->reqGridHeight
6254		    + (tmp - wmPtr->winPtr->reqHeight)/wmPtr->heightInc;
6255	}
6256	*maxHeightPtr = tmp;
6257    }
6258}
6259
6260/*
6261 *----------------------------------------------------------------------
6262 *
6263 * TkSetTransientFor --
6264 *
6265 *	Set a Tk window to be transient with reference to a specified
6266 *	parent or the toplevel ancestor if None is passed as parent.
6267 *
6268 *----------------------------------------------------------------------
6269 */
6270
6271static void
6272TkSetTransientFor(tkwin, parent)
6273    Tk_Window tkwin;
6274    Tk_Window parent;
6275{
6276    if (parent == None) {
6277	parent = Tk_Parent(tkwin);
6278	while (!Tk_IsTopLevel(parent))
6279	    parent = Tk_Parent(tkwin);
6280    }
6281    XSetTransientForHint(Tk_Display(tkwin),
6282	((TkWindow *)tkwin)->wmInfoPtr->wrapperPtr->window,
6283	((TkWindow *)parent)->wmInfoPtr->wrapperPtr->window);
6284}
6285
6286/*
6287 *----------------------------------------------------------------------
6288 *
6289 * TkpMakeMenuWindow --
6290 *
6291 *	Configure the window to be either a pull-down (or pop-up)
6292 *	menu, or as a toplevel (torn-off) menu or palette.
6293 *
6294 * Results:
6295 *	None.
6296 *
6297 * Side effects:
6298 *	Changes the style bit used to create a new Mac toplevel.
6299 *
6300 *----------------------------------------------------------------------
6301 */
6302
6303void
6304TkpMakeMenuWindow(tkwin, transient)
6305    Tk_Window tkwin;		/* New window. */
6306    int transient;		/* 1 means menu is only posted briefly as
6307				 * a popup or pulldown or cascade.  0 means
6308				 * menu is always visible, e.g. as a torn-off
6309				 * menu.  Determines whether save_under and
6310				 * override_redirect should be set. */
6311{
6312    WmInfo *wmPtr;
6313    XSetWindowAttributes atts;
6314    TkWindow *wrapperPtr;
6315    Tcl_Obj *typeObj;
6316
6317    if (!Tk_HasWrapper(tkwin)) {
6318	return;
6319    }
6320    wmPtr = ((TkWindow *) tkwin)->wmInfoPtr;
6321    if (wmPtr->wrapperPtr == NULL) {
6322	CreateWrapper(wmPtr);
6323    }
6324    wrapperPtr = wmPtr->wrapperPtr;
6325    if (transient) {
6326	atts.override_redirect = True;
6327	atts.save_under = True;
6328	typeObj = Tcl_NewStringObj("dropdown_menu", -1);
6329    } else {
6330	atts.override_redirect = False;
6331	atts.save_under = False;
6332	typeObj = Tcl_NewStringObj("menu", -1);
6333	TkSetTransientFor(tkwin, None);
6334    }
6335    SetNetWmType((TkWindow *)tkwin, typeObj);
6336
6337    /*
6338     * The override-redirect and save-under bits must be set on the
6339     * wrapper window in order to have the desired effect.  However,
6340     * also set the override-redirect bit on the window itself, so
6341     * that the "wm overrideredirect" command will see it.
6342     */
6343
6344    if ((atts.override_redirect != Tk_Attributes(wrapperPtr)->override_redirect)
6345	    || (atts.save_under != Tk_Attributes(wrapperPtr)->save_under)) {
6346	Tk_ChangeWindowAttributes((Tk_Window) wrapperPtr,
6347		CWOverrideRedirect|CWSaveUnder, &atts);
6348    }
6349    if (atts.override_redirect != Tk_Attributes(tkwin)->override_redirect) {
6350	Tk_ChangeWindowAttributes(tkwin, CWOverrideRedirect, &atts);
6351    }
6352}
6353
6354/*
6355 *----------------------------------------------------------------------
6356 *
6357 * CreateWrapper --
6358 *
6359 *	This procedure is invoked to create the wrapper window for a
6360 *	toplevel window.  It is called just before a toplevel is mapped
6361 *	for the first time.
6362 *
6363 * Results:
6364 *	None.
6365 *
6366 * Side effects:
6367 *	The wrapper is created and the toplevel is reparented inside it.
6368 *
6369 *----------------------------------------------------------------------
6370 */
6371
6372static void
6373CreateWrapper(wmPtr)
6374    WmInfo *wmPtr;		/* Window manager information for the
6375				 * window. */
6376{
6377    TkWindow *winPtr, *wrapperPtr;
6378    Window parent;
6379    Tcl_HashEntry *hPtr;
6380    int new;
6381
6382    winPtr = wmPtr->winPtr;
6383    if (winPtr->window == None) {
6384	Tk_MakeWindowExist((Tk_Window) winPtr);
6385    }
6386
6387    /*
6388     * The code below is copied from CreateTopLevelWindow,
6389     * Tk_MakeWindowExist, and TkpMakeWindow; The idea is to create an
6390     * "official" Tk window (so that we can get events on it), but to
6391     * hide the window outside the official Tk hierarchy so that it
6392     * isn't visible to the application.  See the comments for the other
6393     * procedures if you have questions about this code.
6394     */
6395
6396    wmPtr->wrapperPtr = wrapperPtr = TkAllocWindow(winPtr->dispPtr,
6397	    Tk_ScreenNumber((Tk_Window) winPtr), winPtr);
6398    wrapperPtr->dirtyAtts |= CWBorderPixel;
6399
6400    /*
6401     * Tk doesn't normally select for StructureNotifyMask events because
6402     * the events are synthesized internally.  However, for wrapper
6403     * windows we need to know when the window manager modifies the
6404     * window configuration.  We also need to select on focus change
6405     * events; these are the only windows for which we care about focus
6406     * changes.
6407     */
6408
6409    wrapperPtr->flags |= TK_WRAPPER;
6410    wrapperPtr->atts.event_mask |= StructureNotifyMask|FocusChangeMask;
6411    wrapperPtr->atts.override_redirect = winPtr->atts.override_redirect;
6412    if (winPtr->flags & TK_EMBEDDED) {
6413	parent = TkUnixContainerId(winPtr);
6414    } else {
6415	parent = XRootWindow(wrapperPtr->display, wrapperPtr->screenNum);
6416    }
6417    wrapperPtr->window = XCreateWindow(wrapperPtr->display,
6418	    parent, wrapperPtr->changes.x, wrapperPtr->changes.y,
6419	    (unsigned) wrapperPtr->changes.width,
6420	    (unsigned) wrapperPtr->changes.height,
6421	    (unsigned) wrapperPtr->changes.border_width, wrapperPtr->depth,
6422	    InputOutput, wrapperPtr->visual,
6423	    wrapperPtr->dirtyAtts|CWOverrideRedirect, &wrapperPtr->atts);
6424    hPtr = Tcl_CreateHashEntry(&wrapperPtr->dispPtr->winTable,
6425	    (char *) wrapperPtr->window, &new);
6426    Tcl_SetHashValue(hPtr, wrapperPtr);
6427    wrapperPtr->mainPtr = winPtr->mainPtr;
6428    wrapperPtr->mainPtr->refCount++;
6429    wrapperPtr->dirtyAtts = 0;
6430    wrapperPtr->dirtyChanges = 0;
6431    wrapperPtr->wmInfoPtr = wmPtr;
6432
6433    /*
6434     * Reparent the toplevel window inside the wrapper.
6435     */
6436
6437    XReparentWindow(wrapperPtr->display, winPtr->window, wrapperPtr->window,
6438	    0, 0);
6439
6440    /*
6441     * Tk must monitor structure events for wrapper windows in order
6442     * to detect changes made by window managers such as resizing,
6443     * mapping, unmapping, etc..
6444     */
6445
6446    Tk_CreateEventHandler((Tk_Window) wmPtr->wrapperPtr, StructureNotifyMask,
6447	    WrapperEventProc, (ClientData) wmPtr);
6448}
6449
6450/*
6451 *----------------------------------------------------------------------
6452 *
6453 * TkWmFocusToplevel --
6454 *
6455 *	This is a utility procedure invoked by focus-management code.
6456 *	The focus code responds to externally generated focus-related
6457 *	events on wrapper windows but ignores those events for any other
6458 *	windows.  This procedure determines whether a given window is a
6459 *	wrapper window and, if so, returns the toplevel window
6460 *	corresponding to the wrapper.
6461 *
6462 * Results:
6463 *	If winPtr is a wrapper window, returns a pointer to the
6464 *	corresponding toplevel window; otherwise returns NULL.
6465 *
6466 * Side effects:
6467 *	None.
6468 *
6469 *----------------------------------------------------------------------
6470 */
6471
6472TkWindow *
6473TkWmFocusToplevel(winPtr)
6474    TkWindow *winPtr;		/* Window that received a focus-related
6475				 * event. */
6476{
6477    if (!(winPtr->flags & TK_WRAPPER)) {
6478	return NULL;
6479    }
6480    return winPtr->wmInfoPtr->winPtr;
6481}
6482
6483/*
6484 *----------------------------------------------------------------------
6485 *
6486 * TkUnixSetMenubar --
6487 *
6488 *	This procedure is invoked by menu management code to specify the
6489 *	window to use as a menubar for a given toplevel window.
6490 *
6491 * Results:
6492 *	None.
6493 *
6494 * Side effects:
6495 *	The window given by menubar will be mapped and positioned inside
6496 *	the wrapper for tkwin and above tkwin.  Menubar will
6497 *	automatically be resized to maintain the height specified by
6498 *	TkUnixSetMenuHeight the same width as tkwin.  Any previous
6499 *	menubar specified for tkwin will be unmapped and ignored from
6500 *	now on.
6501 *
6502 *----------------------------------------------------------------------
6503 */
6504
6505void
6506TkUnixSetMenubar(tkwin, menubar)
6507    Tk_Window tkwin;		/* Token for toplevel window. */
6508    Tk_Window menubar;		/* Token for window that is to serve as
6509				 * menubar for tkwin.  Must not be a
6510				 * toplevel window.  If NULL, any
6511				 * existing menubar is canceled and the
6512				 * menu height is reset to 0. */
6513{
6514    WmInfo *wmPtr = ((TkWindow *) tkwin)->wmInfoPtr;
6515    Tk_Window parent;
6516    TkWindow *menubarPtr = (TkWindow *) menubar;
6517
6518    if (wmPtr->menubar != NULL) {
6519	/*
6520	 * There's already a menubar for this toplevel.  If it isn't the
6521	 * same as the new menubar, unmap it so that it is out of the
6522	 * way, and reparent it back to its original parent.
6523	 */
6524
6525	if (wmPtr->menubar == menubar) {
6526	    return;
6527	}
6528	((TkWindow *) wmPtr->menubar)->wmInfoPtr = NULL;
6529	((TkWindow *) wmPtr->menubar)->flags &= ~TK_REPARENTED;
6530	Tk_UnmapWindow(wmPtr->menubar);
6531	parent = Tk_Parent(wmPtr->menubar);
6532	if (parent != NULL) {
6533	    Tk_MakeWindowExist(parent);
6534	    XReparentWindow(Tk_Display(wmPtr->menubar),
6535		    Tk_WindowId(wmPtr->menubar), Tk_WindowId(parent), 0, 0);
6536	}
6537	Tk_DeleteEventHandler(wmPtr->menubar, StructureNotifyMask,
6538		MenubarDestroyProc, (ClientData) wmPtr->menubar);
6539	Tk_ManageGeometry(wmPtr->menubar, NULL, (ClientData) NULL);
6540    }
6541
6542    wmPtr->menubar = menubar;
6543    if (menubar == NULL) {
6544	wmPtr->menuHeight = 0;
6545    } else {
6546	if ((menubarPtr->flags & TK_TOP_LEVEL)
6547	     || (Tk_Screen(menubar) != Tk_Screen(tkwin))) {
6548	    Tcl_Panic("TkUnixSetMenubar got bad menubar");
6549	}
6550	wmPtr->menuHeight = Tk_ReqHeight(menubar);
6551	if (wmPtr->menuHeight == 0) {
6552	    wmPtr->menuHeight = 1;
6553	}
6554	Tk_MakeWindowExist(tkwin);
6555	Tk_MakeWindowExist(menubar);
6556	if (wmPtr->wrapperPtr == NULL) {
6557	    CreateWrapper(wmPtr);
6558	}
6559	XReparentWindow(Tk_Display(menubar), Tk_WindowId(menubar),
6560		wmPtr->wrapperPtr->window, 0, 0);
6561	menubarPtr->wmInfoPtr = wmPtr;
6562	Tk_MoveResizeWindow(menubar, 0, 0, Tk_Width(tkwin), wmPtr->menuHeight);
6563	Tk_MapWindow(menubar);
6564	Tk_CreateEventHandler(menubar, StructureNotifyMask, MenubarDestroyProc,
6565		(ClientData) menubar);
6566	Tk_ManageGeometry(menubar, &menubarMgrType, (ClientData) wmPtr);
6567	menubarPtr->flags |= TK_REPARENTED;
6568    }
6569    wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
6570    if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
6571	Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) tkwin);
6572	wmPtr->flags |= WM_UPDATE_PENDING;
6573    }
6574}
6575
6576/*
6577 *----------------------------------------------------------------------
6578 *
6579 * MenubarDestroyProc --
6580 *
6581 *	This procedure is invoked by the event dispatcher whenever a
6582 *	menubar window is destroyed (it's also invoked for a few other
6583 *	kinds of events, but we ignore those).
6584 *
6585 * Results:
6586 *	None.
6587 *
6588 * Side effects:
6589 *	The association between the window and its toplevel is broken,
6590 *	so that the window is no longer considered to be a menubar.
6591 *
6592 *----------------------------------------------------------------------
6593 */
6594
6595static void
6596MenubarDestroyProc(clientData, eventPtr)
6597    ClientData clientData;		/* TkWindow pointer for menubar. */
6598    XEvent *eventPtr;			/* Describes what just happened. */
6599{
6600    WmInfo *wmPtr;
6601
6602    if (eventPtr->type != DestroyNotify) {
6603	return;
6604    }
6605    wmPtr = ((TkWindow *) clientData)->wmInfoPtr;
6606    wmPtr->menubar = NULL;
6607    wmPtr->menuHeight = 0;
6608    wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
6609    if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
6610	Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) wmPtr->winPtr);
6611	wmPtr->flags |= WM_UPDATE_PENDING;
6612    }
6613}
6614
6615/*
6616 *----------------------------------------------------------------------
6617 *
6618 * MenubarReqProc --
6619 *
6620 *	This procedure is invoked by the Tk geometry management code
6621 *	whenever a menubar calls Tk_GeometryRequest to request a new
6622 *	size.
6623 *
6624 * Results:
6625 *	None.
6626 *
6627 * Side effects:
6628 *	None.
6629 *
6630 *----------------------------------------------------------------------
6631 */
6632
6633static void
6634MenubarReqProc(clientData, tkwin)
6635    ClientData clientData;	/* Pointer to the window manager
6636				 * information for tkwin's toplevel. */
6637    Tk_Window tkwin;		/* Handle for menubar window. */
6638{
6639    WmInfo *wmPtr = (WmInfo *) clientData;
6640
6641    wmPtr->menuHeight = Tk_ReqHeight(tkwin);
6642    if (wmPtr->menuHeight <= 0) {
6643	wmPtr->menuHeight = 1;
6644    }
6645    wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
6646    if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
6647	Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) wmPtr->winPtr);
6648	wmPtr->flags |= WM_UPDATE_PENDING;
6649    }
6650}
6651
6652/*
6653 *----------------------------------------------------------------------
6654 *
6655 * TkpGetWrapperWindow --
6656 *
6657 *	Given a toplevel window return the hidden wrapper window for
6658 *	the toplevel window if available.
6659 *
6660 * Results:
6661 *	The wrapper window.  NULL is we were not passed a toplevel
6662 *	window or the wrapper has yet to be created.
6663 *
6664 * Side effects:
6665 *	None.
6666 *
6667 *----------------------------------------------------------------------
6668 */
6669
6670TkWindow *
6671TkpGetWrapperWindow(winPtr)
6672    TkWindow *winPtr;		/* A toplevel window pointer. */
6673{
6674    register WmInfo *wmPtr = winPtr->wmInfoPtr;
6675
6676    if ((winPtr == NULL) || (wmPtr == NULL)) {
6677	return NULL;
6678    }
6679
6680    return wmPtr->wrapperPtr;
6681}
6682
6683/*
6684 *----------------------------------------------------------------------
6685 *
6686 * UpdateCommand --
6687 *
6688 *	Update the WM_COMMAND property, taking care to translate
6689 *	the command strings into the external encoding.
6690 *
6691 * Results:
6692 *	None.
6693 *
6694 * Side effects:
6695 *	None.
6696 *
6697 *----------------------------------------------------------------------
6698 */
6699
6700static void
6701UpdateCommand(winPtr)
6702    TkWindow  *winPtr;
6703{
6704    register WmInfo *wmPtr = winPtr->wmInfoPtr;
6705    Tcl_DString cmds, ds;
6706    int i, *offsets;
6707    char **cmdArgv;
6708
6709    /*
6710     * Translate the argv strings into the external encoding.  To avoid
6711     * allocating lots of memory, the strings are appended to a buffer
6712     * with nulls between each string.
6713     *
6714     * This code is tricky because we need to pass and array of pointers
6715     * to XSetCommand.  However, we can't compute the pointers as we go
6716     * because the DString buffer space could get reallocated.  So, store
6717     * offsets for each element as we go, then compute pointers from the
6718     * offsets once the entire DString is done.
6719     */
6720
6721    cmdArgv = (char **) ckalloc(sizeof(char *) * wmPtr->cmdArgc);
6722    offsets = (int *) ckalloc( sizeof(int) * wmPtr->cmdArgc);
6723    Tcl_DStringInit(&cmds);
6724    for (i = 0; i < wmPtr->cmdArgc; i++) {
6725	Tcl_UtfToExternalDString(NULL, wmPtr->cmdArgv[i], -1, &ds);
6726	offsets[i] = Tcl_DStringLength(&cmds);
6727	Tcl_DStringAppend(&cmds, Tcl_DStringValue(&ds),
6728		Tcl_DStringLength(&ds)+1);
6729	Tcl_DStringFree(&ds);
6730    }
6731    cmdArgv[0] = Tcl_DStringValue(&cmds);
6732    for (i = 1; i < wmPtr->cmdArgc; i++) {
6733	cmdArgv[i] = cmdArgv[0] + offsets[i];
6734    }
6735
6736    XSetCommand(winPtr->display, wmPtr->wrapperPtr->window,
6737	    cmdArgv, wmPtr->cmdArgc);
6738    Tcl_DStringFree(&cmds);
6739    ckfree((char *) cmdArgv);
6740    ckfree((char *) offsets);
6741}
6742
6743/*
6744 *----------------------------------------------------------------------
6745 *
6746 * TkpWmSetState --
6747 *
6748 *	Sets the window manager state for the wrapper window of a
6749 *	given toplevel window.
6750 *
6751 * Results:
6752 *	0 on error, 1 otherwise
6753 *
6754 * Side effects:
6755 *	May minimize, restore, or withdraw a window.
6756 *
6757 *----------------------------------------------------------------------
6758 */
6759
6760int
6761TkpWmSetState(winPtr, state)
6762     TkWindow *winPtr;		/* Toplevel window to operate on. */
6763     int state;			/* One of IconicState, NormalState,
6764				 * or WithdrawnState. */
6765{
6766    WmInfo *wmPtr = winPtr->wmInfoPtr;
6767
6768    if (state == WithdrawnState) {
6769        wmPtr->hints.initial_state = WithdrawnState;
6770        wmPtr->withdrawn = 1;
6771        if (wmPtr->flags & WM_NEVER_MAPPED) {
6772            return 1;
6773        }
6774        if (XWithdrawWindow(winPtr->display, wmPtr->wrapperPtr->window,
6775                winPtr->screenNum) == 0) {
6776            return 0;
6777        }
6778        WaitForMapNotify(winPtr, 0);
6779    } else if (state == NormalState) {
6780        wmPtr->hints.initial_state = NormalState;
6781        wmPtr->withdrawn = 0;
6782        if (wmPtr->flags & WM_NEVER_MAPPED) {
6783            return 1;
6784        }
6785        UpdateHints(winPtr);
6786        Tk_MapWindow((Tk_Window) winPtr);
6787    } else if (state == IconicState) {
6788        wmPtr->hints.initial_state = IconicState;
6789        if (wmPtr->flags & WM_NEVER_MAPPED) {
6790            return 1;
6791        }
6792        if (wmPtr->withdrawn) {
6793            UpdateHints(winPtr);
6794            Tk_MapWindow((Tk_Window) winPtr);
6795            wmPtr->withdrawn = 0;
6796        } else {
6797            if (XIconifyWindow(winPtr->display, wmPtr->wrapperPtr->window,
6798                    winPtr->screenNum) == 0) {
6799                return 0;
6800            }
6801            WaitForMapNotify(winPtr, 0);
6802        }
6803    }
6804
6805    return 1;
6806}
6807