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