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