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