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