/[dtapublic]/projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkwinwm.c
ViewVC logotype

Annotation of /projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkwinwm.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 69 - (hide annotations) (download)
Sat Nov 5 10:54:17 2016 UTC (8 years ago) by dashley
File MIME type: text/plain
File size: 135111 byte(s)
License and property (keyword) changes.
1 dashley 69 /* $Header$ */
2 dashley 25
3     /*
4     * tkWinWm.c --
5     *
6     * This module takes care of the interactions between a Tk-based
7     * application and the window manager. Among other things, it
8     * implements the "wm" command and passes geometry information
9     * to the window manager.
10     *
11     * Copyright (c) 1995-1997 Sun Microsystems, Inc.
12     * Copyright (c) 1998-2000 by Scriptics Corporation.
13     *
14     * See the file "license.terms" for information on usage and redistribution
15     * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
16     *
17     * RCS: @(#) $Id: tkwinwm.c,v 1.2 2002/01/28 13:42:22 dtashley Exp $
18     */
19    
20     #include "resource.h"
21     /* Necessary for LoadIcon() to work after port from Scriptics.
22     */
23    
24     #include "tkWinInt.h"
25    
26     /*
27     * Event structure for synthetic activation events. These events are
28     * placed on the event queue whenever a toplevel gets a WM_MOUSEACTIVATE
29     * message.
30     */
31    
32     typedef struct ActivateEvent {
33     Tcl_Event ev;
34     TkWindow *winPtr;
35     } ActivateEvent;
36    
37     /*
38     * A data structure of the following type holds information for
39     * each window manager protocol (such as WM_DELETE_WINDOW) for
40     * which a handler (i.e. a Tcl command) has been defined for a
41     * particular top-level window.
42     */
43    
44     typedef struct ProtocolHandler {
45     Atom protocol; /* Identifies the protocol. */
46     struct ProtocolHandler *nextPtr;
47     /* Next in list of protocol handlers for
48     * the same top-level window, or NULL for
49     * end of list. */
50     Tcl_Interp *interp; /* Interpreter in which to invoke command. */
51     char command[4]; /* Tcl command to invoke when a client
52     * message for this protocol arrives.
53     * The actual size of the structure varies
54     * to accommodate the needs of the actual
55     * command. THIS MUST BE THE LAST FIELD OF
56     * THE STRUCTURE. */
57     } ProtocolHandler;
58    
59     #define HANDLER_SIZE(cmdLength) \
60     ((unsigned) (sizeof(ProtocolHandler) - 3 + cmdLength))
61    
62     /*
63     * A data structure of the following type holds window-manager-related
64     * information for each top-level window in an application.
65     */
66    
67     typedef struct TkWmInfo {
68     TkWindow *winPtr; /* Pointer to main Tk information for
69     * this window. */
70     HWND wrapper; /* This is the decorative frame window
71     * created by the window manager to wrap
72     * a toplevel window. This window is
73     * a direct child of the root window. */
74     Tk_Uid titleUid; /* Title to display in window caption. If
75     * NULL, use name of widget. */
76     Tk_Uid iconName; /* Name to display in icon. */
77     TkWindow *masterPtr; /* Master window for TRANSIENT_FOR property,
78     * or NULL. */
79     XWMHints hints; /* Various pieces of information for
80     * window manager. */
81     char *leaderName; /* Path name of leader of window group
82     * (corresponds to hints.window_group).
83     * Malloc-ed. Note: this field doesn't
84     * get updated if leader is destroyed. */
85     Tk_Window icon; /* Window to use as icon for this window,
86     * or NULL. */
87     Tk_Window iconFor; /* Window for which this window is icon, or
88     * NULL if this isn't an icon for anyone. */
89    
90     /*
91     * Information used to construct an XSizeHints structure for
92     * the window manager:
93     */
94    
95     int defMinWidth, defMinHeight, defMaxWidth, defMaxHeight;
96     /* Default resize limits given by system. */
97     int sizeHintsFlags; /* Flags word for XSizeHints structure.
98     * If the PBaseSize flag is set then the
99     * window is gridded; otherwise it isn't
100     * gridded. */
101     int minWidth, minHeight; /* Minimum dimensions of window, in
102     * grid units, not pixels. */
103     int maxWidth, maxHeight; /* Maximum dimensions of window, in
104     * grid units, not pixels, or 0 to default. */
105     Tk_Window gridWin; /* Identifies the window that controls
106     * gridding for this top-level, or NULL if
107     * the top-level isn't currently gridded. */
108     int widthInc, heightInc; /* Increments for size changes (# pixels
109     * per step). */
110     struct {
111     int x; /* numerator */
112     int y; /* denominator */
113     } minAspect, maxAspect; /* Min/max aspect ratios for window. */
114     int reqGridWidth, reqGridHeight;
115     /* The dimensions of the window (in
116     * grid units) requested through
117     * the geometry manager. */
118     int gravity; /* Desired window gravity. */
119    
120     /*
121     * Information used to manage the size and location of a window.
122     */
123    
124     int width, height; /* Desired dimensions of window, specified
125     * in grid units. These values are
126     * set by the "wm geometry" command and by
127     * ConfigureNotify events (for when wm
128     * resizes window). -1 means user hasn't
129     * requested dimensions. */
130     int x, y; /* Desired X and Y coordinates for window.
131     * These values are set by "wm geometry",
132     * plus by ConfigureNotify events (when wm
133     * moves window). These numbers are
134     * different than the numbers stored in
135     * winPtr->changes because (a) they could be
136     * measured from the right or bottom edge
137     * of the screen (see WM_NEGATIVE_X and
138     * WM_NEGATIVE_Y flags) and (b) if the window
139     * has been reparented then they refer to the
140     * parent rather than the window itself. */
141     int borderWidth, borderHeight;
142     /* Width and height of window dressing, in
143     * pixels for the current style/exStyle. This
144     * includes the border on both sides of the
145     * window. */
146     int configWidth, configHeight;
147     /* Dimensions passed to last request that we
148     * issued to change geometry of window. Used
149     * to eliminate redundant resize operations. */
150     HMENU hMenu; /* the hMenu associated with this menu */
151     DWORD style, exStyle; /* Style flags for the wrapper window. */
152    
153     /*
154     * List of children of the toplevel which have private colormaps.
155     */
156    
157     TkWindow **cmapList; /* Array of window with private colormaps. */
158     int cmapCount; /* Number of windows in array. */
159    
160     /*
161     * Miscellaneous information.
162     */
163    
164     ProtocolHandler *protPtr; /* First in list of protocol handlers for
165     * this window (NULL means none). */
166     int cmdArgc; /* Number of elements in cmdArgv below. */
167     char **cmdArgv; /* Array of strings to store in the
168     * WM_COMMAND property. NULL means nothing
169     * available. */
170     char *clientMachine; /* String to store in WM_CLIENT_MACHINE
171     * property, or NULL. */
172     int flags; /* Miscellaneous flags, defined below. */
173     int numTransients; /* number of transients on this window */
174     struct TkWmInfo *nextPtr; /* Next in list of all top-level windows. */
175     } WmInfo;
176    
177     /*
178     * Flag values for WmInfo structures:
179     *
180     * WM_NEVER_MAPPED - non-zero means window has never been
181     * mapped; need to update all info when
182     * window is first mapped.
183     * WM_UPDATE_PENDING - non-zero means a call to UpdateGeometryInfo
184     * has already been scheduled for this
185     * window; no need to schedule another one.
186     * WM_NEGATIVE_X - non-zero means x-coordinate is measured in
187     * pixels from right edge of screen, rather
188     * than from left edge.
189     * WM_NEGATIVE_Y - non-zero means y-coordinate is measured in
190     * pixels up from bottom of screen, rather than
191     * down from top.
192     * WM_UPDATE_SIZE_HINTS - non-zero means that new size hints need to be
193     * propagated to window manager. Not used on Win.
194     * WM_SYNC_PENDING - set to non-zero while waiting for the window
195     * manager to respond to some state change.
196     * WM_MOVE_PENDING - non-zero means the application has requested
197     * a new position for the window, but it hasn't
198     * been reflected through the window manager
199     * yet.
200     * WM_COLORAMPS_EXPLICIT - non-zero means the colormap windows were
201     * set explicitly via "wm colormapwindows".
202     * WM_ADDED_TOPLEVEL_COLORMAP - non-zero means that when "wm colormapwindows"
203     * was called the top-level itself wasn't
204     * specified, so we added it implicitly at
205     * the end of the list.
206     */
207    
208     #define WM_NEVER_MAPPED (1<<0)
209     #define WM_UPDATE_PENDING (1<<1)
210     #define WM_NEGATIVE_X (1<<2)
211     #define WM_NEGATIVE_Y (1<<3)
212     #define WM_UPDATE_SIZE_HINTS (1<<4)
213     #define WM_SYNC_PENDING (1<<5)
214     #define WM_CREATE_PENDING (1<<6)
215     #define WM_MOVE_PENDING (1<<7)
216     #define WM_COLORMAPS_EXPLICIT (1<<8)
217     #define WM_ADDED_TOPLEVEL_COLORMAP (1<<9)
218     #define WM_WIDTH_NOT_RESIZABLE (1<<10)
219     #define WM_HEIGHT_NOT_RESIZABLE (1<<11)
220    
221     /*
222     * Window styles for various types of toplevel windows.
223     */
224    
225     #define WM_OVERRIDE_STYLE (WS_POPUP|WS_CLIPCHILDREN|CS_DBLCLKS)
226     #define EX_OVERRIDE_STYLE (WS_EX_TOOLWINDOW)
227    
228     #define WM_TOPLEVEL_STYLE (WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|CS_DBLCLKS)
229     #define EX_TOPLEVEL_STYLE (0)
230    
231     #define WM_TRANSIENT_STYLE \
232     (WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_CLIPSIBLINGS|CS_DBLCLKS)
233     #define EX_TRANSIENT_STYLE \
234     (WS_EX_TOOLWINDOW|WS_EX_DLGMODALFRAME)
235    
236     /*
237     * The following structure is the official type record for geometry
238     * management of top-level windows.
239     */
240    
241     static void TopLevelReqProc(ClientData dummy, Tk_Window tkwin);
242    
243     static Tk_GeomMgr wmMgrType = {
244     "wm", /* name */
245     TopLevelReqProc, /* requestProc */
246     (Tk_GeomLostSlaveProc *) NULL, /* lostSlaveProc */
247     };
248    
249     typedef struct ThreadSpecificData {
250     HPALETTE systemPalette; /* System palette; refers to the
251     * currently installed foreground logical
252     * palette. */
253     TkWindow *createWindow; /* Window that is being constructed. This
254     * value is set immediately before a
255     * call to CreateWindowEx, and is used
256     * by SetLimits. This is a gross hack
257     * needed to work around Windows brain
258     * damage where it sends the
259     * WM_GETMINMAXINFO message before the
260     * WM_CREATE window. */
261     int initialized; /* Flag indicating whether thread-
262     * specific elements of module have
263     * been initialized. */
264     int firstWindow; /* Flag, cleared when the first window
265     * is mapped in a non-iconic state. */
266     } ThreadSpecificData;
267     static Tcl_ThreadDataKey dataKey;
268    
269     /*
270     * The following variables cannot be placed in thread local storage
271     * because they must be shared across threads.
272     */
273    
274     static WNDCLASS toplevelClass; /* Class for toplevel windows. */
275     static int initialized; /* Flag indicating whether module has
276     * been initialized. */
277     TCL_DECLARE_MUTEX(winWmMutex)
278    
279    
280     /*
281     * Forward declarations for procedures defined in this file:
282     */
283    
284     static int ActivateWindow _ANSI_ARGS_((Tcl_Event *evPtr,
285     int flags));
286     static void ConfigureEvent _ANSI_ARGS_((TkWindow *winPtr,
287     XConfigureEvent *eventPtr));
288     static void ConfigureTopLevel _ANSI_ARGS_((WINDOWPOS *pos));
289     static void GenerateConfigureNotify _ANSI_ARGS_((
290     TkWindow *winPtr));
291     static void GetMaxSize _ANSI_ARGS_((WmInfo *wmPtr,
292     int *maxWidthPtr, int *maxHeightPtr));
293     static void GetMinSize _ANSI_ARGS_((WmInfo *wmPtr,
294     int *minWidthPtr, int *minHeightPtr));
295     static TkWindow * GetTopLevel _ANSI_ARGS_((HWND hwnd));
296     static void InitWm _ANSI_ARGS_((void));
297     static int InstallColormaps _ANSI_ARGS_((HWND hwnd, int message,
298     int isForemost));
299     static void InvalidateSubTree _ANSI_ARGS_((TkWindow *winPtr,
300     Colormap colormap));
301     static int ParseGeometry _ANSI_ARGS_((Tcl_Interp *interp,
302     char *string, TkWindow *winPtr));
303     static void RaiseWinWhenIdle _ANSI_ARGS_((
304     ClientData clientData));
305     static void RefreshColormap _ANSI_ARGS_((Colormap colormap,
306     TkDisplay *dispPtr));
307     static void SetLimits _ANSI_ARGS_((HWND hwnd, MINMAXINFO *info));
308     static LRESULT CALLBACK TopLevelProc _ANSI_ARGS_((HWND hwnd, UINT message,
309     WPARAM wParam, LPARAM lParam));
310     static void TopLevelEventProc _ANSI_ARGS_((ClientData clientData,
311     XEvent *eventPtr));
312     static void TopLevelReqProc _ANSI_ARGS_((ClientData dummy,
313     Tk_Window tkwin));
314     static void UpdateGeometryInfo _ANSI_ARGS_((
315     ClientData clientData));
316     static void UpdateWrapper _ANSI_ARGS_((TkWindow *winPtr));
317     static LRESULT CALLBACK WmProc _ANSI_ARGS_((HWND hwnd, UINT message,
318     WPARAM wParam, LPARAM lParam));
319     static void WmWaitVisibilityProc _ANSI_ARGS_((
320     ClientData clientData, XEvent *eventPtr));
321    
322     /*
323     *----------------------------------------------------------------------
324     *
325     * InitWm --
326     *
327     * This routine creates the Wm toplevel decorative frame class.
328     *
329     * Results:
330     * None.
331     *
332     * Side effects:
333     * Registers a new window class.
334     *
335     *----------------------------------------------------------------------
336     */
337    
338     static void
339     InitWm(void)
340     {
341     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
342     Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
343     WNDCLASS * classPtr;
344    
345     if (! tsdPtr->initialized) {
346     tsdPtr->initialized = 1;
347     tsdPtr->firstWindow = 1;
348     }
349     if (! initialized) {
350     Tcl_MutexLock(&winWmMutex);
351     if (! initialized) {
352     initialized = 1;
353     classPtr = &toplevelClass;
354    
355     /*
356     * When threads are enabled, we cannot use CLASSDC because
357     * threads will then write into the same device context.
358     *
359     * This is a hack; we should add a subsystem that manages
360     * device context on a per-thread basis. See also tkWinX.c,
361     * which also initializes a WNDCLASS structure.
362     */
363    
364     #ifdef TCL_THREADS
365     classPtr->style = CS_HREDRAW | CS_VREDRAW;
366     #else
367     classPtr->style = CS_HREDRAW | CS_VREDRAW | CS_CLASSDC;
368     #endif
369     classPtr->cbClsExtra = 0;
370     classPtr->cbWndExtra = 0;
371     classPtr->hInstance = Tk_GetHINSTANCE();
372     classPtr->hbrBackground = NULL;
373     classPtr->lpszMenuName = NULL;
374     classPtr->lpszClassName = TK_WIN_TOPLEVEL_CLASS_NAME;
375     classPtr->lpfnWndProc = WmProc;
376     classPtr->hIcon = LoadIcon(Tk_GetHINSTANCE(), (const char *)IDI_EGC);
377     /* Needed to change line above to work reliably with GUI build after
378     ** port from Scriptics code. Old line was:
379     ** classPtr->hIcon = LoadIcon(Tk_GetHINSTANCE(), "tk");
380     */
381     classPtr->hCursor = LoadCursor(NULL, IDC_ARROW);
382    
383     if (!RegisterClass(classPtr)) {
384     panic("Unable to register TkTopLevel class");
385     }
386     }
387     Tcl_MutexUnlock(&winWmMutex);
388     }
389     }
390    
391     /*
392     *----------------------------------------------------------------------
393     *
394     * GetTopLevel --
395     *
396     * This function retrieves the TkWindow associated with the
397     * given HWND.
398     *
399     * Results:
400     * Returns the matching TkWindow.
401     *
402     * Side effects:
403     * None.
404     *
405     *----------------------------------------------------------------------
406     */
407    
408     static TkWindow *
409     GetTopLevel(hwnd)
410     HWND hwnd;
411     {
412     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
413     Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
414    
415     /*
416     * If this function is called before the CreateWindowEx call
417     * has completed, then the user data slot will not have been
418     * set yet, so we use the global createWindow variable.
419     */
420    
421     if (tsdPtr->createWindow) {
422     return tsdPtr->createWindow;
423     }
424     return (TkWindow *) GetWindowLong(hwnd, GWL_USERDATA);
425     }
426    
427     /*
428     *----------------------------------------------------------------------
429     *
430     * SetLimits --
431     *
432     * Updates the minimum and maximum window size constraints.
433     *
434     * Results:
435     * None.
436     *
437     * Side effects:
438     * Changes the values of the info pointer to reflect the current
439     * minimum and maximum size values.
440     *
441     *----------------------------------------------------------------------
442     */
443    
444     static void
445     SetLimits(hwnd, info)
446     HWND hwnd;
447     MINMAXINFO *info;
448     {
449     register WmInfo *wmPtr;
450     int maxWidth, maxHeight;
451     int minWidth, minHeight;
452     int base;
453     TkWindow *winPtr = GetTopLevel(hwnd);
454    
455     if (winPtr == NULL) {
456     return;
457     }
458    
459     wmPtr = winPtr->wmInfoPtr;
460    
461     /*
462     * Copy latest constraint info.
463     */
464    
465     wmPtr->defMinWidth = info->ptMinTrackSize.x;
466     wmPtr->defMinHeight = info->ptMinTrackSize.y;
467     wmPtr->defMaxWidth = info->ptMaxTrackSize.x;
468     wmPtr->defMaxHeight = info->ptMaxTrackSize.y;
469    
470     GetMaxSize(wmPtr, &maxWidth, &maxHeight);
471     GetMinSize(wmPtr, &minWidth, &minHeight);
472    
473     if (wmPtr->gridWin != NULL) {
474     base = winPtr->reqWidth - (wmPtr->reqGridWidth * wmPtr->widthInc);
475     if (base < 0) {
476     base = 0;
477     }
478     base += wmPtr->borderWidth;
479     info->ptMinTrackSize.x = base + (minWidth * wmPtr->widthInc);
480     info->ptMaxTrackSize.x = base + (maxWidth * wmPtr->widthInc);
481    
482     base = winPtr->reqHeight - (wmPtr->reqGridHeight * wmPtr->heightInc);
483     if (base < 0) {
484     base = 0;
485     }
486     base += wmPtr->borderHeight;
487     info->ptMinTrackSize.y = base + (minHeight * wmPtr->heightInc);
488     info->ptMaxTrackSize.y = base + (maxHeight * wmPtr->heightInc);
489     } else {
490     info->ptMaxTrackSize.x = maxWidth + wmPtr->borderWidth;
491     info->ptMaxTrackSize.y = maxHeight + wmPtr->borderHeight;
492     info->ptMinTrackSize.x = minWidth + wmPtr->borderWidth;
493     info->ptMinTrackSize.y = minHeight + wmPtr->borderHeight;
494     }
495    
496     /*
497     * If the window isn't supposed to be resizable, then set the
498     * minimum and maximum dimensions to be the same as the current size.
499     */
500    
501     if (!(wmPtr->flags & WM_SYNC_PENDING)) {
502     if (wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) {
503     info->ptMinTrackSize.x = winPtr->changes.width
504     + wmPtr->borderWidth;
505     info->ptMaxTrackSize.x = info->ptMinTrackSize.x;
506     }
507     if (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE) {
508     info->ptMinTrackSize.y = winPtr->changes.height
509     + wmPtr->borderHeight;
510     info->ptMaxTrackSize.y = info->ptMinTrackSize.y;
511     }
512     }
513     }
514    
515     /*
516     *----------------------------------------------------------------------
517     *
518     * TkWinWmCleanup --
519     *
520     * Unregisters classes registered by the window manager. This is
521     * called from the DLL main entry point when the DLL is unloaded.
522     *
523     * Results:
524     * None.
525     *
526     * Side effects:
527     * The window classes are discarded.
528     *
529     *----------------------------------------------------------------------
530     */
531    
532     void
533     TkWinWmCleanup(hInstance)
534     HINSTANCE hInstance;
535     {
536     ThreadSpecificData *tsdPtr;
537    
538     /*
539     * If we're using stubs to access the Tcl library, and they
540     * haven't been initialized, we can't call Tcl_GetThreadData.
541     */
542    
543     #ifdef USE_TCL_STUBS
544     if (tclStubsPtr == NULL) {
545     return;
546     }
547     #endif
548    
549     tsdPtr = (ThreadSpecificData *)
550     Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
551    
552     if (!tsdPtr->initialized) {
553     return;
554     }
555     tsdPtr->initialized = 0;
556    
557     UnregisterClass(TK_WIN_TOPLEVEL_CLASS_NAME, hInstance);
558     }
559    
560     /*
561     *--------------------------------------------------------------
562     *
563     * TkWmNewWindow --
564     *
565     * This procedure is invoked whenever a new top-level
566     * window is created. Its job is to initialize the WmInfo
567     * structure for the window.
568     *
569     * Results:
570     * None.
571     *
572     * Side effects:
573     * A WmInfo structure gets allocated and initialized.
574     *
575     *--------------------------------------------------------------
576     */
577    
578     void
579     TkWmNewWindow(winPtr)
580     TkWindow *winPtr; /* Newly-created top-level window. */
581     {
582     register WmInfo *wmPtr;
583    
584     wmPtr = (WmInfo *) ckalloc(sizeof(WmInfo));
585     winPtr->wmInfoPtr = wmPtr;
586     wmPtr->winPtr = winPtr;
587     wmPtr->wrapper = NULL;
588     wmPtr->titleUid = NULL;
589     wmPtr->iconName = NULL;
590     wmPtr->masterPtr = NULL;
591     wmPtr->hints.flags = InputHint | StateHint;
592     wmPtr->hints.input = True;
593     wmPtr->hints.initial_state = NormalState;
594     wmPtr->hints.icon_pixmap = None;
595     wmPtr->hints.icon_window = None;
596     wmPtr->hints.icon_x = wmPtr->hints.icon_y = 0;
597     wmPtr->hints.icon_mask = None;
598     wmPtr->hints.window_group = None;
599     wmPtr->leaderName = NULL;
600     wmPtr->icon = NULL;
601     wmPtr->iconFor = NULL;
602     wmPtr->sizeHintsFlags = 0;
603    
604     /*
605     * Default the maximum dimensions to the size of the display.
606     */
607    
608     wmPtr->defMinWidth = wmPtr->defMinHeight = 0;
609     wmPtr->defMaxWidth = DisplayWidth(winPtr->display,
610     winPtr->screenNum);
611     wmPtr->defMaxHeight = DisplayHeight(winPtr->display,
612     winPtr->screenNum);
613     wmPtr->minWidth = wmPtr->minHeight = 1;
614     wmPtr->maxWidth = wmPtr->maxHeight = 0;
615     wmPtr->gridWin = NULL;
616     wmPtr->widthInc = wmPtr->heightInc = 1;
617     wmPtr->minAspect.x = wmPtr->minAspect.y = 1;
618     wmPtr->maxAspect.x = wmPtr->maxAspect.y = 1;
619     wmPtr->reqGridWidth = wmPtr->reqGridHeight = -1;
620     wmPtr->gravity = NorthWestGravity;
621     wmPtr->width = -1;
622     wmPtr->height = -1;
623     wmPtr->hMenu = NULL;
624     wmPtr->x = winPtr->changes.x;
625     wmPtr->y = winPtr->changes.y;
626     wmPtr->borderWidth = 0;
627     wmPtr->borderHeight = 0;
628    
629     wmPtr->cmapList = NULL;
630     wmPtr->cmapCount = 0;
631     wmPtr->numTransients = 0;
632    
633     wmPtr->configWidth = -1;
634     wmPtr->configHeight = -1;
635     wmPtr->protPtr = NULL;
636     wmPtr->cmdArgv = NULL;
637     wmPtr->clientMachine = NULL;
638     wmPtr->flags = WM_NEVER_MAPPED;
639     wmPtr->nextPtr = winPtr->dispPtr->firstWmPtr;
640     winPtr->dispPtr->firstWmPtr = wmPtr;
641    
642     /*
643     * Tk must monitor structure events for top-level windows, in order
644     * to detect size and position changes caused by window managers.
645     */
646    
647     Tk_CreateEventHandler((Tk_Window) winPtr, StructureNotifyMask,
648     TopLevelEventProc, (ClientData) winPtr);
649    
650     /*
651     * Arrange for geometry requests to be reflected from the window
652     * to the window manager.
653     */
654    
655     Tk_ManageGeometry((Tk_Window) winPtr, &wmMgrType, (ClientData) 0);
656     }
657    
658     /*
659     *----------------------------------------------------------------------
660     *
661     * UpdateWrapper --
662     *
663     * This function creates the wrapper window that contains the
664     * window decorations and menus for a toplevel. This function
665     * may be called after a window is mapped to change the window
666     * style.
667     *
668     * Results:
669     * None.
670     *
671     * Side effects:
672     * Destroys any old wrapper window and replaces it with a newly
673     * created wrapper.
674     *
675     *----------------------------------------------------------------------
676     */
677    
678     static void
679     UpdateWrapper(winPtr)
680     TkWindow *winPtr; /* Top-level window to redecorate. */
681     {
682     register WmInfo *wmPtr = winPtr->wmInfoPtr;
683     HWND parentHWND = NULL, oldWrapper;
684     HWND child = TkWinGetHWND(winPtr->window);
685     int x, y, width, height, state;
686     WINDOWPLACEMENT place;
687     Tcl_DString titleString;
688     int *childStateInfo = NULL;
689     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
690     Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
691    
692     parentHWND = NULL;
693     child = TkWinGetHWND(winPtr->window);
694    
695     if (winPtr->flags & TK_EMBEDDED) {
696     wmPtr->wrapper = (HWND) winPtr->privatePtr;
697     if (wmPtr->wrapper == NULL) {
698     panic("TkWmMapWindow: Cannot find container window");
699     }
700     if (!IsWindow(wmPtr->wrapper)) {
701     panic("TkWmMapWindow: Container was destroyed");
702     }
703    
704     } else {
705     /*
706     * Pick the decorative frame style. Override redirect windows get
707     * created as undecorated popups. Transient windows get a modal
708     * dialog frame. Neither override, nor transient windows appear in
709     * the Windows taskbar. Note that a transient window does not resize
710     * by default, so we need to explicitly add the WS_THICKFRAME style
711     * if we want it to be resizeable.
712     */
713    
714     if (winPtr->atts.override_redirect) {
715     wmPtr->style = WM_OVERRIDE_STYLE;
716     wmPtr->exStyle = EX_OVERRIDE_STYLE;
717     } else if (wmPtr->masterPtr) {
718     wmPtr->style = WM_TRANSIENT_STYLE;
719     wmPtr->exStyle = EX_TRANSIENT_STYLE;
720     parentHWND = Tk_GetHWND(Tk_WindowId(wmPtr->masterPtr));
721     if (! ((wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) &&
722     (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE))) {
723     wmPtr->style |= WS_THICKFRAME;
724     }
725     } else {
726     wmPtr->style = WM_TOPLEVEL_STYLE;
727     wmPtr->exStyle = EX_TOPLEVEL_STYLE;
728     }
729    
730     if ((wmPtr->flags & WM_WIDTH_NOT_RESIZABLE)
731     && (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE)) {
732     wmPtr->style &= ~ (WS_MAXIMIZEBOX | WS_SIZEBOX);
733     }
734    
735     /*
736     * Compute the geometry of the parent and child windows.
737     */
738    
739     wmPtr->flags |= WM_CREATE_PENDING|WM_MOVE_PENDING;
740     UpdateGeometryInfo((ClientData)winPtr);
741     wmPtr->flags &= ~(WM_CREATE_PENDING|WM_MOVE_PENDING);
742    
743     width = wmPtr->borderWidth + winPtr->changes.width;
744     height = wmPtr->borderHeight + winPtr->changes.height;
745    
746     /*
747     * Set the initial position from the user or program specified
748     * location. If nothing has been specified, then let the system
749     * pick a location.
750     */
751    
752     if (!(wmPtr->sizeHintsFlags & (USPosition | PPosition))
753     && (wmPtr->flags & WM_NEVER_MAPPED)) {
754     x = CW_USEDEFAULT;
755     y = CW_USEDEFAULT;
756     } else {
757     x = winPtr->changes.x;
758     y = winPtr->changes.y;
759     }
760    
761     /*
762     * Create the containing window, and set the user data to point
763     * to the TkWindow.
764     */
765    
766     tsdPtr->createWindow = winPtr;
767     Tcl_UtfToExternalDString(NULL, wmPtr->titleUid, -1, &titleString);
768     wmPtr->wrapper = CreateWindowEx(wmPtr->exStyle,
769     TK_WIN_TOPLEVEL_CLASS_NAME,
770     Tcl_DStringValue(&titleString), wmPtr->style, x, y, width,
771     height, parentHWND, NULL, Tk_GetHINSTANCE(), NULL);
772     Tcl_DStringFree(&titleString);
773     SetWindowLong(wmPtr->wrapper, GWL_USERDATA, (LONG) winPtr);
774     tsdPtr->createWindow = NULL;
775    
776     place.length = sizeof(WINDOWPLACEMENT);
777     GetWindowPlacement(wmPtr->wrapper, &place);
778     wmPtr->x = place.rcNormalPosition.left;
779     wmPtr->y = place.rcNormalPosition.top;
780    
781     TkInstallFrameMenu((Tk_Window) winPtr);
782     }
783    
784     /*
785     * Now we need to reparent the contained window and set its
786     * style appropriately. Be sure to update the style first so that
787     * Windows doesn't try to set the focus to the child window.
788     */
789    
790     SetWindowLong(child, GWL_STYLE,
791     WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
792     if (winPtr->flags & TK_EMBEDDED) {
793     SetWindowLong(child, GWL_WNDPROC, (LONG) TopLevelProc);
794     }
795     oldWrapper = SetParent(child, wmPtr->wrapper);
796     if (oldWrapper && (oldWrapper != wmPtr->wrapper)
797     && (oldWrapper != GetDesktopWindow())) {
798     SetWindowLong(oldWrapper, GWL_USERDATA, (LONG) NULL);
799    
800     if (wmPtr->numTransients > 0) {
801     /*
802     * Unset the current wrapper as the parent for all transient
803     * children for whom this is the master
804     */
805     WmInfo *wmPtr2;
806    
807     childStateInfo = (int *)ckalloc((unsigned) wmPtr->numTransients
808     * sizeof(int));
809     state = 0;
810     for (wmPtr2 = winPtr->dispPtr->firstWmPtr; wmPtr2 != NULL;
811     wmPtr2 = wmPtr2->nextPtr) {
812     if (wmPtr2->masterPtr == winPtr) {
813     if (!(wmPtr2->flags & WM_NEVER_MAPPED)) {
814     childStateInfo[state++] = wmPtr2->hints.initial_state;
815     SetParent(TkWinGetHWND(wmPtr2->winPtr->window), NULL);
816     }
817     }
818     }
819     }
820     /*
821     * Remove the menubar before destroying the window so the menubar
822     * isn't destroyed.
823     */
824    
825     SetMenu(oldWrapper, NULL);
826     DestroyWindow(oldWrapper);
827     }
828    
829     wmPtr->flags &= ~WM_NEVER_MAPPED;
830     SendMessage(wmPtr->wrapper, TK_ATTACHWINDOW, (WPARAM) child, 0);
831    
832     /*
833     * Force an initial transition from withdrawn to the real
834     * initial state.
835     */
836    
837     state = wmPtr->hints.initial_state;
838     wmPtr->hints.initial_state = WithdrawnState;
839     TkpWmSetState(winPtr, state);
840    
841     /*
842     * If we are embedded then force a mapping of the window now,
843     * because we do not necessarily own the wrapper and may not
844     * get another opportunity to map ourselves. We should not be
845     * in either iconified or zoomed states when we get here, so
846     * it is safe to just check for TK_EMBEDDED without checking
847     * what state we are supposed to be in (default to NormalState).
848     */
849    
850     if (winPtr->flags & TK_EMBEDDED) {
851     XMapWindow(winPtr->display, winPtr->window);
852     }
853    
854     /*
855     * Set up menus on the wrapper if required.
856     */
857    
858     if (wmPtr->hMenu != NULL) {
859     wmPtr->flags = WM_SYNC_PENDING;
860     SetMenu(wmPtr->wrapper, wmPtr->hMenu);
861     wmPtr->flags &= ~WM_SYNC_PENDING;
862     }
863    
864     if (childStateInfo) {
865     if (wmPtr->numTransients > 0) {
866     /*
867     * Reset all transient children for whom this is the master
868     */
869     WmInfo *wmPtr2;
870    
871     state = 0;
872     for (wmPtr2 = winPtr->dispPtr->firstWmPtr; wmPtr2 != NULL;
873     wmPtr2 = wmPtr2->nextPtr) {
874     if (wmPtr2->masterPtr == winPtr) {
875     if (!(wmPtr2->flags & WM_NEVER_MAPPED)) {
876     UpdateWrapper(wmPtr2->winPtr);
877     TkpWmSetState(wmPtr2->winPtr, childStateInfo[state++]);
878     }
879     }
880     }
881     }
882    
883     ckfree((char *) childStateInfo);
884     }
885    
886     /*
887     * If this is the first window created by the application, then
888     * we should activate the initial window.
889     */
890    
891     if (tsdPtr->firstWindow) {
892     tsdPtr->firstWindow = 0;
893     SetActiveWindow(wmPtr->wrapper);
894     }
895     }
896    
897     /*
898     *--------------------------------------------------------------
899     *
900     * TkWmMapWindow --
901     *
902     * This procedure is invoked to map a top-level window. This
903     * module gets a chance to update all window-manager-related
904     * information in properties before the window manager sees
905     * the map event and checks the properties. It also gets to
906     * decide whether or not to even map the window after all.
907     *
908     * Results:
909     * None.
910     *
911     * Side effects:
912     * Properties of winPtr may get updated to provide up-to-date
913     * information to the window manager. The window may also get
914     * mapped, but it may not be if this procedure decides that
915     * isn't appropriate (e.g. because the window is withdrawn).
916     *
917     *--------------------------------------------------------------
918     */
919    
920     void
921     TkWmMapWindow(winPtr)
922     TkWindow *winPtr; /* Top-level window that's about to
923     * be mapped. */
924     {
925     register WmInfo *wmPtr = winPtr->wmInfoPtr;
926     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
927     Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
928    
929     if (!tsdPtr->initialized) {
930     InitWm();
931     }
932    
933     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
934     if (wmPtr->hints.initial_state == WithdrawnState) {
935     return;
936     }
937    
938     /*
939     * Map the window in either the iconified or normal state. Note that
940     * we only send a map event if the window is in the normal state.
941     */
942    
943     TkpWmSetState(winPtr, wmPtr->hints.initial_state);
944     }
945    
946     /*
947     * This is the first time this window has ever been mapped.
948     * Store all the window-manager-related information for the
949     * window.
950     */
951    
952     if (wmPtr->titleUid == NULL) {
953     wmPtr->titleUid = winPtr->nameUid;
954     }
955     UpdateWrapper(winPtr);
956     }
957    
958     /*
959     *--------------------------------------------------------------
960     *
961     * TkWmUnmapWindow --
962     *
963     * This procedure is invoked to unmap a top-level window. The
964     * only thing it does special is unmap the decorative frame before
965     * unmapping the toplevel window.
966     *
967     * Results:
968     * None.
969     *
970     * Side effects:
971     * Unmaps the decorative frame and the window.
972     *
973     *--------------------------------------------------------------
974     */
975    
976     void
977     TkWmUnmapWindow(winPtr)
978     TkWindow *winPtr; /* Top-level window that's about to
979     * be unmapped. */
980     {
981     TkpWmSetState(winPtr, WithdrawnState);
982     }
983    
984     /*
985     *----------------------------------------------------------------------
986     *
987     * TkpWmSetState --
988     *
989     * Sets the window manager state for the wrapper window of a
990     * given toplevel window.
991     *
992     * Results:
993     * None.
994     *
995     * Side effects:
996     * May maximize, minimize, restore, or withdraw a window.
997     *
998     *----------------------------------------------------------------------
999     */
1000    
1001     void
1002     TkpWmSetState(winPtr, state)
1003     TkWindow *winPtr; /* Toplevel window to operate on. */
1004     int state; /* One of IconicState, ZoomState, NormalState,
1005     * or WithdrawnState. */
1006     {
1007     WmInfo *wmPtr = winPtr->wmInfoPtr;
1008     int cmd;
1009    
1010     if ((wmPtr->flags & WM_NEVER_MAPPED) ||
1011     (wmPtr->masterPtr && !Tk_IsMapped(wmPtr->masterPtr))) {
1012     wmPtr->hints.initial_state = state;
1013     return;
1014     }
1015    
1016     wmPtr->flags |= WM_SYNC_PENDING;
1017     if (state == WithdrawnState) {
1018     cmd = SW_HIDE;
1019     } else if (state == IconicState) {
1020     cmd = SW_SHOWMINNOACTIVE;
1021     } else if (state == NormalState) {
1022     cmd = SW_SHOWNOACTIVATE;
1023     } else if (state == ZoomState) {
1024     cmd = SW_SHOWMAXIMIZED;
1025     }
1026    
1027     ShowWindow(wmPtr->wrapper, cmd);
1028     wmPtr->flags &= ~WM_SYNC_PENDING;
1029     }
1030    
1031     /*
1032     *--------------------------------------------------------------
1033     *
1034     * TkWmDeadWindow --
1035     *
1036     * This procedure is invoked when a top-level window is
1037     * about to be deleted. It cleans up the wm-related data
1038     * structures for the window.
1039     *
1040     * Results:
1041     * None.
1042     *
1043     * Side effects:
1044     * The WmInfo structure for winPtr gets freed up.
1045     *
1046     *--------------------------------------------------------------
1047     */
1048    
1049     void
1050     TkWmDeadWindow(winPtr)
1051     TkWindow *winPtr; /* Top-level window that's being deleted. */
1052     {
1053     register WmInfo *wmPtr = winPtr->wmInfoPtr;
1054     WmInfo *wmPtr2;
1055    
1056     if (wmPtr == NULL) {
1057     return;
1058     }
1059    
1060     /*
1061     * Clean up event related window info.
1062     */
1063    
1064     if (winPtr->dispPtr->firstWmPtr == wmPtr) {
1065     winPtr->dispPtr->firstWmPtr = wmPtr->nextPtr;
1066     } else {
1067     register WmInfo *prevPtr;
1068     for (prevPtr = winPtr->dispPtr->firstWmPtr; ;
1069     prevPtr = prevPtr->nextPtr) {
1070     if (prevPtr == NULL) {
1071     panic("couldn't unlink window in TkWmDeadWindow");
1072     }
1073     if (prevPtr->nextPtr == wmPtr) {
1074     prevPtr->nextPtr = wmPtr->nextPtr;
1075     break;
1076     }
1077     }
1078     }
1079    
1080     /*
1081     * Reset all transient windows whose master is the dead window.
1082     */
1083    
1084     for (wmPtr2 = winPtr->dispPtr->firstWmPtr; wmPtr2 != NULL;
1085     wmPtr2 = wmPtr2->nextPtr) {
1086     if (wmPtr2->masterPtr == winPtr) {
1087     wmPtr2->masterPtr = NULL;
1088     if ((wmPtr2->wrapper != None)
1089     && !(wmPtr2->flags & (WM_NEVER_MAPPED))) {
1090     UpdateWrapper(wmPtr2->winPtr);
1091     }
1092     }
1093     }
1094    
1095     if (wmPtr->hints.flags & IconPixmapHint) {
1096     Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap);
1097     }
1098     if (wmPtr->hints.flags & IconMaskHint) {
1099     Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_mask);
1100     }
1101     if (wmPtr->leaderName != NULL) {
1102     ckfree(wmPtr->leaderName);
1103     }
1104     if (wmPtr->icon != NULL) {
1105     wmPtr2 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
1106     wmPtr2->iconFor = NULL;
1107     }
1108     if (wmPtr->iconFor != NULL) {
1109     wmPtr2 = ((TkWindow *) wmPtr->iconFor)->wmInfoPtr;
1110     wmPtr2->icon = NULL;
1111     wmPtr2->hints.flags &= ~IconWindowHint;
1112     }
1113     while (wmPtr->protPtr != NULL) {
1114     ProtocolHandler *protPtr;
1115    
1116     protPtr = wmPtr->protPtr;
1117     wmPtr->protPtr = protPtr->nextPtr;
1118     Tcl_EventuallyFree((ClientData) protPtr, TCL_DYNAMIC);
1119     }
1120     if (wmPtr->cmdArgv != NULL) {
1121     ckfree((char *) wmPtr->cmdArgv);
1122     }
1123     if (wmPtr->clientMachine != NULL) {
1124     ckfree((char *) wmPtr->clientMachine);
1125     }
1126     if (wmPtr->flags & WM_UPDATE_PENDING) {
1127     Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
1128     }
1129     if (wmPtr->masterPtr != NULL) {
1130     wmPtr2 = wmPtr->masterPtr->wmInfoPtr;
1131     /*
1132     * If we had a master, tell them that we aren't tied
1133     * to them anymore
1134     */
1135     if (wmPtr2 != NULL) {
1136     wmPtr2->numTransients--;
1137     }
1138     Tk_DeleteEventHandler((Tk_Window) wmPtr->masterPtr,
1139     VisibilityChangeMask,
1140     WmWaitVisibilityProc, (ClientData) winPtr);
1141     wmPtr->masterPtr = NULL;
1142     }
1143    
1144     /*
1145     * Destroy the decorative frame window.
1146     */
1147    
1148     if (!(winPtr->flags & TK_EMBEDDED)) {
1149     if (wmPtr->wrapper != NULL) {
1150     DestroyWindow(wmPtr->wrapper);
1151     } else {
1152     DestroyWindow(Tk_GetHWND(winPtr->window));
1153     }
1154     }
1155     ckfree((char *) wmPtr);
1156     winPtr->wmInfoPtr = NULL;
1157     }
1158    
1159     /*
1160     *--------------------------------------------------------------
1161     *
1162     * TkWmSetClass --
1163     *
1164     * This procedure is invoked whenever a top-level window's
1165     * class is changed. If the window has been mapped then this
1166     * procedure updates the window manager property for the
1167     * class. If the window hasn't been mapped, the update is
1168     * deferred until just before the first mapping.
1169     *
1170     * Results:
1171     * None.
1172     *
1173     * Side effects:
1174     * A window property may get updated.
1175     *
1176     *--------------------------------------------------------------
1177     */
1178    
1179     void
1180     TkWmSetClass(winPtr)
1181     TkWindow *winPtr; /* Newly-created top-level window. */
1182     {
1183     return;
1184     }
1185    
1186     /*
1187     *----------------------------------------------------------------------
1188     *
1189     * Tk_WmCmd --
1190     *
1191     * This procedure is invoked to process the "wm" Tcl command.
1192     * See the user documentation for details on what it does.
1193     *
1194     * Results:
1195     * A standard Tcl result.
1196     *
1197     * Side effects:
1198     * See the user documentation.
1199     *
1200     *----------------------------------------------------------------------
1201     */
1202    
1203     /* ARGSUSED */
1204     int
1205     Tk_WmCmd(clientData, interp, argc, argv)
1206     ClientData clientData; /* Main window associated with
1207     * interpreter. */
1208     Tcl_Interp *interp; /* Current interpreter. */
1209     int argc; /* Number of arguments. */
1210     char **argv; /* Argument strings. */
1211     {
1212     Tk_Window tkwin = (Tk_Window) clientData;
1213     TkWindow *winPtr = NULL;
1214     register WmInfo *wmPtr;
1215     int c;
1216     size_t length;
1217     TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
1218    
1219     if (argc < 2) {
1220     wrongNumArgs:
1221     Tcl_AppendResult(interp, "wrong # args: should be \"",
1222     argv[0], " option window ?arg ...?\"", (char *) NULL);
1223     return TCL_ERROR;
1224     }
1225     c = argv[1][0];
1226     length = strlen(argv[1]);
1227     if ((c == 't') && (strncmp(argv[1], "tracing", length) == 0)
1228     && (length >= 3)) {
1229     if ((argc != 2) && (argc != 3)) {
1230     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1231     argv[0], " tracing ?boolean?\"", (char *) NULL);
1232     return TCL_ERROR;
1233     }
1234     if (argc == 2) {
1235     Tcl_SetResult(interp, ((dispPtr->wmTracing) ? "on" : "off"),
1236     TCL_STATIC);
1237     return TCL_OK;
1238     }
1239     return Tcl_GetBoolean(interp, argv[2], &dispPtr->wmTracing);
1240     }
1241    
1242     if (argc < 3) {
1243     goto wrongNumArgs;
1244     }
1245     winPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
1246     if (winPtr == NULL) {
1247     return TCL_ERROR;
1248     }
1249     if (!(winPtr->flags & TK_TOP_LEVEL)) {
1250     Tcl_AppendResult(interp, "window \"", winPtr->pathName,
1251     "\" isn't a top-level window", (char *) NULL);
1252     return TCL_ERROR;
1253     }
1254     wmPtr = winPtr->wmInfoPtr;
1255     if ((c == 'a') && (strncmp(argv[1], "aspect", length) == 0)) {
1256     int numer1, denom1, numer2, denom2;
1257    
1258     if ((argc != 3) && (argc != 7)) {
1259     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1260     argv[0], " aspect window ?minNumer minDenom ",
1261     "maxNumer maxDenom?\"", (char *) NULL);
1262     return TCL_ERROR;
1263     }
1264     if (argc == 3) {
1265     if (wmPtr->sizeHintsFlags & PAspect) {
1266     char buf[TCL_INTEGER_SPACE * 4];
1267    
1268     sprintf(buf, "%d %d %d %d", wmPtr->minAspect.x,
1269     wmPtr->minAspect.y, wmPtr->maxAspect.x,
1270     wmPtr->maxAspect.y);
1271     Tcl_SetResult(interp, buf, TCL_VOLATILE);
1272     }
1273     return TCL_OK;
1274     }
1275     if (*argv[3] == '\0') {
1276     wmPtr->sizeHintsFlags &= ~PAspect;
1277     } else {
1278     if ((Tcl_GetInt(interp, argv[3], &numer1) != TCL_OK)
1279     || (Tcl_GetInt(interp, argv[4], &denom1) != TCL_OK)
1280     || (Tcl_GetInt(interp, argv[5], &numer2) != TCL_OK)
1281     || (Tcl_GetInt(interp, argv[6], &denom2) != TCL_OK)) {
1282     return TCL_ERROR;
1283     }
1284     if ((numer1 <= 0) || (denom1 <= 0) || (numer2 <= 0) ||
1285     (denom2 <= 0)) {
1286     Tcl_SetResult(interp, "aspect number can't be <= 0",
1287     TCL_STATIC);
1288     return TCL_ERROR;
1289     }
1290     wmPtr->minAspect.x = numer1;
1291     wmPtr->minAspect.y = denom1;
1292     wmPtr->maxAspect.x = numer2;
1293     wmPtr->maxAspect.y = denom2;
1294     wmPtr->sizeHintsFlags |= PAspect;
1295     }
1296     goto updateGeom;
1297     } else if ((c == 'c') && (strncmp(argv[1], "client", length) == 0)
1298     && (length >= 2)) {
1299     if ((argc != 3) && (argc != 4)) {
1300     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1301     argv[0], " client window ?name?\"",
1302     (char *) NULL);
1303     return TCL_ERROR;
1304     }
1305     if (argc == 3) {
1306     if (wmPtr->clientMachine != NULL) {
1307     Tcl_SetResult(interp, wmPtr->clientMachine, TCL_STATIC);
1308     }
1309     return TCL_OK;
1310     }
1311     if (argv[3][0] == 0) {
1312     if (wmPtr->clientMachine != NULL) {
1313     ckfree((char *) wmPtr->clientMachine);
1314     wmPtr->clientMachine = NULL;
1315     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
1316     XDeleteProperty(winPtr->display, winPtr->window,
1317     Tk_InternAtom((Tk_Window) winPtr,
1318     "WM_CLIENT_MACHINE"));
1319     }
1320     }
1321     return TCL_OK;
1322     }
1323     if (wmPtr->clientMachine != NULL) {
1324     ckfree((char *) wmPtr->clientMachine);
1325     }
1326     wmPtr->clientMachine = (char *)
1327     ckalloc((unsigned) (strlen(argv[3]) + 1));
1328     strcpy(wmPtr->clientMachine, argv[3]);
1329     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
1330     XTextProperty textProp;
1331     if (XStringListToTextProperty(&wmPtr->clientMachine, 1, &textProp)
1332     != 0) {
1333     XSetWMClientMachine(winPtr->display, winPtr->window,
1334     &textProp);
1335     XFree((char *) textProp.value);
1336     }
1337     }
1338     } else if ((c == 'c') && (strncmp(argv[1], "colormapwindows", length) == 0)
1339     && (length >= 3)) {
1340     TkWindow **cmapList;
1341     TkWindow *winPtr2;
1342     int i, windowArgc, gotToplevel;
1343     char **windowArgv;
1344    
1345     if ((argc != 3) && (argc != 4)) {
1346     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1347     argv[0], " colormapwindows window ?windowList?\"",
1348     (char *) NULL);
1349     return TCL_ERROR;
1350     }
1351     if (argc == 3) {
1352     Tk_MakeWindowExist((Tk_Window) winPtr);
1353     for (i = 0; i < wmPtr->cmapCount; i++) {
1354     if ((i == (wmPtr->cmapCount-1))
1355     && (wmPtr->flags & WM_ADDED_TOPLEVEL_COLORMAP)) {
1356     break;
1357     }
1358     Tcl_AppendElement(interp, wmPtr->cmapList[i]->pathName);
1359     }
1360     return TCL_OK;
1361     }
1362     if (Tcl_SplitList(interp, argv[3], &windowArgc, &windowArgv)
1363     != TCL_OK) {
1364     return TCL_ERROR;
1365     }
1366     cmapList = (TkWindow **) ckalloc((unsigned)
1367     ((windowArgc+1)*sizeof(TkWindow*)));
1368     for (i = 0; i < windowArgc; i++) {
1369     winPtr2 = (TkWindow *) Tk_NameToWindow(interp, windowArgv[i],
1370     tkwin);
1371     if (winPtr2 == NULL) {
1372     ckfree((char *) cmapList);
1373     ckfree((char *) windowArgv);
1374     return TCL_ERROR;
1375     }
1376     if (winPtr2 == winPtr) {
1377     gotToplevel = 1;
1378     }
1379     if (winPtr2->window == None) {
1380     Tk_MakeWindowExist((Tk_Window) winPtr2);
1381     }
1382     cmapList[i] = winPtr2;
1383     }
1384     if (!gotToplevel) {
1385     wmPtr->flags |= WM_ADDED_TOPLEVEL_COLORMAP;
1386     cmapList[windowArgc] = winPtr;
1387     windowArgc++;
1388     } else {
1389     wmPtr->flags &= ~WM_ADDED_TOPLEVEL_COLORMAP;
1390     }
1391     wmPtr->flags |= WM_COLORMAPS_EXPLICIT;
1392     if (wmPtr->cmapList != NULL) {
1393     ckfree((char *)wmPtr->cmapList);
1394     }
1395     wmPtr->cmapList = cmapList;
1396     wmPtr->cmapCount = windowArgc;
1397     ckfree((char *) windowArgv);
1398    
1399     /*
1400     * Now we need to force the updated colormaps to be installed.
1401     */
1402    
1403     if (wmPtr == winPtr->dispPtr->foregroundWmPtr) {
1404     InstallColormaps(wmPtr->wrapper, WM_QUERYNEWPALETTE, 1);
1405     } else {
1406     InstallColormaps(wmPtr->wrapper, WM_PALETTECHANGED, 0);
1407     }
1408     return TCL_OK;
1409     } else if ((c == 'c') && (strncmp(argv[1], "command", length) == 0)
1410     && (length >= 3)) {
1411     int cmdArgc;
1412     char **cmdArgv;
1413    
1414     if ((argc != 3) && (argc != 4)) {
1415     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1416     argv[0], " command window ?value?\"",
1417     (char *) NULL);
1418     return TCL_ERROR;
1419     }
1420     if (argc == 3) {
1421     if (wmPtr->cmdArgv != NULL) {
1422     Tcl_SetResult(interp,
1423     Tcl_Merge(wmPtr->cmdArgc, wmPtr->cmdArgv),
1424     TCL_DYNAMIC);
1425     }
1426     return TCL_OK;
1427     }
1428     if (argv[3][0] == 0) {
1429     if (wmPtr->cmdArgv != NULL) {
1430     ckfree((char *) wmPtr->cmdArgv);
1431     wmPtr->cmdArgv = NULL;
1432     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
1433     XDeleteProperty(winPtr->display, winPtr->window,
1434     Tk_InternAtom((Tk_Window) winPtr, "WM_COMMAND"));
1435     }
1436     }
1437     return TCL_OK;
1438     }
1439     if (Tcl_SplitList(interp, argv[3], &cmdArgc, &cmdArgv) != TCL_OK) {
1440     return TCL_ERROR;
1441     }
1442     if (wmPtr->cmdArgv != NULL) {
1443     ckfree((char *) wmPtr->cmdArgv);
1444     }
1445     wmPtr->cmdArgc = cmdArgc;
1446     wmPtr->cmdArgv = cmdArgv;
1447     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
1448     XSetCommand(winPtr->display, winPtr->window, cmdArgv, cmdArgc);
1449     }
1450     } else if ((c == 'd') && (strncmp(argv[1], "deiconify", length) == 0)) {
1451     if (argc != 3) {
1452     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1453     argv[0], " deiconify window\"", (char *) NULL);
1454     return TCL_ERROR;
1455     }
1456     if (wmPtr->iconFor != NULL) {
1457     Tcl_AppendResult(interp, "can't deiconify ", argv[2],
1458     ": it is an icon for ", winPtr->pathName, (char *) NULL);
1459     return TCL_ERROR;
1460     }
1461     if (winPtr->flags & TK_EMBEDDED) {
1462     Tcl_AppendResult(interp, "can't deiconify ", winPtr->pathName,
1463     ": it is an embedded window", (char *) NULL);
1464     return TCL_ERROR;
1465     }
1466     /*
1467     * If WM_UPDATE_PENDING is true, a pending UpdateGeometryInfo may
1468     * need to be called first to update a withdrew toplevel's geometry
1469     * before it is deiconified by TkpWmSetState.
1470     * Don't bother if we've never been mapped.
1471     */
1472     if ((wmPtr->flags & WM_UPDATE_PENDING) &&
1473     !(wmPtr->flags & WM_NEVER_MAPPED)) {
1474     Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
1475     UpdateGeometryInfo((ClientData) winPtr);
1476     }
1477    
1478     /*
1479     * If we were in the ZoomState (maximized), 'wm deiconify'
1480     * should not cause the window to shrink
1481     */
1482     if (wmPtr->hints.initial_state == ZoomState) {
1483     TkpWmSetState(winPtr, ZoomState);
1484     } else {
1485     TkpWmSetState(winPtr, NormalState);
1486     }
1487    
1488     /*
1489     * Follow Windows-like style here, raising the window to the top.
1490     * Do this when idle, to not cause an unrefreshable window to
1491     * get mapped.
1492     */
1493     Tcl_DoWhenIdle(RaiseWinWhenIdle, (ClientData) winPtr);
1494     } else if ((c == 'f') && (strncmp(argv[1], "focusmodel", length) == 0)
1495     && (length >= 2)) {
1496     if ((argc != 3) && (argc != 4)) {
1497     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1498     argv[0], " focusmodel window ?active|passive?\"",
1499     (char *) NULL);
1500     return TCL_ERROR;
1501     }
1502     if (argc == 3) {
1503     Tcl_SetResult(interp, (wmPtr->hints.input ? "passive" : "active"),
1504     TCL_STATIC);
1505     return TCL_OK;
1506     }
1507     c = argv[3][0];
1508     length = strlen(argv[3]);
1509     if ((c == 'a') && (strncmp(argv[3], "active", length) == 0)) {
1510     wmPtr->hints.input = False;
1511     } else if ((c == 'p') && (strncmp(argv[3], "passive", length) == 0)) {
1512     wmPtr->hints.input = True;
1513     } else {
1514     Tcl_AppendResult(interp, "bad argument \"", argv[3],
1515     "\": must be active or passive", (char *) NULL);
1516     return TCL_ERROR;
1517     }
1518     } else if ((c == 'f') && (strncmp(argv[1], "frame", length) == 0)
1519     && (length >= 2)) {
1520     HWND hwnd;
1521     char buf[TCL_INTEGER_SPACE];
1522    
1523     if (argc != 3) {
1524     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1525     argv[0], " frame window\"", (char *) NULL);
1526     return TCL_ERROR;
1527     }
1528     if (Tk_WindowId((Tk_Window) winPtr) == None) {
1529     Tk_MakeWindowExist((Tk_Window) winPtr);
1530     }
1531     hwnd = wmPtr->wrapper;
1532     if (hwnd == NULL) {
1533     hwnd = Tk_GetHWND(Tk_WindowId((Tk_Window) winPtr));
1534     }
1535     sprintf(buf, "0x%x", (unsigned int) hwnd);
1536     Tcl_SetResult(interp, buf, TCL_VOLATILE);
1537     } else if ((c == 'g') && (strncmp(argv[1], "geometry", length) == 0)
1538     && (length >= 2)) {
1539     char xSign, ySign;
1540     int width, height;
1541    
1542     if ((argc != 3) && (argc != 4)) {
1543     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1544     argv[0], " geometry window ?newGeometry?\"",
1545     (char *) NULL);
1546     return TCL_ERROR;
1547     }
1548     if (argc == 3) {
1549     char buf[16 + TCL_INTEGER_SPACE * 4];
1550    
1551     xSign = (wmPtr->flags & WM_NEGATIVE_X) ? '-' : '+';
1552     ySign = (wmPtr->flags & WM_NEGATIVE_Y) ? '-' : '+';
1553     if (wmPtr->gridWin != NULL) {
1554     width = wmPtr->reqGridWidth + (winPtr->changes.width
1555     - winPtr->reqWidth)/wmPtr->widthInc;
1556     height = wmPtr->reqGridHeight + (winPtr->changes.height
1557     - winPtr->reqHeight)/wmPtr->heightInc;
1558     } else {
1559     width = winPtr->changes.width;
1560     height = winPtr->changes.height;
1561     }
1562     sprintf(buf, "%dx%d%c%d%c%d", width, height, xSign, wmPtr->x,
1563     ySign, wmPtr->y);
1564     Tcl_SetResult(interp, buf, TCL_VOLATILE);
1565     return TCL_OK;
1566     }
1567     if (*argv[3] == '\0') {
1568     wmPtr->width = -1;
1569     wmPtr->height = -1;
1570     goto updateGeom;
1571     }
1572     return ParseGeometry(interp, argv[3], winPtr);
1573     } else if ((c == 'g') && (strncmp(argv[1], "grid", length) == 0)
1574     && (length >= 3)) {
1575     int reqWidth, reqHeight, widthInc, heightInc;
1576    
1577     if ((argc != 3) && (argc != 7)) {
1578     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1579     argv[0], " grid window ?baseWidth baseHeight ",
1580     "widthInc heightInc?\"", (char *) NULL);
1581     return TCL_ERROR;
1582     }
1583     if (argc == 3) {
1584     if (wmPtr->sizeHintsFlags & PBaseSize) {
1585     char buf[TCL_INTEGER_SPACE * 4];
1586    
1587     sprintf(buf, "%d %d %d %d", wmPtr->reqGridWidth,
1588     wmPtr->reqGridHeight, wmPtr->widthInc,
1589     wmPtr->heightInc);
1590     Tcl_SetResult(interp, buf, TCL_VOLATILE);
1591     }
1592     return TCL_OK;
1593     }
1594     if (*argv[3] == '\0') {
1595     /*
1596     * Turn off gridding and reset the width and height
1597     * to make sense as ungridded numbers.
1598     */
1599    
1600     wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc);
1601     if (wmPtr->width != -1) {
1602     wmPtr->width = winPtr->reqWidth + (wmPtr->width
1603     - wmPtr->reqGridWidth)*wmPtr->widthInc;
1604     wmPtr->height = winPtr->reqHeight + (wmPtr->height
1605     - wmPtr->reqGridHeight)*wmPtr->heightInc;
1606     }
1607     wmPtr->widthInc = 1;
1608     wmPtr->heightInc = 1;
1609     } else {
1610     if ((Tcl_GetInt(interp, argv[3], &reqWidth) != TCL_OK)
1611     || (Tcl_GetInt(interp, argv[4], &reqHeight) != TCL_OK)
1612     || (Tcl_GetInt(interp, argv[5], &widthInc) != TCL_OK)
1613     || (Tcl_GetInt(interp, argv[6], &heightInc) != TCL_OK)) {
1614     return TCL_ERROR;
1615     }
1616     if (reqWidth < 0) {
1617     Tcl_SetResult(interp, "baseWidth can't be < 0", TCL_STATIC);
1618     return TCL_ERROR;
1619     }
1620     if (reqHeight < 0) {
1621     Tcl_SetResult(interp, "baseHeight can't be < 0", TCL_STATIC);
1622     return TCL_ERROR;
1623     }
1624     if (widthInc < 0) {
1625     Tcl_SetResult(interp, "widthInc can't be < 0", TCL_STATIC);
1626     return TCL_ERROR;
1627     }
1628     if (heightInc < 0) {
1629     Tcl_SetResult(interp, "heightInc can't be < 0", TCL_STATIC);
1630     return TCL_ERROR;
1631     }
1632     Tk_SetGrid((Tk_Window) winPtr, reqWidth, reqHeight, widthInc,
1633     heightInc);
1634     }
1635     goto updateGeom;
1636     } else if ((c == 'g') && (strncmp(argv[1], "group", length) == 0)
1637     && (length >= 3)) {
1638     Tk_Window tkwin2;
1639    
1640     if ((argc != 3) && (argc != 4)) {
1641     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1642     argv[0], " group window ?pathName?\"",
1643     (char *) NULL);
1644     return TCL_ERROR;
1645     }
1646     if (argc == 3) {
1647     if (wmPtr->hints.flags & WindowGroupHint) {
1648     Tcl_SetResult(interp, wmPtr->leaderName, TCL_STATIC);
1649     }
1650     return TCL_OK;
1651     }
1652     if (*argv[3] == '\0') {
1653     wmPtr->hints.flags &= ~WindowGroupHint;
1654     if (wmPtr->leaderName != NULL) {
1655     ckfree(wmPtr->leaderName);
1656     }
1657     wmPtr->leaderName = NULL;
1658     } else {
1659     tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin);
1660     if (tkwin2 == NULL) {
1661     return TCL_ERROR;
1662     }
1663     Tk_MakeWindowExist(tkwin2);
1664     wmPtr->hints.window_group = Tk_WindowId(tkwin2);
1665     wmPtr->hints.flags |= WindowGroupHint;
1666     wmPtr->leaderName = ckalloc((unsigned) (strlen(argv[3])+1));
1667     strcpy(wmPtr->leaderName, argv[3]);
1668     }
1669     } else if ((c == 'i') && (strncmp(argv[1], "iconbitmap", length) == 0)
1670     && (length >= 5)) {
1671     Pixmap pixmap;
1672    
1673     if ((argc != 3) && (argc != 4)) {
1674     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1675     argv[0], " iconbitmap window ?bitmap?\"",
1676     (char *) NULL);
1677     return TCL_ERROR;
1678     }
1679     if (argc == 3) {
1680     if (wmPtr->hints.flags & IconPixmapHint) {
1681     Tcl_SetResult(interp,
1682     Tk_NameOfBitmap(winPtr->display,
1683     wmPtr->hints.icon_pixmap), TCL_STATIC);
1684     }
1685     return TCL_OK;
1686     }
1687     if (*argv[3] == '\0') {
1688     if (wmPtr->hints.icon_pixmap != None) {
1689     Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap);
1690     }
1691     wmPtr->hints.flags &= ~IconPixmapHint;
1692     } else {
1693     pixmap = Tk_GetBitmap(interp, (Tk_Window) winPtr,
1694     Tk_GetUid(argv[3]));
1695     if (pixmap == None) {
1696     return TCL_ERROR;
1697     }
1698     wmPtr->hints.icon_pixmap = pixmap;
1699     wmPtr->hints.flags |= IconPixmapHint;
1700     }
1701     } else if ((c == 'i') && (strncmp(argv[1], "iconify", length) == 0)
1702     && (length >= 5)) {
1703     if (argc != 3) {
1704     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1705     argv[0], " iconify window\"", (char *) NULL);
1706     return TCL_ERROR;
1707     }
1708     if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) {
1709     Tcl_AppendResult(interp, "can't iconify \"", winPtr->pathName,
1710     "\": override-redirect flag is set", (char *) NULL);
1711     return TCL_ERROR;
1712     }
1713     if (wmPtr->masterPtr != NULL) {
1714     Tcl_AppendResult(interp, "can't iconify \"", winPtr->pathName,
1715     "\": it is a transient", (char *) NULL);
1716     return TCL_ERROR;
1717     }
1718     if (wmPtr->iconFor != NULL) {
1719     Tcl_AppendResult(interp, "can't iconify ", argv[2],
1720     ": it is an icon for ", winPtr->pathName, (char *) NULL);
1721     return TCL_ERROR;
1722     }
1723     if (winPtr->flags & TK_EMBEDDED) {
1724     Tcl_AppendResult(interp, "can't iconify ", winPtr->pathName,
1725     ": it is an embedded window", (char *) NULL);
1726     return TCL_ERROR;
1727     }
1728     TkpWmSetState(winPtr, IconicState);
1729     } else if ((c == 'i') && (strncmp(argv[1], "iconmask", length) == 0)
1730     && (length >= 5)) {
1731     Pixmap pixmap;
1732    
1733     if ((argc != 3) && (argc != 4)) {
1734     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1735     argv[0], " iconmask window ?bitmap?\"",
1736     (char *) NULL);
1737     return TCL_ERROR;
1738     }
1739     if (argc == 3) {
1740     if (wmPtr->hints.flags & IconMaskHint) {
1741     Tcl_SetResult(interp,
1742     Tk_NameOfBitmap(winPtr->display, wmPtr->hints.icon_mask),
1743     TCL_STATIC);
1744     }
1745     return TCL_OK;
1746     }
1747     if (*argv[3] == '\0') {
1748     if (wmPtr->hints.icon_mask != None) {
1749     Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_mask);
1750     }
1751     wmPtr->hints.flags &= ~IconMaskHint;
1752     } else {
1753     pixmap = Tk_GetBitmap(interp, tkwin, Tk_GetUid(argv[3]));
1754     if (pixmap == None) {
1755     return TCL_ERROR;
1756     }
1757     wmPtr->hints.icon_mask = pixmap;
1758     wmPtr->hints.flags |= IconMaskHint;
1759     }
1760     } else if ((c == 'i') && (strncmp(argv[1], "iconname", length) == 0)
1761     && (length >= 5)) {
1762     if (argc > 4) {
1763     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1764     argv[0], " iconname window ?newName?\"", (char *) NULL);
1765     return TCL_ERROR;
1766     }
1767     if (argc == 3) {
1768     Tcl_SetResult(interp,
1769     ((wmPtr->iconName != NULL) ? wmPtr->iconName : ""),
1770     TCL_STATIC);
1771     return TCL_OK;
1772     } else {
1773     wmPtr->iconName = Tk_GetUid(argv[3]);
1774     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
1775     XSetIconName(winPtr->display, winPtr->window, wmPtr->iconName);
1776     }
1777     }
1778     } else if ((c == 'i') && (strncmp(argv[1], "iconposition", length) == 0)
1779     && (length >= 5)) {
1780     int x, y;
1781    
1782     if ((argc != 3) && (argc != 5)) {
1783     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1784     argv[0], " iconposition window ?x y?\"",
1785     (char *) NULL);
1786     return TCL_ERROR;
1787     }
1788     if (argc == 3) {
1789     if (wmPtr->hints.flags & IconPositionHint) {
1790     char buf[TCL_INTEGER_SPACE * 2];
1791    
1792     sprintf(buf, "%d %d", wmPtr->hints.icon_x,
1793     wmPtr->hints.icon_y);
1794     Tcl_SetResult(interp, buf, TCL_VOLATILE);
1795     }
1796     return TCL_OK;
1797     }
1798     if (*argv[3] == '\0') {
1799     wmPtr->hints.flags &= ~IconPositionHint;
1800     } else {
1801     if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK)
1802     || (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)){
1803     return TCL_ERROR;
1804     }
1805     wmPtr->hints.icon_x = x;
1806     wmPtr->hints.icon_y = y;
1807     wmPtr->hints.flags |= IconPositionHint;
1808     }
1809     } else if ((c == 'i') && (strncmp(argv[1], "iconwindow", length) == 0)
1810     && (length >= 5)) {
1811     Tk_Window tkwin2;
1812     WmInfo *wmPtr2;
1813     XSetWindowAttributes atts;
1814    
1815     if ((argc != 3) && (argc != 4)) {
1816     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1817     argv[0], " iconwindow window ?pathName?\"",
1818     (char *) NULL);
1819     return TCL_ERROR;
1820     }
1821     if (argc == 3) {
1822     if (wmPtr->icon != NULL) {
1823     Tcl_SetResult(interp, Tk_PathName(wmPtr->icon), TCL_STATIC);
1824     }
1825     return TCL_OK;
1826     }
1827     if (*argv[3] == '\0') {
1828     wmPtr->hints.flags &= ~IconWindowHint;
1829     if (wmPtr->icon != NULL) {
1830     /*
1831     * Let the window use button events again, then remove
1832     * it as icon window.
1833     */
1834    
1835     atts.event_mask = Tk_Attributes(wmPtr->icon)->event_mask
1836     | ButtonPressMask;
1837     Tk_ChangeWindowAttributes(wmPtr->icon, CWEventMask, &atts);
1838     wmPtr2 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
1839     wmPtr2->iconFor = NULL;
1840     wmPtr2->hints.initial_state = WithdrawnState;
1841     }
1842     wmPtr->icon = NULL;
1843     } else {
1844     tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin);
1845     if (tkwin2 == NULL) {
1846     return TCL_ERROR;
1847     }
1848     if (!Tk_IsTopLevel(tkwin2)) {
1849     Tcl_AppendResult(interp, "can't use ", argv[3],
1850     " as icon window: not at top level", (char *) NULL);
1851     return TCL_ERROR;
1852     }
1853     wmPtr2 = ((TkWindow *) tkwin2)->wmInfoPtr;
1854     if (wmPtr2->iconFor != NULL) {
1855     Tcl_AppendResult(interp, argv[3], " is already an icon for ",
1856     Tk_PathName(wmPtr2->iconFor), (char *) NULL);
1857     return TCL_ERROR;
1858     }
1859     if (wmPtr->icon != NULL) {
1860     WmInfo *wmPtr3 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
1861     wmPtr3->iconFor = NULL;
1862    
1863     /*
1864     * Let the window use button events again.
1865     */
1866    
1867     atts.event_mask = Tk_Attributes(wmPtr->icon)->event_mask
1868     | ButtonPressMask;
1869     Tk_ChangeWindowAttributes(wmPtr->icon, CWEventMask, &atts);
1870     }
1871    
1872     /*
1873     * Disable button events in the icon window: some window
1874     * managers (like olvwm) want to get the events themselves,
1875     * but X only allows one application at a time to receive
1876     * button events for a window.
1877     */
1878    
1879     atts.event_mask = Tk_Attributes(tkwin2)->event_mask
1880     & ~ButtonPressMask;
1881     Tk_ChangeWindowAttributes(tkwin2, CWEventMask, &atts);
1882     Tk_MakeWindowExist(tkwin2);
1883     wmPtr->hints.icon_window = Tk_WindowId(tkwin2);
1884     wmPtr->hints.flags |= IconWindowHint;
1885     wmPtr->icon = tkwin2;
1886     wmPtr2->iconFor = (Tk_Window) winPtr;
1887     if (!(wmPtr2->flags & WM_NEVER_MAPPED)) {
1888     if (XWithdrawWindow(Tk_Display(tkwin2), Tk_WindowId(tkwin2),
1889     Tk_ScreenNumber(tkwin2)) == 0) {
1890     Tcl_SetResult(interp,
1891     "couldn't send withdraw message to window manager",
1892     TCL_STATIC);
1893     return TCL_ERROR;
1894     }
1895     }
1896     }
1897     } else if ((c == 'm') && (strncmp(argv[1], "maxsize", length) == 0)
1898     && (length >= 2)) {
1899     int width, height;
1900     if ((argc != 3) && (argc != 5)) {
1901     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1902     argv[0], " maxsize window ?width height?\"",
1903     (char *) NULL);
1904     return TCL_ERROR;
1905     }
1906     if (argc == 3) {
1907     char buf[TCL_INTEGER_SPACE * 2];
1908    
1909     GetMaxSize(wmPtr, &width, &height);
1910     sprintf(buf, "%d %d", width, height);
1911     Tcl_SetResult(interp, buf, TCL_VOLATILE);
1912     return TCL_OK;
1913     }
1914     if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK)
1915     || (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) {
1916     return TCL_ERROR;
1917     }
1918     wmPtr->maxWidth = width;
1919     wmPtr->maxHeight = height;
1920     goto updateGeom;
1921     } else if ((c == 'm') && (strncmp(argv[1], "minsize", length) == 0)
1922     && (length >= 2)) {
1923     int width, height;
1924     if ((argc != 3) && (argc != 5)) {
1925     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1926     argv[0], " minsize window ?width height?\"",
1927     (char *) NULL);
1928     return TCL_ERROR;
1929     }
1930     if (argc == 3) {
1931     char buf[TCL_INTEGER_SPACE * 2];
1932    
1933     GetMinSize(wmPtr, &width, &height);
1934     sprintf(buf, "%d %d", width, height);
1935     Tcl_SetResult(interp, buf, TCL_VOLATILE);
1936     return TCL_OK;
1937     }
1938     if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK)
1939     || (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) {
1940     return TCL_ERROR;
1941     }
1942     wmPtr->minWidth = width;
1943     wmPtr->minHeight = height;
1944     goto updateGeom;
1945     } else if ((c == 'o')
1946     && (strncmp(argv[1], "overrideredirect", length) == 0)) {
1947     int boolean, curValue;
1948     XSetWindowAttributes atts;
1949    
1950     if ((argc != 3) && (argc != 4)) {
1951     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1952     argv[0], " overrideredirect window ?boolean?\"",
1953     (char *) NULL);
1954     return TCL_ERROR;
1955     }
1956     curValue = Tk_Attributes((Tk_Window) winPtr)->override_redirect;
1957     if (argc == 3) {
1958     Tcl_SetBooleanObj(Tcl_GetObjResult(interp), curValue);
1959     return TCL_OK;
1960     }
1961     if (Tcl_GetBoolean(interp, argv[3], &boolean) != TCL_OK) {
1962     return TCL_ERROR;
1963     }
1964     if (curValue != boolean) {
1965     /*
1966     * Only do this if we are really changing value, because it
1967     * causes some funky stuff to occur
1968     */
1969     atts.override_redirect = (boolean) ? True : False;
1970     Tk_ChangeWindowAttributes((Tk_Window) winPtr, CWOverrideRedirect,
1971     &atts);
1972     if (!(wmPtr->flags & (WM_NEVER_MAPPED)
1973     && !(winPtr->flags & TK_EMBEDDED))) {
1974     UpdateWrapper(winPtr);
1975     }
1976     }
1977     } else if ((c == 'p') && (strncmp(argv[1], "positionfrom", length) == 0)
1978     && (length >= 2)) {
1979     if ((argc != 3) && (argc != 4)) {
1980     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1981     argv[0], " positionfrom window ?user/program?\"",
1982     (char *) NULL);
1983     return TCL_ERROR;
1984     }
1985     if (argc == 3) {
1986     if (wmPtr->sizeHintsFlags & USPosition) {
1987     Tcl_SetResult(interp, "user", TCL_STATIC);
1988     } else if (wmPtr->sizeHintsFlags & PPosition) {
1989     Tcl_SetResult(interp, "program", TCL_STATIC);
1990     }
1991     return TCL_OK;
1992     }
1993     if (*argv[3] == '\0') {
1994     wmPtr->sizeHintsFlags &= ~(USPosition|PPosition);
1995     } else {
1996     c = argv[3][0];
1997     length = strlen(argv[3]);
1998     if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) {
1999     wmPtr->sizeHintsFlags &= ~PPosition;
2000     wmPtr->sizeHintsFlags |= USPosition;
2001     } else if ((c == 'p')
2002     && (strncmp(argv[3], "program", length) == 0)) {
2003     wmPtr->sizeHintsFlags &= ~USPosition;
2004     wmPtr->sizeHintsFlags |= PPosition;
2005     } else {
2006     Tcl_AppendResult(interp, "bad argument \"", argv[3],
2007     "\": must be program or user", (char *) NULL);
2008     return TCL_ERROR;
2009     }
2010     }
2011     goto updateGeom;
2012     } else if ((c == 'p') && (strncmp(argv[1], "protocol", length) == 0)
2013     && (length >= 2)) {
2014     register ProtocolHandler *protPtr, *prevPtr;
2015     Atom protocol;
2016     int cmdLength;
2017    
2018     if ((argc < 3) || (argc > 5)) {
2019     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
2020     argv[0], " protocol window ?name? ?command?\"",
2021     (char *) NULL);
2022     return TCL_ERROR;
2023     }
2024     if (argc == 3) {
2025     /*
2026     * Return a list of all defined protocols for the window.
2027     */
2028     for (protPtr = wmPtr->protPtr; protPtr != NULL;
2029     protPtr = protPtr->nextPtr) {
2030     Tcl_AppendElement(interp,
2031     Tk_GetAtomName((Tk_Window) winPtr, protPtr->protocol));
2032     }
2033     return TCL_OK;
2034     }
2035     protocol = Tk_InternAtom((Tk_Window) winPtr, argv[3]);
2036     if (argc == 4) {
2037     /*
2038     * Return the command to handle a given protocol.
2039     */
2040     for (protPtr = wmPtr->protPtr; protPtr != NULL;
2041     protPtr = protPtr->nextPtr) {
2042     if (protPtr->protocol == protocol) {
2043     Tcl_SetResult(interp, protPtr->command, TCL_STATIC);
2044     return TCL_OK;
2045     }
2046     }
2047     return TCL_OK;
2048     }
2049    
2050     /*
2051     * Delete any current protocol handler, then create a new
2052     * one with the specified command, unless the command is
2053     * empty.
2054     */
2055    
2056     for (protPtr = wmPtr->protPtr, prevPtr = NULL; protPtr != NULL;
2057     prevPtr = protPtr, protPtr = protPtr->nextPtr) {
2058     if (protPtr->protocol == protocol) {
2059     if (prevPtr == NULL) {
2060     wmPtr->protPtr = protPtr->nextPtr;
2061     } else {
2062     prevPtr->nextPtr = protPtr->nextPtr;
2063     }
2064     Tcl_EventuallyFree((ClientData) protPtr, TCL_DYNAMIC);
2065     break;
2066     }
2067     }
2068     cmdLength = strlen(argv[4]);
2069     if (cmdLength > 0) {
2070     protPtr = (ProtocolHandler *) ckalloc(HANDLER_SIZE(cmdLength));
2071     protPtr->protocol = protocol;
2072     protPtr->nextPtr = wmPtr->protPtr;
2073     wmPtr->protPtr = protPtr;
2074     protPtr->interp = interp;
2075     strcpy(protPtr->command, argv[4]);
2076     }
2077     } else if ((c == 'r') && (strncmp(argv[1], "resizable", length) == 0)) {
2078     int width, height;
2079    
2080     if ((argc != 3) && (argc != 5)) {
2081     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
2082     argv[0], " resizable window ?width height?\"",
2083     (char *) NULL);
2084     return TCL_ERROR;
2085     }
2086     if (argc == 3) {
2087     char buf[TCL_INTEGER_SPACE * 2];
2088    
2089     sprintf(buf, "%d %d",
2090     (wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) ? 0 : 1,
2091     (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE) ? 0 : 1);
2092     Tcl_SetResult(interp, buf, TCL_VOLATILE);
2093     return TCL_OK;
2094     }
2095     if ((Tcl_GetBoolean(interp, argv[3], &width) != TCL_OK)
2096     || (Tcl_GetBoolean(interp, argv[4], &height) != TCL_OK)) {
2097     return TCL_ERROR;
2098     }
2099     if (width) {
2100     wmPtr->flags &= ~WM_WIDTH_NOT_RESIZABLE;
2101     } else {
2102     wmPtr->flags |= WM_WIDTH_NOT_RESIZABLE;
2103     }
2104     if (height) {
2105     wmPtr->flags &= ~WM_HEIGHT_NOT_RESIZABLE;
2106     } else {
2107     wmPtr->flags |= WM_HEIGHT_NOT_RESIZABLE;
2108     }
2109     if (!((wmPtr->flags & WM_NEVER_MAPPED)
2110     && !(winPtr->flags & TK_EMBEDDED))) {
2111     UpdateWrapper(winPtr);
2112     }
2113     goto updateGeom;
2114     } else if ((c == 's') && (strncmp(argv[1], "sizefrom", length) == 0)
2115     && (length >= 2)) {
2116     if ((argc != 3) && (argc != 4)) {
2117     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
2118     argv[0], " sizefrom window ?user|program?\"",
2119     (char *) NULL);
2120     return TCL_ERROR;
2121     }
2122     if (argc == 3) {
2123     if (wmPtr->sizeHintsFlags & USSize) {
2124     Tcl_SetResult(interp, "user", TCL_STATIC);
2125     } else if (wmPtr->sizeHintsFlags & PSize) {
2126     Tcl_SetResult(interp, "program", TCL_STATIC);
2127     }
2128     return TCL_OK;
2129     }
2130     if (*argv[3] == '\0') {
2131     wmPtr->sizeHintsFlags &= ~(USSize|PSize);
2132     } else {
2133     c = argv[3][0];
2134     length = strlen(argv[3]);
2135     if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) {
2136     wmPtr->sizeHintsFlags &= ~PSize;
2137     wmPtr->sizeHintsFlags |= USSize;
2138     } else if ((c == 'p')
2139     && (strncmp(argv[3], "program", length) == 0)) {
2140     wmPtr->sizeHintsFlags &= ~USSize;
2141     wmPtr->sizeHintsFlags |= PSize;
2142     } else {
2143     Tcl_AppendResult(interp, "bad argument \"", argv[3],
2144     "\": must be program or user", (char *) NULL);
2145     return TCL_ERROR;
2146     }
2147     }
2148     goto updateGeom;
2149     } else if ((c == 's') && (strncmp(argv[1], "state", length) == 0)
2150     && (length >= 2)) {
2151     if ((argc < 3) || (argc > 4)) {
2152     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
2153     argv[0], " state window ?state?\"", (char *) NULL);
2154     return TCL_ERROR;
2155     }
2156     if (argc == 4) {
2157     if (wmPtr->iconFor != NULL) {
2158     Tcl_AppendResult(interp, "can't change state of ", argv[2],
2159     ": it is an icon for ", Tk_PathName(wmPtr->iconFor),
2160     (char *) NULL);
2161     return TCL_ERROR;
2162     }
2163     if (winPtr->flags & TK_EMBEDDED) {
2164     Tcl_AppendResult(interp, "can't change state of ",
2165     winPtr->pathName, ": it is an embedded window",
2166     (char *) NULL);
2167     return TCL_ERROR;
2168     }
2169    
2170     c = argv[3][0];
2171     length = strlen(argv[3]);
2172    
2173     if ((c == 'n') && (strncmp(argv[3], "normal", length) == 0)) {
2174     TkpWmSetState(winPtr, NormalState);
2175     /*
2176     * This varies from 'wm deiconify' because it does not
2177     * force the window to be raised and receive focus
2178     */
2179     } else if ((c == 'i')
2180     && (strncmp(argv[3], "iconic", length) == 0)) {
2181     if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) {
2182     Tcl_AppendResult(interp, "can't iconify \"",
2183     winPtr->pathName,
2184     "\": override-redirect flag is set",
2185     (char *) NULL);
2186     return TCL_ERROR;
2187     }
2188     if (wmPtr->masterPtr != NULL) {
2189     Tcl_AppendResult(interp, "can't iconify \"",
2190     winPtr->pathName,
2191     "\": it is a transient", (char *) NULL);
2192     return TCL_ERROR;
2193     }
2194     TkpWmSetState(winPtr, IconicState);
2195     } else if ((c == 'w')
2196     && (strncmp(argv[3], "withdrawn", length) == 0)) {
2197     TkpWmSetState(winPtr, WithdrawnState);
2198     } else if ((c == 'z')
2199     && (strncmp(argv[3], "zoomed", length) == 0)) {
2200     TkpWmSetState(winPtr, ZoomState);
2201     } else {
2202     Tcl_AppendResult(interp, "bad argument \"", argv[3],
2203     "\": must be normal, iconic, withdrawn or zoomed",
2204     (char *) NULL);
2205     return TCL_ERROR;
2206     }
2207     } else {
2208     if (wmPtr->iconFor != NULL) {
2209     Tcl_SetResult(interp, "icon", TCL_STATIC);
2210     } else {
2211     switch (wmPtr->hints.initial_state) {
2212     case NormalState:
2213     Tcl_SetResult(interp, "normal", TCL_STATIC);
2214     break;
2215     case IconicState:
2216     Tcl_SetResult(interp, "iconic", TCL_STATIC);
2217     break;
2218     case WithdrawnState:
2219     Tcl_SetResult(interp, "withdrawn", TCL_STATIC);
2220     break;
2221     case ZoomState:
2222     Tcl_SetResult(interp, "zoomed", TCL_STATIC);
2223     break;
2224     }
2225     }
2226     }
2227     } else if ((c == 't') && (strncmp(argv[1], "title", length) == 0)
2228     && (length >= 2)) {
2229     if (argc > 4) {
2230     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
2231     argv[0], " title window ?newTitle?\"", (char *) NULL);
2232     return TCL_ERROR;
2233     }
2234     if (argc == 3) {
2235     Tcl_SetResult(interp, ((wmPtr->titleUid != NULL) ?
2236     wmPtr->titleUid : winPtr->nameUid), TCL_STATIC);
2237     return TCL_OK;
2238     } else {
2239     wmPtr->titleUid = Tk_GetUid(argv[3]);
2240     if (!(wmPtr->flags & WM_NEVER_MAPPED) && wmPtr->wrapper != NULL) {
2241     Tcl_DString titleString;
2242     Tcl_UtfToExternalDString(NULL, wmPtr->titleUid, -1,
2243     &titleString);
2244     SetWindowText(wmPtr->wrapper, Tcl_DStringValue(&titleString));
2245     Tcl_DStringFree(&titleString);
2246     }
2247     }
2248     } else if ((c == 't') && (strncmp(argv[1], "transient", length) == 0)
2249     && (length >= 3)) {
2250     TkWindow *masterPtr = wmPtr->masterPtr;
2251    
2252     if ((argc != 3) && (argc != 4)) {
2253     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
2254     argv[0], " transient window ?master?\"", (char *) NULL);
2255     return TCL_ERROR;
2256     }
2257     if (argc == 3) {
2258     if (masterPtr != NULL) {
2259     Tcl_SetResult(interp, Tk_PathName(masterPtr), TCL_STATIC);
2260     }
2261     return TCL_OK;
2262     }
2263     if (masterPtr != NULL) {
2264     /*
2265     * If we had a master, tell them that we aren't tied
2266     * to them anymore
2267     */
2268     masterPtr->wmInfoPtr->numTransients--;
2269     Tk_DeleteEventHandler((Tk_Window) masterPtr,
2270     VisibilityChangeMask,
2271     WmWaitVisibilityProc, (ClientData) winPtr);
2272     }
2273     if (argv[3][0] == '\0') {
2274     wmPtr->masterPtr = NULL;
2275     } else {
2276     masterPtr = (TkWindow*) Tk_NameToWindow(interp, argv[3], tkwin);
2277     if (masterPtr == NULL) {
2278     return TCL_ERROR;
2279     }
2280     if (masterPtr == winPtr) {
2281     wmPtr->masterPtr = NULL;
2282     } else if (masterPtr != wmPtr->masterPtr) {
2283     Tk_MakeWindowExist((Tk_Window)masterPtr);
2284    
2285     /*
2286     * Ensure that the master window is actually a Tk toplevel.
2287     */
2288    
2289     while (!(masterPtr->flags & TK_TOP_LEVEL)) {
2290     masterPtr = masterPtr->parentPtr;
2291     }
2292     wmPtr->masterPtr = masterPtr;
2293     masterPtr->wmInfoPtr->numTransients++;
2294    
2295     /*
2296     * Bind a visibility event handler to the master window,
2297     * to ensure that when it is mapped, the children will
2298     * have their state set properly.
2299     */
2300    
2301     Tk_CreateEventHandler((Tk_Window) masterPtr,
2302     VisibilityChangeMask,
2303     WmWaitVisibilityProc, (ClientData) winPtr);
2304     }
2305     }
2306     if (!((wmPtr->flags & WM_NEVER_MAPPED)
2307     && !(winPtr->flags & TK_EMBEDDED))) {
2308     UpdateWrapper(winPtr);
2309     }
2310     } else if ((c == 'w') && (strncmp(argv[1], "withdraw", length) == 0)) {
2311     if (argc != 3) {
2312     Tcl_AppendResult(interp, "wrong # arguments: must be \"",
2313     argv[0], " withdraw window\"", (char *) NULL);
2314     return TCL_ERROR;
2315     }
2316     if (wmPtr->iconFor != NULL) {
2317     Tcl_AppendResult(interp, "can't withdraw ", argv[2],
2318     ": it is an icon for ", Tk_PathName(wmPtr->iconFor),
2319     (char *) NULL);
2320     return TCL_ERROR;
2321     }
2322     TkpWmSetState(winPtr, WithdrawnState);
2323     } else {
2324     Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],
2325     "\": must be aspect, client, command, deiconify, ",
2326     "focusmodel, frame, geometry, grid, group, iconbitmap, ",
2327     "iconify, iconmask, iconname, iconposition, ",
2328     "iconwindow, maxsize, minsize, overrideredirect, ",
2329     "positionfrom, protocol, resizable, sizefrom, state, title, ",
2330     "transient, or withdraw",
2331     (char *) NULL);
2332     return TCL_ERROR;
2333     }
2334     return TCL_OK;
2335    
2336     updateGeom:
2337     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
2338     Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
2339     wmPtr->flags |= WM_UPDATE_PENDING;
2340     }
2341     return TCL_OK;
2342     }
2343     /*ARGSUSED*/
2344     static void
2345     WmWaitVisibilityProc(clientData, eventPtr)
2346     ClientData clientData; /* Pointer to window. */
2347     XEvent *eventPtr; /* Information about event. */
2348     {
2349     TkWindow *winPtr = (TkWindow *) clientData;
2350     TkWindow *masterPtr = winPtr->wmInfoPtr->masterPtr;
2351    
2352     if ((eventPtr->type == VisibilityNotify) && (masterPtr != NULL)) {
2353     int state = masterPtr->wmInfoPtr->hints.initial_state;
2354    
2355     if ((state == NormalState) || (state == ZoomState)) {
2356     state = winPtr->wmInfoPtr->hints.initial_state;
2357     if ((state == NormalState) || (state == ZoomState)) {
2358     UpdateWrapper(winPtr);
2359     }
2360     }
2361     }
2362     }
2363    
2364     /*
2365     *----------------------------------------------------------------------
2366     *
2367     * Tk_SetGrid --
2368     *
2369     * This procedure is invoked by a widget when it wishes to set a grid
2370     * coordinate system that controls the size of a top-level window.
2371     * It provides a C interface equivalent to the "wm grid" command and
2372     * is usually asscoiated with the -setgrid option.
2373     *
2374     * Results:
2375     * None.
2376     *
2377     * Side effects:
2378     * Grid-related information will be passed to the window manager, so
2379     * that the top-level window associated with tkwin will resize on
2380     * even grid units. If some other window already controls gridding
2381     * for the top-level window then this procedure call has no effect.
2382     *
2383     *----------------------------------------------------------------------
2384     */
2385    
2386     void
2387     Tk_SetGrid(tkwin, reqWidth, reqHeight, widthInc, heightInc)
2388     Tk_Window tkwin; /* Token for window. New window mgr info
2389     * will be posted for the top-level window
2390     * associated with this window. */
2391     int reqWidth; /* Width (in grid units) corresponding to
2392     * the requested geometry for tkwin. */
2393     int reqHeight; /* Height (in grid units) corresponding to
2394     * the requested geometry for tkwin. */
2395     int widthInc, heightInc; /* Pixel increments corresponding to a
2396     * change of one grid unit. */
2397     {
2398     TkWindow *winPtr = (TkWindow *) tkwin;
2399     register WmInfo *wmPtr;
2400    
2401     /*
2402     * Find the top-level window for tkwin, plus the window manager
2403     * information.
2404     */
2405    
2406     while (!(winPtr->flags & TK_TOP_LEVEL)) {
2407     winPtr = winPtr->parentPtr;
2408     }
2409     wmPtr = winPtr->wmInfoPtr;
2410    
2411     if ((wmPtr->gridWin != NULL) && (wmPtr->gridWin != tkwin)) {
2412     return;
2413     }
2414    
2415     if ((wmPtr->reqGridWidth == reqWidth)
2416     && (wmPtr->reqGridHeight == reqHeight)
2417     && (wmPtr->widthInc == widthInc)
2418     && (wmPtr->heightInc == heightInc)
2419     && ((wmPtr->sizeHintsFlags & (PBaseSize|PResizeInc))
2420     == (PBaseSize|PResizeInc))) {
2421     return;
2422     }
2423    
2424     /*
2425     * If gridding was previously off, then forget about any window
2426     * size requests made by the user or via "wm geometry": these are
2427     * in pixel units and there's no easy way to translate them to
2428     * grid units since the new requested size of the top-level window in
2429     * pixels may not yet have been registered yet (it may filter up
2430     * the hierarchy in DoWhenIdle handlers). However, if the window
2431     * has never been mapped yet then just leave the window size alone:
2432     * assume that it is intended to be in grid units but just happened
2433     * to have been specified before this procedure was called.
2434     */
2435    
2436     if ((wmPtr->gridWin == NULL) && !(wmPtr->flags & WM_NEVER_MAPPED)) {
2437     wmPtr->width = -1;
2438     wmPtr->height = -1;
2439     }
2440    
2441     /*
2442     * Set the new gridding information, and start the process of passing
2443     * all of this information to the window manager.
2444     */
2445    
2446     wmPtr->gridWin = tkwin;
2447     wmPtr->reqGridWidth = reqWidth;
2448     wmPtr->reqGridHeight = reqHeight;
2449     wmPtr->widthInc = widthInc;
2450     wmPtr->heightInc = heightInc;
2451     wmPtr->sizeHintsFlags |= PBaseSize|PResizeInc;
2452     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
2453     Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
2454     wmPtr->flags |= WM_UPDATE_PENDING;
2455     }
2456     }
2457    
2458     /*
2459     *----------------------------------------------------------------------
2460     *
2461     * Tk_UnsetGrid --
2462     *
2463     * This procedure cancels the effect of a previous call
2464     * to Tk_SetGrid.
2465     *
2466     * Results:
2467     * None.
2468     *
2469     * Side effects:
2470     * If tkwin currently controls gridding for its top-level window,
2471     * gridding is cancelled for that top-level window; if some other
2472     * window controls gridding then this procedure has no effect.
2473     *
2474     *----------------------------------------------------------------------
2475     */
2476    
2477     void
2478     Tk_UnsetGrid(tkwin)
2479     Tk_Window tkwin; /* Token for window that is currently
2480     * controlling gridding. */
2481     {
2482     TkWindow *winPtr = (TkWindow *) tkwin;
2483     register WmInfo *wmPtr;
2484    
2485     /*
2486     * Find the top-level window for tkwin, plus the window manager
2487     * information.
2488     */
2489    
2490     while (!(winPtr->flags & TK_TOP_LEVEL)) {
2491     winPtr = winPtr->parentPtr;
2492     }
2493     wmPtr = winPtr->wmInfoPtr;
2494     if (tkwin != wmPtr->gridWin) {
2495     return;
2496     }
2497    
2498     wmPtr->gridWin = NULL;
2499     wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc);
2500     if (wmPtr->width != -1) {
2501     wmPtr->width = winPtr->reqWidth + (wmPtr->width
2502     - wmPtr->reqGridWidth)*wmPtr->widthInc;
2503     wmPtr->height = winPtr->reqHeight + (wmPtr->height
2504     - wmPtr->reqGridHeight)*wmPtr->heightInc;
2505     }
2506     wmPtr->widthInc = 1;
2507     wmPtr->heightInc = 1;
2508    
2509     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
2510     Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
2511     wmPtr->flags |= WM_UPDATE_PENDING;
2512     }
2513     }
2514    
2515     /*
2516     *----------------------------------------------------------------------
2517     *
2518     * TopLevelEventProc --
2519     *
2520     * This procedure is invoked when a top-level (or other externally-
2521     * managed window) is restructured in any way.
2522     *
2523     * Results:
2524     * None.
2525     *
2526     * Side effects:
2527     * Tk's internal data structures for the window get modified to
2528     * reflect the structural change.
2529     *
2530     *----------------------------------------------------------------------
2531     */
2532    
2533     static void
2534     TopLevelEventProc(clientData, eventPtr)
2535     ClientData clientData; /* Window for which event occurred. */
2536     XEvent *eventPtr; /* Event that just happened. */
2537     {
2538     register TkWindow *winPtr = (TkWindow *) clientData;
2539    
2540     if (eventPtr->type == DestroyNotify) {
2541     Tk_ErrorHandler handler;
2542    
2543     if (!(winPtr->flags & TK_ALREADY_DEAD)) {
2544     /*
2545     * A top-level window was deleted externally (e.g., by the window
2546     * manager). This is probably not a good thing, but cleanup as
2547     * best we can. The error handler is needed because
2548     * Tk_DestroyWindow will try to destroy the window, but of course
2549     * it's already gone.
2550     */
2551    
2552     handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
2553     (Tk_ErrorProc *) NULL, (ClientData) NULL);
2554     Tk_DestroyWindow((Tk_Window) winPtr);
2555     Tk_DeleteErrorHandler(handler);
2556     }
2557     }
2558     else if (eventPtr->type == ConfigureNotify) {
2559     WmInfo *wmPtr;
2560     wmPtr = winPtr->wmInfoPtr;
2561    
2562     if (winPtr->flags & TK_EMBEDDED) {
2563     Tk_Window tkwin = (Tk_Window)winPtr;
2564     SendMessage(wmPtr->wrapper, TK_GEOMETRYREQ, Tk_ReqWidth(tkwin),
2565     Tk_ReqHeight(tkwin));
2566     }
2567     }
2568     }
2569    
2570     /*
2571     *----------------------------------------------------------------------
2572     *
2573     * TopLevelReqProc --
2574     *
2575     * This procedure is invoked by the geometry manager whenever
2576     * the requested size for a top-level window is changed.
2577     *
2578     * Results:
2579     * None.
2580     *
2581     * Side effects:
2582     * Arrange for the window to be resized to satisfy the request
2583     * (this happens as a when-idle action).
2584     *
2585     *----------------------------------------------------------------------
2586     */
2587    
2588     /* ARGSUSED */
2589     static void
2590     TopLevelReqProc(dummy, tkwin)
2591     ClientData dummy; /* Not used. */
2592     Tk_Window tkwin; /* Information about window. */
2593     {
2594     TkWindow *winPtr = (TkWindow *) tkwin;
2595     WmInfo *wmPtr;
2596    
2597     wmPtr = winPtr->wmInfoPtr;
2598     if (winPtr->flags & TK_EMBEDDED) {
2599     SendMessage(wmPtr->wrapper, TK_GEOMETRYREQ, Tk_ReqWidth(tkwin),
2600     Tk_ReqHeight(tkwin));
2601     }
2602     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
2603     Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
2604     wmPtr->flags |= WM_UPDATE_PENDING;
2605     }
2606     }
2607    
2608     /*
2609     *----------------------------------------------------------------------
2610     *
2611     * UpdateGeometryInfo --
2612     *
2613     * This procedure is invoked when a top-level window is first
2614     * mapped, and also as a when-idle procedure, to bring the
2615     * geometry and/or position of a top-level window back into
2616     * line with what has been requested by the user and/or widgets.
2617     * This procedure doesn't return until the system has
2618     * responded to the geometry change.
2619     *
2620     * Results:
2621     * None.
2622     *
2623     * Side effects:
2624     * The window's size and location may change, unless the WM prevents
2625     * that from happening.
2626     *
2627     *----------------------------------------------------------------------
2628     */
2629    
2630     static void
2631     UpdateGeometryInfo(clientData)
2632     ClientData clientData; /* Pointer to the window's record. */
2633     {
2634     int x, y; /* Position of border on desktop. */
2635     int width, height; /* Size of client area. */
2636     RECT rect;
2637     register TkWindow *winPtr = (TkWindow *) clientData;
2638     register WmInfo *wmPtr = winPtr->wmInfoPtr;
2639    
2640     wmPtr->flags &= ~WM_UPDATE_PENDING;
2641    
2642     /*
2643     * If the window is minimized or maximized, we should not update
2644     * our geometry since it will end up with the wrong values.
2645     * ConfigureToplevel will reschedule UpdateGeometryInfo when the
2646     * state of the window changes.
2647     */
2648    
2649     if (IsIconic(wmPtr->wrapper) || IsZoomed(wmPtr->wrapper)) {
2650     return;
2651     }
2652    
2653     /*
2654     * Compute the border size for the current window style. This
2655     * size will include the resize handles, the title bar and the
2656     * menubar. Note that this size will not be correct if the
2657     * menubar spans multiple lines. The height will be off by a
2658     * multiple of the menubar height. It really only measures the
2659     * minimum size of the border.
2660     */
2661    
2662     rect.left = rect.right = rect.top = rect.bottom = 0;
2663     AdjustWindowRectEx(&rect, wmPtr->style, wmPtr->hMenu != NULL,
2664     wmPtr->exStyle);
2665     wmPtr->borderWidth = rect.right - rect.left;
2666     wmPtr->borderHeight = rect.bottom - rect.top;
2667    
2668     /*
2669     * Compute the new size for the top-level window. See the
2670     * user documentation for details on this, but the size
2671     * requested depends on (a) the size requested internally
2672     * by the window's widgets, (b) the size requested by the
2673     * user in a "wm geometry" command or via wm-based interactive
2674     * resizing (if any), and (c) whether or not the window is
2675     * gridded. Don't permit sizes <= 0 because this upsets
2676     * the X server.
2677     */
2678    
2679     if (wmPtr->width == -1) {
2680     width = winPtr->reqWidth;
2681     } else if (wmPtr->gridWin != NULL) {
2682     width = winPtr->reqWidth
2683     + (wmPtr->width - wmPtr->reqGridWidth)*wmPtr->widthInc;
2684     } else {
2685     width = wmPtr->width;
2686     }
2687     if (width <= 0) {
2688     width = 1;
2689     }
2690     if (wmPtr->height == -1) {
2691     height = winPtr->reqHeight;
2692     } else if (wmPtr->gridWin != NULL) {
2693     height = winPtr->reqHeight
2694     + (wmPtr->height - wmPtr->reqGridHeight)*wmPtr->heightInc;
2695     } else {
2696     height = wmPtr->height;
2697     }
2698     if (height <= 0) {
2699     height = 1;
2700     }
2701    
2702     /*
2703     * Compute the new position for the upper-left pixel of the window's
2704     * decorative frame. This is tricky, because we need to include the
2705     * border widths supplied by a reparented parent in this calculation,
2706     * but can't use the parent's current overall size since that may
2707     * change as a result of this code.
2708     */
2709    
2710     if (wmPtr->flags & WM_NEGATIVE_X) {
2711     x = DisplayWidth(winPtr->display, winPtr->screenNum) - wmPtr->x
2712     - (width + wmPtr->borderWidth);
2713     } else {
2714     x = wmPtr->x;
2715     }
2716     if (wmPtr->flags & WM_NEGATIVE_Y) {
2717     y = DisplayHeight(winPtr->display, winPtr->screenNum) - wmPtr->y
2718     - (height + wmPtr->borderHeight);
2719     } else {
2720     y = wmPtr->y;
2721     }
2722    
2723     /*
2724     * If this window is embedded and the container is also in this
2725     * process, we don't need to do anything special about the
2726     * geometry, except to make sure that the desired size is known
2727     * by the container. Also, zero out any position information,
2728     * since embedded windows are not allowed to move.
2729     */
2730    
2731     if (winPtr->flags & TK_BOTH_HALVES) {
2732     wmPtr->x = wmPtr->y = 0;
2733     wmPtr->flags &= ~(WM_NEGATIVE_X|WM_NEGATIVE_Y);
2734     Tk_GeometryRequest((Tk_Window) TkpGetOtherWindow(winPtr),
2735     width, height);
2736     return;
2737     }
2738    
2739     /*
2740     * Reconfigure the window if it isn't already configured correctly. Base
2741     * the size check on what we *asked for* last time, not what we got.
2742     * Return immediately if there have been no changes in the requested
2743     * geometry of the toplevel.
2744     */
2745     /* TODO: need to add flag for possible menu size change */
2746    
2747     if (!((wmPtr->flags & WM_MOVE_PENDING)
2748     || (width != wmPtr->configWidth)
2749     || (height != wmPtr->configHeight))) {
2750     return;
2751     }
2752     wmPtr->flags &= ~WM_MOVE_PENDING;
2753    
2754     wmPtr->configWidth = width;
2755     wmPtr->configHeight = height;
2756    
2757     /*
2758     * Don't bother moving the window if we are in the process of
2759     * creating it. Just update the geometry info based on what
2760     * we asked for.
2761     */
2762    
2763     if (wmPtr->flags & WM_CREATE_PENDING) {
2764     winPtr->changes.x = x;
2765     winPtr->changes.y = y;
2766     winPtr->changes.width = width;
2767     winPtr->changes.height = height;
2768     return;
2769     }
2770    
2771     wmPtr->flags |= WM_SYNC_PENDING;
2772     if (winPtr->flags & TK_EMBEDDED) {
2773     /*
2774     * The wrapper window is in a different process, so we need
2775     * to send it a geometry request. This protocol assumes that
2776     * the other process understands this Tk message, otherwise
2777     * our requested geometry will be ignored.
2778     */
2779    
2780     SendMessage(wmPtr->wrapper, TK_GEOMETRYREQ, width, height);
2781     } else {
2782     int reqHeight, reqWidth;
2783     RECT windowRect;
2784     int menuInc = GetSystemMetrics(SM_CYMENU);
2785     int newHeight;
2786    
2787     /*
2788     * We have to keep resizing the window until we get the
2789     * requested height in the client area. If the client
2790     * area has zero height, then the window rect is too
2791     * small by definition. Try increasing the border height
2792     * and try again. Once we have a positive size, then
2793     * we can adjust the height exactly. If the window
2794     * rect comes back smaller than we requested, we have
2795     * hit the maximum constraints that Windows imposes.
2796     * Once we find a positive client size, the next size
2797     * is the one we try no matter what.
2798     */
2799    
2800     reqHeight = height + wmPtr->borderHeight;
2801     reqWidth = width + wmPtr->borderWidth;
2802    
2803     while (1) {
2804     MoveWindow(wmPtr->wrapper, x, y, reqWidth, reqHeight, TRUE);
2805     GetWindowRect(wmPtr->wrapper, &windowRect);
2806     newHeight = windowRect.bottom - windowRect.top;
2807    
2808     /*
2809     * If the request wasn't satisfied, we have hit an external
2810     * constraint and must stop.
2811     */
2812    
2813     if (newHeight < reqHeight) {
2814     break;
2815     }
2816    
2817     /*
2818     * Now check the size of the client area against our ideal.
2819     */
2820    
2821     GetClientRect(wmPtr->wrapper, &windowRect);
2822     newHeight = windowRect.bottom - windowRect.top;
2823    
2824     if (newHeight == height) {
2825     /*
2826     * We're done.
2827     */
2828     break;
2829     } else if (newHeight > height) {
2830     /*
2831     * One last resize to get rid of the extra space.
2832     */
2833     menuInc = newHeight - height;
2834     reqHeight -= menuInc;
2835     if (wmPtr->flags & WM_NEGATIVE_Y) {
2836     y += menuInc;
2837     }
2838     MoveWindow(wmPtr->wrapper, x, y, reqWidth, reqHeight, TRUE);
2839     break;
2840     }
2841    
2842     /*
2843     * We didn't get enough space to satisfy our requested
2844     * height, so the menu must have wrapped. Increase the
2845     * size of the window by one menu height and move the
2846     * window if it is positioned relative to the lower right
2847     * corner of the screen.
2848     */
2849    
2850     reqHeight += menuInc;
2851     if (wmPtr->flags & WM_NEGATIVE_Y) {
2852     y -= menuInc;
2853     }
2854     }
2855     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
2856     DrawMenuBar(wmPtr->wrapper);
2857     }
2858     }
2859     wmPtr->flags &= ~WM_SYNC_PENDING;
2860     }
2861    
2862     /*
2863     *--------------------------------------------------------------
2864     *
2865     * ParseGeometry --
2866     *
2867     * This procedure parses a geometry string and updates
2868     * information used to control the geometry of a top-level
2869     * window.
2870     *
2871     * Results:
2872     * A standard Tcl return value, plus an error message in
2873     * the interp's result if an error occurs.
2874     *
2875     * Side effects:
2876     * The size and/or location of winPtr may change.
2877     *
2878     *--------------------------------------------------------------
2879     */
2880    
2881     static int
2882     ParseGeometry(interp, string, winPtr)
2883     Tcl_Interp *interp; /* Used for error reporting. */
2884     char *string; /* String containing new geometry. Has the
2885     * standard form "=wxh+x+y". */
2886     TkWindow *winPtr; /* Pointer to top-level window whose
2887     * geometry is to be changed. */
2888     {
2889     register WmInfo *wmPtr = winPtr->wmInfoPtr;
2890     int x, y, width, height, flags;
2891     char *end;
2892     register char *p = string;
2893    
2894     /*
2895     * The leading "=" is optional.
2896     */
2897    
2898     if (*p == '=') {
2899     p++;
2900     }
2901    
2902     /*
2903     * Parse the width and height, if they are present. Don't
2904     * actually update any of the fields of wmPtr until we've
2905     * successfully parsed the entire geometry string.
2906     */
2907    
2908     width = wmPtr->width;
2909     height = wmPtr->height;
2910     x = wmPtr->x;
2911     y = wmPtr->y;
2912     flags = wmPtr->flags;
2913     if (isdigit(UCHAR(*p))) {
2914     width = strtoul(p, &end, 10);
2915     p = end;
2916     if (*p != 'x') {
2917     goto error;
2918     }
2919     p++;
2920     if (!isdigit(UCHAR(*p))) {
2921     goto error;
2922     }
2923     height = strtoul(p, &end, 10);
2924     p = end;
2925     }
2926    
2927     /*
2928     * Parse the X and Y coordinates, if they are present.
2929     */
2930    
2931     if (*p != '\0') {
2932     flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y);
2933     if (*p == '-') {
2934     flags |= WM_NEGATIVE_X;
2935     } else if (*p != '+') {
2936     goto error;
2937     }
2938     p++;
2939     if (!isdigit(UCHAR(*p)) && (*p != '-')) {
2940     goto error;
2941     }
2942     x = strtol(p, &end, 10);
2943     p = end;
2944     if (*p == '-') {
2945     flags |= WM_NEGATIVE_Y;
2946     } else if (*p != '+') {
2947     goto error;
2948     }
2949     p++;
2950     if (!isdigit(UCHAR(*p)) && (*p != '-')) {
2951     goto error;
2952     }
2953     y = strtol(p, &end, 10);
2954     if (*end != '\0') {
2955     goto error;
2956     }
2957    
2958     /*
2959     * Assume that the geometry information came from the user,
2960     * unless an explicit source has been specified. Otherwise
2961     * most window managers assume that the size hints were
2962     * program-specified and they ignore them.
2963     */
2964    
2965     if ((wmPtr->sizeHintsFlags & (USPosition|PPosition)) == 0) {
2966     wmPtr->sizeHintsFlags |= USPosition;
2967     }
2968     }
2969    
2970     /*
2971     * Everything was parsed OK. Update the fields of *wmPtr and
2972     * arrange for the appropriate information to be percolated out
2973     * to the window manager at the next idle moment.
2974     */
2975    
2976     wmPtr->width = width;
2977     wmPtr->height = height;
2978     wmPtr->x = x;
2979     wmPtr->y = y;
2980     flags |= WM_MOVE_PENDING;
2981     wmPtr->flags = flags;
2982    
2983     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
2984     Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
2985     wmPtr->flags |= WM_UPDATE_PENDING;
2986     }
2987     return TCL_OK;
2988    
2989     error:
2990     Tcl_AppendResult(interp, "bad geometry specifier \"",
2991     string, "\"", (char *) NULL);
2992     return TCL_ERROR;
2993     }
2994    
2995     /*
2996     *----------------------------------------------------------------------
2997     *
2998     * Tk_GetRootCoords --
2999     *
3000     * Given a token for a window, this procedure traces through the
3001     * window's lineage to find the (virtual) root-window coordinates
3002     * corresponding to point (0,0) in the window.
3003     *
3004     * Results:
3005     * The locations pointed to by xPtr and yPtr are filled in with
3006     * the root coordinates of the (0,0) point in tkwin.
3007     *
3008     * Side effects:
3009     * None.
3010     *
3011     *----------------------------------------------------------------------
3012     */
3013    
3014     void
3015     Tk_GetRootCoords(tkwin, xPtr, yPtr)
3016     Tk_Window tkwin; /* Token for window. */
3017     int *xPtr; /* Where to store x-displacement of (0,0). */
3018     int *yPtr; /* Where to store y-displacement of (0,0). */
3019     {
3020     register TkWindow *winPtr = (TkWindow *) tkwin;
3021    
3022     /*
3023     * If the window is mapped, let Windows figure out the translation.
3024     */
3025    
3026     if (winPtr->window != None) {
3027     HWND hwnd = Tk_GetHWND(winPtr->window);
3028     POINT point;
3029    
3030     point.x = 0;
3031     point.y = 0;
3032    
3033     ClientToScreen(hwnd, &point);
3034    
3035     *xPtr = point.x;
3036     *yPtr = point.y;
3037     } else {
3038     *xPtr = 0;
3039     *yPtr = 0;
3040     }
3041     }
3042    
3043     /*
3044     *----------------------------------------------------------------------
3045     *
3046     * Tk_CoordsToWindow --
3047     *
3048     * Given the (virtual) root coordinates of a point, this procedure
3049     * returns the token for the top-most window covering that point,
3050     * if there exists such a window in this application.
3051     *
3052     * Results:
3053     * The return result is either a token for the window corresponding
3054     * to rootX and rootY, or else NULL to indicate that there is no such
3055     * window.
3056     *
3057     * Side effects:
3058     * None.
3059     *
3060     *----------------------------------------------------------------------
3061     */
3062    
3063     Tk_Window
3064     Tk_CoordsToWindow(rootX, rootY, tkwin)
3065     int rootX, rootY; /* Coordinates of point in root window. If
3066     * a virtual-root window manager is in use,
3067     * these coordinates refer to the virtual
3068     * root, not the real root. */
3069     Tk_Window tkwin; /* Token for any window in application;
3070     * used to identify the display. */
3071     {
3072     POINT pos;
3073     HWND hwnd;
3074     TkWindow *winPtr;
3075    
3076     pos.x = rootX;
3077     pos.y = rootY;
3078     hwnd = WindowFromPoint(pos);
3079    
3080     winPtr = (TkWindow *) Tk_HWNDToWindow(hwnd);
3081     if (winPtr && (winPtr->mainPtr == ((TkWindow *) tkwin)->mainPtr)) {
3082     return (Tk_Window) winPtr;
3083     }
3084     return NULL;
3085     }
3086    
3087     /*
3088     *----------------------------------------------------------------------
3089     *
3090     * Tk_GetVRootGeometry --
3091     *
3092     * This procedure returns information about the virtual root
3093     * window corresponding to a particular Tk window.
3094     *
3095     * Results:
3096     * The values at xPtr, yPtr, widthPtr, and heightPtr are set
3097     * with the offset and dimensions of the root window corresponding
3098     * to tkwin. If tkwin is being managed by a virtual root window
3099     * manager these values correspond to the virtual root window being
3100     * used for tkwin; otherwise the offsets will be 0 and the
3101     * dimensions will be those of the screen.
3102     *
3103     * Side effects:
3104     * Vroot window information is refreshed if it is out of date.
3105     *
3106     *----------------------------------------------------------------------
3107     */
3108    
3109     void
3110     Tk_GetVRootGeometry(tkwin, xPtr, yPtr, widthPtr, heightPtr)
3111     Tk_Window tkwin; /* Window whose virtual root is to be
3112     * queried. */
3113     int *xPtr, *yPtr; /* Store x and y offsets of virtual root
3114     * here. */
3115     int *widthPtr, *heightPtr; /* Store dimensions of virtual root here. */
3116     {
3117     TkWindow *winPtr = (TkWindow *) tkwin;
3118    
3119     *xPtr = 0;
3120     *yPtr = 0;
3121     *widthPtr = DisplayWidth(winPtr->display, winPtr->screenNum);
3122     *heightPtr = DisplayHeight(winPtr->display, winPtr->screenNum);
3123     }
3124    
3125     /*
3126     *----------------------------------------------------------------------
3127     *
3128     * Tk_MoveToplevelWindow --
3129     *
3130     * This procedure is called instead of Tk_MoveWindow to adjust
3131     * the x-y location of a top-level window. It delays the actual
3132     * move to a later time and keeps window-manager information
3133     * up-to-date with the move
3134     *
3135     * Results:
3136     * None.
3137     *
3138     * Side effects:
3139     * The window is eventually moved so that its upper-left corner
3140     * (actually, the upper-left corner of the window's decorative
3141     * frame, if there is one) is at (x,y).
3142     *
3143     *----------------------------------------------------------------------
3144     */
3145    
3146     void
3147     Tk_MoveToplevelWindow(tkwin, x, y)
3148     Tk_Window tkwin; /* Window to move. */
3149     int x, y; /* New location for window (within
3150     * parent). */
3151     {
3152     TkWindow *winPtr = (TkWindow *) tkwin;
3153     register WmInfo *wmPtr = winPtr->wmInfoPtr;
3154    
3155     if (!(winPtr->flags & TK_TOP_LEVEL)) {
3156     panic("Tk_MoveToplevelWindow called with non-toplevel window");
3157     }
3158     wmPtr->x = x;
3159     wmPtr->y = y;
3160     wmPtr->flags |= WM_MOVE_PENDING;
3161     wmPtr->flags &= ~(WM_NEGATIVE_X|WM_NEGATIVE_Y);
3162     if ((wmPtr->sizeHintsFlags & (USPosition|PPosition)) == 0) {
3163     wmPtr->sizeHintsFlags |= USPosition;
3164     }
3165    
3166     /*
3167     * If the window has already been mapped, must bring its geometry
3168     * up-to-date immediately, otherwise an event might arrive from the
3169     * server that would overwrite wmPtr->x and wmPtr->y and lose the
3170     * new position.
3171     */
3172    
3173     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
3174     if (wmPtr->flags & WM_UPDATE_PENDING) {
3175     Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
3176     }
3177     UpdateGeometryInfo((ClientData) winPtr);
3178     }
3179     }
3180    
3181     /*
3182     *----------------------------------------------------------------------
3183     *
3184     * TkWmProtocolEventProc --
3185     *
3186     * This procedure is called by the Tk_HandleEvent whenever a
3187     * ClientMessage event arrives whose type is "WM_PROTOCOLS".
3188     * This procedure handles the message from the window manager
3189     * in an appropriate fashion.
3190     *
3191     * Results:
3192     * None.
3193     *
3194     * Side effects:
3195     * Depends on what sort of handler, if any, was set up for the
3196     * protocol.
3197     *
3198     *----------------------------------------------------------------------
3199     */
3200    
3201     void
3202     TkWmProtocolEventProc(winPtr, eventPtr)
3203     TkWindow *winPtr; /* Window to which the event was sent. */
3204     XEvent *eventPtr; /* X event. */
3205     {
3206     WmInfo *wmPtr;
3207     register ProtocolHandler *protPtr;
3208     Atom protocol;
3209     int result;
3210     Tcl_Interp *interp;
3211    
3212     wmPtr = winPtr->wmInfoPtr;
3213     if (wmPtr == NULL) {
3214     return;
3215     }
3216     protocol = (Atom) eventPtr->xclient.data.l[0];
3217     for (protPtr = wmPtr->protPtr; protPtr != NULL;
3218     protPtr = protPtr->nextPtr) {
3219     if (protocol == protPtr->protocol) {
3220     /*
3221     * Cache atom name, as we might destroy the window as a
3222     * result of the eval.
3223     */
3224     char *name = Tk_GetAtomName((Tk_Window) winPtr, protocol);
3225    
3226     Tcl_Preserve((ClientData) protPtr);
3227     interp = protPtr->interp;
3228     Tcl_Preserve((ClientData) interp);
3229     result = Tcl_GlobalEval(interp, protPtr->command);
3230     if (result != TCL_OK) {
3231     Tcl_AddErrorInfo(interp, "\n (command for \"");
3232     Tcl_AddErrorInfo(interp, name);
3233     Tcl_AddErrorInfo(interp, "\" window manager protocol)");
3234     Tcl_BackgroundError(interp);
3235     }
3236     Tcl_Release((ClientData) interp);
3237     Tcl_Release((ClientData) protPtr);
3238     return;
3239     }
3240     }
3241    
3242     /*
3243     * No handler was present for this protocol. If this is a
3244     * WM_DELETE_WINDOW message then just destroy the window.
3245     */
3246    
3247     if (protocol == Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW")) {
3248     Tk_DestroyWindow((Tk_Window) winPtr);
3249     }
3250     }
3251    
3252     /*
3253     *----------------------------------------------------------------------
3254     *
3255     * TkWmRestackToplevel --
3256     *
3257     * This procedure restacks a top-level window.
3258     *
3259     * Results:
3260     * None.
3261     *
3262     * Side effects:
3263     * WinPtr gets restacked as specified by aboveBelow and otherPtr.
3264     * This procedure doesn't return until the restack has taken
3265     * effect and the ConfigureNotify event for it has been received.
3266     *
3267     *----------------------------------------------------------------------
3268     */
3269    
3270     void
3271     TkWmRestackToplevel(winPtr, aboveBelow, otherPtr)
3272     TkWindow *winPtr; /* Window to restack. */
3273     int aboveBelow; /* Gives relative position for restacking;
3274     * must be Above or Below. */
3275     TkWindow *otherPtr; /* Window relative to which to restack;
3276     * if NULL, then winPtr gets restacked
3277     * above or below *all* siblings. */
3278     {
3279     HWND hwnd, insertAfter;
3280    
3281     /*
3282     * Can't set stacking order properly until the window is on the
3283     * screen (mapping it may give it a reparent window).
3284     */
3285    
3286     if (winPtr->window == None) {
3287     Tk_MakeWindowExist((Tk_Window) winPtr);
3288     }
3289     if (winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
3290     TkWmMapWindow(winPtr);
3291     }
3292     hwnd = (winPtr->wmInfoPtr->wrapper != NULL)
3293     ? winPtr->wmInfoPtr->wrapper : Tk_GetHWND(winPtr->window);
3294    
3295     if (otherPtr != NULL) {
3296     if (otherPtr->window == None) {
3297     Tk_MakeWindowExist((Tk_Window) otherPtr);
3298     }
3299     if (otherPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
3300     TkWmMapWindow(otherPtr);
3301     }
3302     insertAfter = (otherPtr->wmInfoPtr->wrapper != NULL)
3303     ? otherPtr->wmInfoPtr->wrapper : Tk_GetHWND(otherPtr->window);
3304     } else {
3305     insertAfter = NULL;
3306     }
3307    
3308     TkWinSetWindowPos(hwnd, insertAfter, aboveBelow);
3309     }
3310    
3311     /*
3312     *----------------------------------------------------------------------
3313     *
3314     * TkWmAddToColormapWindows --
3315     *
3316     * This procedure is called to add a given window to the
3317     * WM_COLORMAP_WINDOWS property for its top-level, if it
3318     * isn't already there. It is invoked by the Tk code that
3319     * creates a new colormap, in order to make sure that colormap
3320     * information is propagated to the window manager by default.
3321     *
3322     * Results:
3323     * None.
3324     *
3325     * Side effects:
3326     * WinPtr's window gets added to the WM_COLORMAP_WINDOWS
3327     * property of its nearest top-level ancestor, unless the
3328     * colormaps have been set explicitly with the
3329     * "wm colormapwindows" command.
3330     *
3331     *----------------------------------------------------------------------
3332     */
3333    
3334     void
3335     TkWmAddToColormapWindows(winPtr)
3336     TkWindow *winPtr; /* Window with a non-default colormap.
3337     * Should not be a top-level window. */
3338     {
3339     TkWindow *topPtr;
3340     TkWindow **oldPtr, **newPtr;
3341     int count, i;
3342    
3343     if (winPtr->window == None) {
3344     return;
3345     }
3346    
3347     for (topPtr = winPtr->parentPtr; ; topPtr = topPtr->parentPtr) {
3348     if (topPtr == NULL) {
3349     /*
3350     * Window is being deleted. Skip the whole operation.
3351     */
3352    
3353     return;
3354     }
3355     if (topPtr->flags & TK_TOP_LEVEL) {
3356     break;
3357     }
3358     }
3359     if (topPtr->wmInfoPtr->flags & WM_COLORMAPS_EXPLICIT) {
3360     return;
3361     }
3362    
3363     /*
3364     * Make sure that the window isn't already in the list.
3365     */
3366    
3367     count = topPtr->wmInfoPtr->cmapCount;
3368     oldPtr = topPtr->wmInfoPtr->cmapList;
3369    
3370     for (i = 0; i < count; i++) {
3371     if (oldPtr[i] == winPtr) {
3372     return;
3373     }
3374     }
3375    
3376     /*
3377     * Make a new bigger array and use it to reset the property.
3378     * Automatically add the toplevel itself as the last element
3379     * of the list.
3380     */
3381    
3382     newPtr = (TkWindow **) ckalloc((unsigned) ((count+2)*sizeof(TkWindow*)));
3383     if (count > 0) {
3384     memcpy(newPtr, oldPtr, count * sizeof(TkWindow*));
3385     }
3386     if (count == 0) {
3387     count++;
3388     }
3389     newPtr[count-1] = winPtr;
3390     newPtr[count] = topPtr;
3391     if (oldPtr != NULL) {
3392     ckfree((char *) oldPtr);
3393     }
3394    
3395     topPtr->wmInfoPtr->cmapList = newPtr;
3396     topPtr->wmInfoPtr->cmapCount = count+1;
3397    
3398     /*
3399     * Now we need to force the updated colormaps to be installed.
3400     */
3401    
3402     if (topPtr->wmInfoPtr == winPtr->dispPtr->foregroundWmPtr) {
3403     InstallColormaps(topPtr->wmInfoPtr->wrapper, WM_QUERYNEWPALETTE, 1);
3404     } else {
3405     InstallColormaps(topPtr->wmInfoPtr->wrapper, WM_PALETTECHANGED, 0);
3406     }
3407     }
3408    
3409     /*
3410     *----------------------------------------------------------------------
3411     *
3412     * TkWmRemoveFromColormapWindows --
3413     *
3414     * This procedure is called to remove a given window from the
3415     * WM_COLORMAP_WINDOWS property for its top-level. It is invoked
3416     * when windows are deleted.
3417     *
3418     * Results:
3419     * None.
3420     *
3421     * Side effects:
3422     * WinPtr's window gets removed from the WM_COLORMAP_WINDOWS
3423     * property of its nearest top-level ancestor, unless the
3424     * top-level itself is being deleted too.
3425     *
3426     *----------------------------------------------------------------------
3427     */
3428    
3429     void
3430     TkWmRemoveFromColormapWindows(winPtr)
3431     TkWindow *winPtr; /* Window that may be present in
3432     * WM_COLORMAP_WINDOWS property for its
3433     * top-level. Should not be a top-level
3434     * window. */
3435     {
3436     TkWindow *topPtr;
3437     TkWindow **oldPtr;
3438     int count, i, j;
3439    
3440     for (topPtr = winPtr->parentPtr; ; topPtr = topPtr->parentPtr) {
3441     if (topPtr == NULL) {
3442     /*
3443     * Ancestors have been deleted, so skip the whole operation.
3444     * Seems like this can't ever happen?
3445     */
3446    
3447     return;
3448     }
3449     if (topPtr->flags & TK_TOP_LEVEL) {
3450     break;
3451     }
3452     }
3453     if (topPtr->flags & TK_ALREADY_DEAD) {
3454     /*
3455     * Top-level is being deleted, so there's no need to cleanup
3456     * the WM_COLORMAP_WINDOWS property.
3457     */
3458    
3459     return;
3460     }
3461    
3462     /*
3463     * Find the window and slide the following ones down to cover
3464     * it up.
3465     */
3466    
3467     count = topPtr->wmInfoPtr->cmapCount;
3468     oldPtr = topPtr->wmInfoPtr->cmapList;
3469     for (i = 0; i < count; i++) {
3470     if (oldPtr[i] == winPtr) {
3471     for (j = i ; j < count-1; j++) {
3472     oldPtr[j] = oldPtr[j+1];
3473     }
3474     topPtr->wmInfoPtr->cmapCount = count-1;
3475     break;
3476     }
3477     }
3478     }
3479    
3480     /*
3481     *----------------------------------------------------------------------
3482     *
3483     * TkWinSetMenu--
3484     *
3485     * Associcates a given HMENU to a window.
3486     *
3487     * Results:
3488     * None.
3489     *
3490     * Side effects:
3491     * The menu will end up being drawn in the window, and the geometry
3492     * of the window will have to be changed.
3493     *
3494     *----------------------------------------------------------------------
3495     */
3496    
3497     void
3498     TkWinSetMenu(tkwin, hMenu)
3499     Tk_Window tkwin; /* the window to put the menu in */
3500     HMENU hMenu; /* the menu to set */
3501     {
3502     TkWindow *winPtr = (TkWindow *) tkwin;
3503     WmInfo *wmPtr = winPtr->wmInfoPtr;
3504    
3505     wmPtr->hMenu = hMenu;
3506    
3507     if (!(wmPtr->flags & TK_EMBEDDED)) {
3508     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
3509     int syncPending = wmPtr->flags & WM_SYNC_PENDING;
3510    
3511     wmPtr->flags |= WM_SYNC_PENDING;
3512     SetMenu(wmPtr->wrapper, hMenu);
3513     if (!syncPending) {
3514     wmPtr->flags &= ~WM_SYNC_PENDING;
3515     }
3516     }
3517     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
3518     Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
3519     wmPtr->flags |= WM_UPDATE_PENDING|WM_MOVE_PENDING;
3520     }
3521     }
3522     }
3523    
3524     /*
3525     *----------------------------------------------------------------------
3526     *
3527     * ConfigureTopLevel --
3528     *
3529     * Generate a ConfigureNotify event based on the current position
3530     * information. This procedure is called by TopLevelProc.
3531     *
3532     * Results:
3533     * None.
3534     *
3535     * Side effects:
3536     * Queues a new event.
3537     *
3538     *----------------------------------------------------------------------
3539     */
3540    
3541     static void
3542     ConfigureTopLevel(pos)
3543     WINDOWPOS *pos;
3544     {
3545     TkWindow *winPtr = GetTopLevel(pos->hwnd);
3546     WmInfo *wmPtr;
3547     int state; /* Current window state. */
3548     RECT rect;
3549     WINDOWPLACEMENT windowPos;
3550    
3551     if (winPtr == NULL) {
3552     return;
3553     }
3554    
3555     wmPtr = winPtr->wmInfoPtr;
3556    
3557     /*
3558     * Determine the current window state.
3559     */
3560    
3561     if (!IsWindowVisible(wmPtr->wrapper)) {
3562     state = WithdrawnState;
3563     } else {
3564     windowPos.length = sizeof(WINDOWPLACEMENT);
3565     GetWindowPlacement(wmPtr->wrapper, &windowPos);
3566     switch (windowPos.showCmd) {
3567     case SW_SHOWMAXIMIZED:
3568     state = ZoomState;
3569     break;
3570     case SW_SHOWMINIMIZED:
3571     state = IconicState;
3572     break;
3573     case SW_SHOWNORMAL:
3574     state = NormalState;
3575     break;
3576     }
3577     }
3578    
3579     /*
3580     * If the state of the window just changed, be sure to update the
3581     * child window information.
3582     */
3583    
3584     if (wmPtr->hints.initial_state != state) {
3585     wmPtr->hints.initial_state = state;
3586     switch (state) {
3587     case WithdrawnState:
3588     case IconicState:
3589     XUnmapWindow(winPtr->display, winPtr->window);
3590     break;
3591    
3592     case NormalState:
3593     /*
3594     * Schedule a geometry update. Since we ignore geometry
3595     * requests while in any other state, the geometry info
3596     * may be stale.
3597     */
3598    
3599     if (!(wmPtr->flags & WM_UPDATE_PENDING)) {
3600     Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
3601     wmPtr->flags |= WM_UPDATE_PENDING;
3602     }
3603     /* fall through */
3604     case ZoomState:
3605     XMapWindow(winPtr->display, winPtr->window);
3606     pos->flags |= SWP_NOMOVE | SWP_NOSIZE;
3607     break;
3608     }
3609     }
3610    
3611     /*
3612     * Don't report geometry changes in the Iconic or Withdrawn states.
3613     */
3614    
3615     if (state == WithdrawnState || state == IconicState) {
3616     return;
3617     }
3618    
3619    
3620     /*
3621     * Compute the current geometry of the client area, reshape the
3622     * Tk window and generate a ConfigureNotify event.
3623     */
3624    
3625     GetClientRect(wmPtr->wrapper, &rect);
3626     winPtr->changes.x = pos->x;
3627     winPtr->changes.y = pos->y;
3628     winPtr->changes.width = rect.right - rect.left;
3629     winPtr->changes.height = rect.bottom - rect.top;
3630     wmPtr->borderHeight = pos->cy - winPtr->changes.height;
3631     MoveWindow(Tk_GetHWND(winPtr->window), 0, 0,
3632     winPtr->changes.width, winPtr->changes.height, TRUE);
3633     GenerateConfigureNotify(winPtr);
3634    
3635     /*
3636     * Update window manager geometry info if needed.
3637     */
3638    
3639     if (state == NormalState) {
3640    
3641     /*
3642     * Update size information from the event. There are a couple of
3643     * tricky points here:
3644     *
3645     * 1. If the user changed the size externally then set wmPtr->width
3646     * and wmPtr->height just as if a "wm geometry" command had been
3647     * invoked with the same information.
3648     * 2. However, if the size is changing in response to a request
3649     * coming from us (sync is set), then don't set
3650     * wmPtr->width or wmPtr->height (otherwise the window will stop
3651     * tracking geometry manager requests).
3652     */
3653    
3654     if (!(wmPtr->flags & WM_SYNC_PENDING)) {
3655     if (!(pos->flags & SWP_NOSIZE)) {
3656     if ((wmPtr->width == -1)
3657     && (winPtr->changes.width == winPtr->reqWidth)) {
3658     /*
3659     * Don't set external width, since the user didn't
3660     * change it from what the widgets asked for.
3661     */
3662     } else {
3663     if (wmPtr->gridWin != NULL) {
3664     wmPtr->width = wmPtr->reqGridWidth
3665     + (winPtr->changes.width - winPtr->reqWidth)
3666     / wmPtr->widthInc;
3667     if (wmPtr->width < 0) {
3668     wmPtr->width = 0;
3669     }
3670     } else {
3671     wmPtr->width = winPtr->changes.width;
3672     }
3673     }
3674     if ((wmPtr->height == -1)
3675     && (winPtr->changes.height == winPtr->reqHeight)) {
3676     /*
3677     * Don't set external height, since the user didn't change
3678     * it from what the widgets asked for.
3679     */
3680     } else {
3681     if (wmPtr->gridWin != NULL) {
3682     wmPtr->height = wmPtr->reqGridHeight
3683     + (winPtr->changes.height - winPtr->reqHeight)
3684     / wmPtr->heightInc;
3685     if (wmPtr->height < 0) {
3686     wmPtr->height = 0;
3687     }
3688     } else {
3689     wmPtr->height = winPtr->changes.height;
3690     }
3691     }
3692     wmPtr->configWidth = winPtr->changes.width;
3693     wmPtr->configHeight = winPtr->changes.height;
3694     }
3695     /*
3696     * If the user moved the window, we should switch back
3697     * to normal coordinates.
3698     */
3699    
3700     if (!(pos->flags & SWP_NOMOVE)) {
3701     wmPtr->flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y);
3702     }
3703     }
3704    
3705     /*
3706     * Update the wrapper window location information.
3707     */
3708    
3709     if (wmPtr->flags & WM_NEGATIVE_X) {
3710     wmPtr->x = DisplayWidth(winPtr->display, winPtr->screenNum)
3711     - winPtr->changes.x - (winPtr->changes.width
3712     + wmPtr->borderWidth);
3713     } else {
3714     wmPtr->x = winPtr->changes.x;
3715     }
3716     if (wmPtr->flags & WM_NEGATIVE_Y) {
3717     wmPtr->y = DisplayHeight(winPtr->display, winPtr->screenNum)
3718     - winPtr->changes.y - (winPtr->changes.height
3719     + wmPtr->borderHeight);
3720     } else {
3721     wmPtr->y = winPtr->changes.y;
3722     }
3723     }
3724     }
3725    
3726     /*
3727     *----------------------------------------------------------------------
3728     *
3729     * GenerateConfigureNotify --
3730     *
3731     * Generate a ConfigureNotify event from the current geometry
3732     * information for the specified toplevel window.
3733     *
3734     * Results:
3735     * None.
3736     *
3737     * Side effects:
3738     * Sends an X event.
3739     *
3740     *----------------------------------------------------------------------
3741     */
3742    
3743     static void
3744     GenerateConfigureNotify(winPtr)
3745     TkWindow *winPtr;
3746     {
3747     XEvent event;
3748    
3749     /*
3750     * Generate a ConfigureNotify event.
3751     */
3752    
3753     event.type = ConfigureNotify;
3754     event.xconfigure.serial = winPtr->display->request;
3755     event.xconfigure.send_event = False;
3756     event.xconfigure.display = winPtr->display;
3757     event.xconfigure.event = winPtr->window;
3758     event.xconfigure.window = winPtr->window;
3759     event.xconfigure.border_width = winPtr->changes.border_width;
3760     event.xconfigure.override_redirect = winPtr->atts.override_redirect;
3761     event.xconfigure.x = winPtr->changes.x;
3762     event.xconfigure.y = winPtr->changes.y;
3763     event.xconfigure.width = winPtr->changes.width;
3764     event.xconfigure.height = winPtr->changes.height;
3765     event.xconfigure.above = None;
3766     Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
3767     }
3768    
3769     /*
3770     *----------------------------------------------------------------------
3771     *
3772     * InstallColormaps --
3773     *
3774     * Installs the colormaps associated with the toplevel which is
3775     * currently active.
3776     *
3777     * Results:
3778     * None.
3779     *
3780     * Side effects:
3781     * May change the system palette and generate damage.
3782     *
3783     *----------------------------------------------------------------------
3784     */
3785    
3786     static int
3787     InstallColormaps(hwnd, message, isForemost)
3788     HWND hwnd; /* Toplevel wrapper window whose colormaps
3789     * should be installed. */
3790     int message; /* Either WM_PALETTECHANGED or
3791     * WM_QUERYNEWPALETTE */
3792     int isForemost; /* 1 if window is foremost, else 0 */
3793     {
3794     int i;
3795     HDC dc;
3796     HPALETTE oldPalette;
3797     TkWindow *winPtr = GetTopLevel(hwnd);
3798     WmInfo *wmPtr;
3799     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
3800     Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
3801    
3802     if (winPtr == NULL) {
3803     return 0;
3804     }
3805    
3806     wmPtr = winPtr->wmInfoPtr;
3807    
3808     if (message == WM_QUERYNEWPALETTE) {
3809     /*
3810     * Case 1: This window is about to become the foreground window, so we
3811     * need to install the primary palette. If the system palette was
3812     * updated, then Windows will generate a WM_PALETTECHANGED message.
3813     * Otherwise, we have to synthesize one in order to ensure that the
3814     * secondary palettes are installed properly.
3815     */
3816    
3817     winPtr->dispPtr->foregroundWmPtr = wmPtr;
3818    
3819     if (wmPtr->cmapCount > 0) {
3820     winPtr = wmPtr->cmapList[0];
3821     }
3822    
3823     tsdPtr->systemPalette = TkWinGetPalette(winPtr->atts.colormap);
3824     dc = GetDC(hwnd);
3825     oldPalette = SelectPalette(dc, tsdPtr->systemPalette, FALSE);
3826     if (RealizePalette(dc)) {
3827     RefreshColormap(winPtr->atts.colormap, winPtr->dispPtr);
3828     } else if (wmPtr->cmapCount > 1) {
3829     SelectPalette(dc, oldPalette, TRUE);
3830     RealizePalette(dc);
3831     ReleaseDC(hwnd, dc);
3832     SendMessage(hwnd, WM_PALETTECHANGED, (WPARAM)hwnd,
3833     (LPARAM)NULL);
3834     return TRUE;
3835     }
3836    
3837     } else {
3838     /*
3839     * Window is being notified of a change in the system palette.
3840     * If this window is the foreground window, then we should only
3841     * install the secondary palettes, since the primary was installed
3842     * in response to the WM_QUERYPALETTE message. Otherwise, install
3843     * all of the palettes.
3844     */
3845    
3846    
3847     if (!isForemost) {
3848     if (wmPtr->cmapCount > 0) {
3849     winPtr = wmPtr->cmapList[0];
3850     }
3851     i = 1;
3852     } else {
3853     if (wmPtr->cmapCount <= 1) {
3854     return TRUE;
3855     }
3856     winPtr = wmPtr->cmapList[1];
3857     i = 2;
3858     }
3859     dc = GetDC(hwnd);
3860     oldPalette = SelectPalette(dc,
3861     TkWinGetPalette(winPtr->atts.colormap), TRUE);
3862     if (RealizePalette(dc)) {
3863     RefreshColormap(winPtr->atts.colormap, winPtr->dispPtr);
3864     }
3865     for (; i < wmPtr->cmapCount; i++) {
3866     winPtr = wmPtr->cmapList[i];
3867     SelectPalette(dc, TkWinGetPalette(winPtr->atts.colormap), TRUE);
3868     if (RealizePalette(dc)) {
3869     RefreshColormap(winPtr->atts.colormap, winPtr->dispPtr);
3870     }
3871     }
3872     }
3873    
3874     SelectPalette(dc, oldPalette, TRUE);
3875     RealizePalette(dc);
3876     ReleaseDC(hwnd, dc);
3877     return TRUE;
3878     }
3879    
3880     /*
3881     *----------------------------------------------------------------------
3882     *
3883     * RefreshColormap --
3884     *
3885     * This function is called to force all of the windows that use
3886     * a given colormap to redraw themselves. The quickest way to
3887     * do this is to iterate over the toplevels, looking in the
3888     * cmapList for matches. This will quickly eliminate subtrees
3889     * that don't use a given colormap.
3890     *
3891     * Results:
3892     * None.
3893     *
3894     * Side effects:
3895     * Causes damage events to be generated.
3896     *
3897     *----------------------------------------------------------------------
3898     */
3899    
3900     static void
3901     RefreshColormap(colormap, dispPtr)
3902     Colormap colormap;
3903     TkDisplay *dispPtr;
3904     {
3905