home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / tcl / tk3.3b1 / tkWm.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-08  |  98.2 KB  |  3,253 lines

  1. /* 
  2.  * tkWm.c --
  3.  *
  4.  *    This module takes care of the interactions between a Tk-based
  5.  *    application and the window manager.  Among other things, it
  6.  *    implements the "wm" command and passes geometry information
  7.  *    to the window manager.
  8.  *
  9.  * Copyright (c) 1991-1993 The Regents of the University of California.
  10.  * All rights reserved.
  11.  *
  12.  * Permission is hereby granted, without written agreement and without
  13.  * license or royalty fees, to use, copy, modify, and distribute this
  14.  * software and its documentation for any purpose, provided that the
  15.  * above copyright notice and the following two paragraphs appear in
  16.  * all copies of this software.
  17.  * 
  18.  * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
  19.  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
  20.  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
  21.  * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  22.  *
  23.  * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  24.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  25.  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  26.  * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
  27.  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  28.  */
  29.  
  30. #ifndef lint
  31. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkWm.c,v 1.68 93/07/08 10:47:11 ouster Exp $ SPRITE (Berkeley)";
  32. #endif
  33.  
  34. #include "tkConfig.h"
  35. #include "tkInt.h"
  36. #include <errno.h>
  37.  
  38. /*
  39.  * A data structure of the following type holds information for
  40.  * each window manager protocol (such as WM_DELETE_WINDOW) for
  41.  * which a handler (i.e. a Tcl command) has been defined for a
  42.  * particular top-level window.
  43.  */
  44.  
  45. typedef struct ProtocolHandler {
  46.     Atom protocol;        /* Identifies the protocol. */
  47.     struct ProtocolHandler *nextPtr;
  48.                 /* Next in list of protocol handlers for
  49.                  * the same top-level window, or NULL for
  50.                  * end of list. */
  51.     Tcl_Interp *interp;        /* Interpreter in which to invoke command. */
  52.     char command[4];        /* Tcl command to invoke when a client
  53.                  * message for this protocol arrives. 
  54.                  * The actual size of the structure varies
  55.                  * to accommodate the needs of the actual
  56.                  * command. THIS MUST BE THE LAST FIELD OF
  57.                  * THE STRUCTURE. */
  58. } ProtocolHandler;
  59.  
  60. #define HANDLER_SIZE(cmdLength) \
  61.     ((unsigned) (sizeof(ProtocolHandler) - 3 + cmdLength))
  62.  
  63. /*
  64.  * A data structure of the following type holds window-manager-related
  65.  * information for each top-level window in an application.
  66.  */
  67.  
  68. typedef struct TkWmInfo {
  69.     TkWindow *winPtr;        /* Pointer to main Tk information for
  70.                  * this window. */
  71.     Window reparent;        /* If the window has been reparented, this
  72.                  * gives the ID of the ancestor of the window
  73.                  * that is a child of the root window (may
  74.                  * not be window's immediate parent).  If
  75.                  * the window isn't reparented, this has the
  76.                  * value None. */
  77.     Tk_Uid titleUid;        /* Title to display in window caption.  If
  78.                  * NULL, use name of widget. */
  79.     Tk_Uid iconName;        /* Name to display in icon. */
  80.     Window master;        /* Master window for TRANSIENT_FOR property,
  81.                  * or None. */
  82.     XWMHints hints;        /* Various pieces of information for
  83.                  * window manager. */
  84.     Tk_Uid leaderName;        /* Path name of leader of window group
  85.                  * (corresponds to hints.window_group).
  86.                  * Note:  this field doesn't get updated
  87.                  * if leader is destroyed. */
  88.     Tk_Uid iconWindowName;    /* Path name of window specified as icon
  89.                  * window for this window, or NULL.  Note:
  90.                  * this field doesn't get updated if
  91.                  * iconWindowName is destroyed. */
  92.     Tk_Uid masterWindowName;    /* Path name of window specified as master
  93.                  * in "wm transient" command, or NULL.
  94.                  * Note:  this field doesn't get updated if
  95.                  * masterWindowName is destroyed. */
  96.     int withdrawn;        /* Non-zero means window has been withdrawn. */
  97.  
  98.     /*
  99.      * Information used to construct an XSizeHints structure for
  100.      * the window manager:
  101.      */
  102.  
  103.     int sizeHintsFlags;        /* Flags word for XSizeHints structure.
  104.                  * If the PBaseSize flag is set then the
  105.                  * window is gridded;  otherwise it isn't
  106.                  * gridded. */
  107.     int minWidth, minHeight;    /* Minimum dimensions of window, in
  108.                  * grid units, not pixels. */
  109.     int maxWidth, maxHeight;    /* Maximum dimensions of window, in
  110.                  * grid units, not pixels. */
  111.     int widthInc, heightInc;    /* Increments for size changes (# pixels
  112.                  * per step). */
  113.     struct {
  114.     int x;    /* numerator */
  115.     int y;  /* denominator */
  116.     } minAspect, maxAspect;    /* Min/max aspect ratios for window. */
  117.     int reqGridWidth, reqGridHeight;
  118.                 /* The dimensions of the window (in
  119.                  * grid units) requested through
  120.                  * the geometry manager. */
  121.     int gravity;        /* Desired window gravity. */
  122.  
  123.     /*
  124.      * Information used to manage the size and location of a window.
  125.      */
  126.  
  127.     int width, height;        /* Desired dimensions of window, specified
  128.                  * in grid units.  These values are
  129.                  * set by the "wm geometry" command and by
  130.                  * ConfigureNotify events (for when wm
  131.                  * resizes window).  -1 means user hasn't
  132.                  * requested dimensions. */
  133.     int x, y;            /* Desired X and Y coordinates for window.
  134.                  * These values are set by "wm geometry",
  135.                  * plus by ConfigureNotify events (when wm
  136.                  * moves window).  These numbers are
  137.                  * different than the numbers stored in
  138.                  * winPtr->changes because (a) they could be
  139.                  * measured from the right or bottom edge
  140.                  * of the screen (see WM_NEGATIVE_X and
  141.                  * WM_NEGATIVE_Y flags) and (b) if the window
  142.                  * has been reparented then they refer to the
  143.                  * parent rather than the window itself. */
  144.     int parentWidth, parentHeight;
  145.                 /* Width and height of reparent, in pixels
  146.                  * *including border*.  If window hasn't been
  147.                  * reparented then these will be the outer
  148.                  * dimensions of the window, including
  149.                  * border. */
  150.     int xInParent, yInParent;    /* Offset of window within reparent,  measured
  151.                  * from upper-left outer corner of parent's
  152.                  * border to upper-left outer corner of child's
  153.                  * border.  If not reparented then these are
  154.                  * zero. */
  155.     int configWidth, configHeight;
  156.                 /* Dimensions passed to last request that we
  157.                  * issued to change geometry of window.  Used
  158.                  * to eliminate redundant resize operations. */
  159.  
  160.     /*
  161.      * Information about the virtual root window for this top-level,
  162.      * if there is one.
  163.      */
  164.  
  165.     Window vRoot;        /* Virtual root window for this top-level,
  166.                  * or None if there is no virtual root
  167.                  * window (i.e. just use the screen's root). */
  168.     int vRootX, vRootY;        /* Position of the virtual root inside the
  169.                  * root window.  If the WM_VROOT_OFFSET_STALE
  170.                  * flag is set then this information may be
  171.                  * incorrect and needs to be refreshed from
  172.                  * the X server.  If vRoot is NULL then these
  173.                  * values are both 0. */
  174.     unsigned int vRootWidth, vRootHeight;
  175.                 /* Dimensions of the virtual root window.
  176.                  * If vRoot is NULL, gives the dimensions
  177.                  * of the containing screen.  This information
  178.                  * is never stale, even though vRootX and
  179.                  * vRootY can be. */
  180.  
  181.     /*
  182.      * Miscellaneous information.
  183.      */
  184.  
  185.     ProtocolHandler *protPtr;    /* First in list of protocol handlers for
  186.                  * this window (NULL means none). */
  187.     int cmdArgc;        /* Number of elements in cmdArgv below. */
  188.     char **cmdArgv;        /* Array of strings to store in the
  189.                  * WM_COMMAND property.  NULL means nothing
  190.                  * available. */
  191.     char *clientMachine;    /* String to store in WM_CLIENT_MACHINE
  192.                  * property, or NULL. */
  193.     int flags;            /* Miscellaneous flags, defined below. */
  194.     struct TkWmInfo *nextPtr;    /* Next in list of all top-level windows. */
  195. } WmInfo;
  196.  
  197. /*
  198.  * Flag values for WmInfo structures:
  199.  *
  200.  * WM_NEVER_MAPPED -        non-zero means window has never been
  201.  *                mapped;  need to update all info when
  202.  *                window is first mapped.
  203.  * WM_UPDATE_PENDING -        non-zero means a call to UpdateGeometryInfo
  204.  *                has already been scheduled for this
  205.  *                window;  no need to schedule another one.
  206.  * WM_NEGATIVE_X -        non-zero means x-coordinate is measured in
  207.  *                pixels from right edge of screen, rather
  208.  *                than from left edge.
  209.  * WM_NEGATIVE_Y -        non-zero means y-coordinate is measured in
  210.  *                pixels up from bottom of screen, rather than
  211.  *                down from top.
  212.  * WM_UPDATE_SIZE_HINTS -    non-zero means that new size hints need to be
  213.  *                propagated to window manager.
  214.  * WM_SYNC_PENDING -        set to non-zero while waiting for the window
  215.  *                manager to respond to some state change.
  216.  * WM_VROOT_OFFSET_STALE -    non-zero means that (x,y) offset information
  217.  *                about the virtual root window is stale and
  218.  *                needs to be fetched fresh from the X server.
  219.  * WM_ABOUT_TO_MAP -        non-zero means that the window is about to
  220.  *                be mapped by TkWmMapWindow.  This is used
  221.  *                by UpdateGeometryInfo to modify its behavior.
  222.  * WM_MOVE_PENDING -        non-zero means the application has requested
  223.  *                a new position for the window, but it hasn't
  224.  *                been reflected through the window manager
  225.  *                yet.
  226.  */
  227.  
  228. #define WM_NEVER_MAPPED        1
  229. #define WM_UPDATE_PENDING    2
  230. #define WM_NEGATIVE_X        4
  231. #define WM_NEGATIVE_Y        8
  232. #define WM_UPDATE_SIZE_HINTS    0x10
  233. #define WM_SYNC_PENDING        0x20
  234. #define WM_VROOT_OFFSET_STALE    0x40
  235. #define WM_ABOUT_TO_MAP        0x100
  236. #define WM_MOVE_PENDING        0x200
  237.  
  238. /*
  239.  * This module keeps a list of all top-level windows, primarily to
  240.  * simplify the job of Tk_CoordsToWindow.
  241.  */
  242.  
  243. static WmInfo *firstWmPtr = NULL;    /* Points to first top-level window. */
  244.  
  245. #define IS_GRIDDED(wmPtr) ((wmPtr)->sizeHintsFlags & PBaseSize)
  246.  
  247. /*
  248.  * The variable below is used to enable or disable tracing in this
  249.  * module.  If tracing is enabled, then information is printed on
  250.  * standard output about interesting interactions with the window
  251.  * manager.
  252.  */
  253.  
  254. static int wmTracing = 0;
  255.  
  256. /*
  257.  * Forward declarations for procedures defined in this file:
  258.  */
  259.  
  260. static int        ComputeReparentGeometry _ANSI_ARGS_((TkWindow *winPtr));
  261. static void        ConfigureEvent _ANSI_ARGS_((TkWindow *winPtr,
  262.                 XConfigureEvent *eventPtr));
  263. static int        ParseGeometry _ANSI_ARGS_((Tcl_Interp *interp,
  264.                 char *string, TkWindow *winPtr));
  265. static void        ReparentEvent _ANSI_ARGS_((TkWindow *winPtr,
  266.                 XReparentEvent *eventPtr));
  267. static void        TopLevelEventProc _ANSI_ARGS_((ClientData clientData,
  268.                 XEvent *eventPtr));
  269. static void        TopLevelReqProc _ANSI_ARGS_((ClientData dummy,
  270.                 Tk_Window tkwin));
  271. static void        UpdateGeometryInfo _ANSI_ARGS_((
  272.                 ClientData clientData));
  273. static void        UpdateHints _ANSI_ARGS_((TkWindow *winPtr));
  274. static void        UpdateSizeHints _ANSI_ARGS_((TkWindow *winPtr));
  275. static void        UpdateVRootGeometry _ANSI_ARGS_((WmInfo *wmPtr));
  276. static void        UpdateWmProtocols _ANSI_ARGS_((WmInfo *wmPtr));
  277. static void        WaitForConfigureNotify _ANSI_ARGS_((TkWindow *winPtr,
  278.                 unsigned long serial));
  279. static int        WaitForEvent _ANSI_ARGS_((Display *display,
  280.                 Window window, long mask, XEvent *eventPtr));
  281. static void        WaitForMapNotify _ANSI_ARGS_((TkWindow *winPtr,
  282.                 int mapped));
  283.  
  284. /*
  285.  *--------------------------------------------------------------
  286.  *
  287.  * TkWmNewWindow --
  288.  *
  289.  *    This procedure is invoked whenever a new top-level
  290.  *    window is created.  Its job is to initialize the WmInfo
  291.  *    structure for the window.
  292.  *
  293.  * Results:
  294.  *    None.
  295.  *
  296.  * Side effects:
  297.  *    A WmInfo structure gets allocated and initialized.
  298.  *
  299.  *--------------------------------------------------------------
  300.  */
  301.  
  302. void
  303. TkWmNewWindow(winPtr)
  304.     TkWindow *winPtr;        /* Newly-created top-level window. */
  305. {
  306.     register WmInfo *wmPtr;
  307.  
  308.     wmPtr = (WmInfo *) ckalloc(sizeof(WmInfo));
  309.     wmPtr->winPtr = winPtr;
  310.     wmPtr->reparent = None;
  311.     wmPtr->titleUid = NULL;
  312.     wmPtr->iconName = NULL;
  313.     wmPtr->master = None;
  314.     wmPtr->hints.flags = InputHint | StateHint;
  315.     wmPtr->hints.input = True;
  316.     wmPtr->hints.initial_state = NormalState;
  317.     wmPtr->hints.icon_pixmap = None;
  318.     wmPtr->hints.icon_window = None;
  319.     wmPtr->hints.icon_x = wmPtr->hints.icon_y = 0;
  320.     wmPtr->hints.icon_mask = None;
  321.     wmPtr->hints.window_group = None;
  322.     wmPtr->leaderName = NULL;
  323.     wmPtr->iconWindowName = NULL;
  324.     wmPtr->masterWindowName = NULL;
  325.     wmPtr->withdrawn = 0;
  326.     wmPtr->sizeHintsFlags = 0;
  327.     wmPtr->minWidth = wmPtr->minHeight = 0;
  328.     wmPtr->maxWidth = wmPtr->maxHeight = 10000;
  329.     wmPtr->widthInc = wmPtr->heightInc = 1;
  330.     wmPtr->minAspect.x = wmPtr->minAspect.y = 1;
  331.     wmPtr->maxAspect.x = wmPtr->maxAspect.y = 1;
  332.     wmPtr->reqGridWidth = wmPtr->reqGridHeight = -1;
  333.     wmPtr->gravity = NorthWestGravity;
  334.     wmPtr->width = -1;
  335.     wmPtr->height = -1;
  336.     wmPtr->x = winPtr->changes.x;
  337.     wmPtr->y = winPtr->changes.y;
  338.     wmPtr->parentWidth = winPtr->changes.width
  339.         + 2*winPtr->changes.border_width;
  340.     wmPtr->parentHeight = winPtr->changes.height
  341.         + 2*winPtr->changes.border_width;
  342.     wmPtr->xInParent = wmPtr->yInParent = 0;
  343.     wmPtr->configWidth = -1;
  344.     wmPtr->configHeight = -1;
  345.     wmPtr->vRoot = None;
  346.     wmPtr->protPtr = NULL;
  347.     wmPtr->cmdArgv = NULL;
  348.     wmPtr->clientMachine = NULL;
  349.     wmPtr->flags = WM_NEVER_MAPPED;
  350.     wmPtr->nextPtr = firstWmPtr;
  351.     firstWmPtr = wmPtr;
  352.     winPtr->wmInfoPtr = wmPtr;
  353.  
  354.     UpdateVRootGeometry(wmPtr);
  355.  
  356.     /*
  357.      * Tk must monitor structure events for top-level windows, in order
  358.      * to detect size and position changes caused by window managers.
  359.      */
  360.  
  361.     Tk_CreateEventHandler((Tk_Window) winPtr, StructureNotifyMask,
  362.         TopLevelEventProc, (ClientData) winPtr);
  363.  
  364.     /*
  365.      * Arrange for geometry requests to be reflected from the window
  366.      * to the window manager.
  367.      */
  368.  
  369.     Tk_ManageGeometry((Tk_Window) winPtr, TopLevelReqProc, (ClientData) 0);
  370. }
  371.  
  372. /*
  373.  *--------------------------------------------------------------
  374.  *
  375.  * TkWmMapWindow --
  376.  *
  377.  *    This procedure is invoked to map a top-level window.  This
  378.  *    module gets a chance to update all window-manager-related
  379.  *    information in properties before the window manager sees
  380.  *    the map event and checks the properties.  It also gets to
  381.  *    decide whether or not to even map the window after all.
  382.  *
  383.  * Results:
  384.  *    None.
  385.  *
  386.  * Side effects:
  387.  *    Properties of winPtr may get updated to provide up-to-date
  388.  *    information to the window manager.  The window may also get
  389.  *    mapped, but it may not be if this procedure decides that
  390.  *    isn't appropriate (e.g. because the window is withdrawn).
  391.  *
  392.  *--------------------------------------------------------------
  393.  */
  394.  
  395. void
  396. TkWmMapWindow(winPtr)
  397.     TkWindow *winPtr;        /* Top-level window that's about to
  398.                  * be mapped. */
  399. {
  400.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  401.     XTextProperty textProp;
  402.  
  403.     if (wmPtr->flags & WM_NEVER_MAPPED) {
  404.     wmPtr->flags &= ~WM_NEVER_MAPPED;
  405.  
  406.     /*
  407.      * This is the first time this window has ever been mapped.
  408.      * Store all the window-manager-related information for the
  409.      * window.
  410.      */
  411.  
  412.     if (wmPtr->titleUid == NULL) {
  413.         wmPtr->titleUid = winPtr->nameUid;
  414.     }
  415.     if (XStringListToTextProperty(&wmPtr->titleUid, 1, &textProp)  != 0) {
  416.         XSetWMName(winPtr->display, winPtr->window, &textProp);
  417.         XFree((char *) textProp.value);
  418.     }
  419.     
  420.     TkWmSetClass(winPtr);
  421.     
  422.     if (wmPtr->iconName != NULL) {
  423.         XSetIconName(winPtr->display, winPtr->window, wmPtr->iconName);
  424.     }
  425.     
  426.     if (wmPtr->master != None) {
  427.         XSetTransientForHint(winPtr->display, winPtr->window,
  428.             wmPtr->master);
  429.     }
  430.     
  431.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  432.     UpdateHints(winPtr);
  433.     UpdateWmProtocols(wmPtr);
  434.     if (wmPtr->cmdArgv != NULL) {
  435.         XSetCommand(winPtr->display, winPtr->window, wmPtr->cmdArgv,
  436.             wmPtr->cmdArgc);
  437.     }
  438.     if (wmPtr->clientMachine != NULL) {
  439.         if (XStringListToTextProperty(&wmPtr->clientMachine, 1, &textProp)
  440.             != 0) {
  441.         XSetWMClientMachine(winPtr->display, winPtr->window,
  442.             &textProp);
  443.         XFree((char *) textProp.value);
  444.         }
  445.     }
  446.     }
  447.     if (wmPtr->hints.initial_state == WithdrawnState) {
  448.     return;
  449.     }
  450.     wmPtr->flags |= WM_ABOUT_TO_MAP;
  451.     if (wmPtr->flags & WM_UPDATE_PENDING) {
  452.     Tk_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
  453.     }
  454.     UpdateGeometryInfo((ClientData) winPtr);
  455.     wmPtr->flags &= ~WM_ABOUT_TO_MAP;
  456.  
  457.     /*
  458.      * Map the window, then wait to be sure that the window manager has
  459.      * processed the map operation.
  460.      */
  461.  
  462.     XMapWindow(winPtr->display, winPtr->window);
  463.     if (wmPtr->hints.initial_state == NormalState) {
  464.     WaitForMapNotify(winPtr, 1);
  465.     }
  466. }
  467.  
  468. /*
  469.  *--------------------------------------------------------------
  470.  *
  471.  * TkWmUnmapWindow --
  472.  *
  473.  *    This procedure is invoked to unmap a top-level window.  The
  474.  *    only thing it does special is to wait for the window actually
  475.  *    to be unmapped.
  476.  *
  477.  * Results:
  478.  *    None.
  479.  *
  480.  * Side effects:
  481.  *    Unmaps the window.
  482.  *
  483.  *--------------------------------------------------------------
  484.  */
  485.  
  486. void
  487. TkWmUnmapWindow(winPtr)
  488.     TkWindow *winPtr;        /* Top-level window that's about to
  489.                  * be mapped. */
  490. {
  491.     /*
  492.      * It seems to be important to wait after unmapping a top-level
  493.      * window until the window really gets unmapped.  I don't completely
  494.      * understand all the interactions with the window manager, but if
  495.      * we go on without waiting, and if the window is then mapped again
  496.      * quickly, events seem to get lost so that we think the window isn't
  497.      * mapped when in fact it is mapped.  I suspect that this has something
  498.      * to do with the window manager filtering Map events (and possily not
  499.      * filtering Unmap events?).
  500.      */ 
  501.     XUnmapWindow(winPtr->display, winPtr->window);
  502.     WaitForMapNotify(winPtr, 0);
  503. }
  504.  
  505. /*
  506.  *--------------------------------------------------------------
  507.  *
  508.  * TkWmDeadWindow --
  509.  *
  510.  *    This procedure is invoked when a top-level window is
  511.  *    about to be deleted.  It cleans up the wm-related data
  512.  *    structures for the window.
  513.  *
  514.  * Results:
  515.  *    None.
  516.  *
  517.  * Side effects:
  518.  *    The WmInfo structure for winPtr gets freed up.
  519.  *
  520.  *--------------------------------------------------------------
  521.  */
  522.  
  523. void
  524. TkWmDeadWindow(winPtr)
  525.     TkWindow *winPtr;        /* Top-level window that's being deleted. */
  526. {
  527.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  528.  
  529.     if (wmPtr == NULL) {
  530.     return;
  531.     }
  532.     if (firstWmPtr == wmPtr) {
  533.     firstWmPtr = wmPtr->nextPtr;
  534.     } else {
  535.     register WmInfo *prevPtr;
  536.  
  537.     for (prevPtr = firstWmPtr; ; prevPtr = prevPtr->nextPtr) {
  538.         if (prevPtr == NULL) {
  539.         panic("couldn't unlink window in TkWmDeadWindow");
  540.         }
  541.         if (prevPtr->nextPtr == wmPtr) {
  542.         prevPtr->nextPtr = wmPtr->nextPtr;
  543.         break;
  544.         }
  545.     }
  546.     }
  547.     if (wmPtr->hints.flags & IconPixmapHint) {
  548.     Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap);
  549.     }
  550.     if (wmPtr->hints.flags & IconMaskHint) {
  551.     Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_mask);
  552.     }
  553.     while (wmPtr->protPtr != NULL) {
  554.     ProtocolHandler *protPtr;
  555.  
  556.     protPtr = wmPtr->protPtr;
  557.     wmPtr->protPtr = protPtr->nextPtr;
  558.     Tk_EventuallyFree((ClientData) protPtr, (Tk_FreeProc *) free);
  559.     }
  560.     if (wmPtr->cmdArgv != NULL) {
  561.     ckfree((char *) wmPtr->cmdArgv);
  562.     }
  563.     if (wmPtr->clientMachine != NULL) {
  564.     ckfree((char *) wmPtr->clientMachine);
  565.     }
  566.     if (wmPtr->flags & WM_UPDATE_PENDING) {
  567.     Tk_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
  568.     }
  569.     ckfree((char *) wmPtr);
  570.     winPtr->wmInfoPtr = NULL;
  571. }
  572.  
  573. /*
  574.  *--------------------------------------------------------------
  575.  *
  576.  * TkWmSetClass --
  577.  *
  578.  *    This procedure is invoked whenever a top-level window's
  579.  *    class is changed.  If the window has been mapped then this
  580.  *    procedure updates the window manager property for the
  581.  *    class.  If the window hasn't been mapped, the update is
  582.  *    deferred until just before the first mapping.
  583.  *
  584.  * Results:
  585.  *    None.
  586.  *
  587.  * Side effects:
  588.  *    A window property may get updated.
  589.  *
  590.  *--------------------------------------------------------------
  591.  */
  592.  
  593. void
  594. TkWmSetClass(winPtr)
  595.     TkWindow *winPtr;        /* Newly-created top-level window. */
  596. {
  597.     if (winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
  598.     return;
  599.     }
  600.  
  601.     if (winPtr->classUid != NULL) {
  602.     XClassHint *classPtr;
  603.  
  604.     classPtr = XAllocClassHint();
  605.     classPtr->res_name = winPtr->nameUid;
  606.     classPtr->res_class = winPtr->classUid;
  607.     XSetClassHint(winPtr->display, winPtr->window, classPtr);
  608.     XFree((char *) classPtr);
  609.     }
  610. }
  611.  
  612. /*
  613.  *----------------------------------------------------------------------
  614.  *
  615.  * Tk_WmCmd --
  616.  *
  617.  *    This procedure is invoked to process the "wm" Tcl command.
  618.  *    See the user documentation for details on what it does.
  619.  *
  620.  * Results:
  621.  *    A standard Tcl result.
  622.  *
  623.  * Side effects:
  624.  *    See the user documentation.
  625.  *
  626.  *----------------------------------------------------------------------
  627.  */
  628.  
  629.     /* ARGSUSED */
  630. int
  631. Tk_WmCmd(clientData, interp, argc, argv)
  632.     ClientData clientData;    /* Main window associated with
  633.                  * interpreter. */
  634.     Tcl_Interp *interp;        /* Current interpreter. */
  635.     int argc;            /* Number of arguments. */
  636.     char **argv;        /* Argument strings. */
  637. {
  638.     Tk_Window tkwin = (Tk_Window) clientData;
  639.     TkWindow *winPtr;
  640.     register WmInfo *wmPtr;
  641.     char c;
  642.     int length;
  643.  
  644.     if (argc < 2) {
  645.     wrongNumArgs:
  646.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  647.         argv[0], " option window ?arg ...?\"", (char *) NULL);
  648.     return TCL_ERROR;
  649.     }
  650.     c = argv[1][0];
  651.     length = strlen(argv[1]);
  652.     if ((c == 't') && (strncmp(argv[1], "tracing", length) == 0)
  653.         && (length >= 3)) {
  654.     if ((argc != 2) && (argc != 3)) {
  655.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  656.             argv[0], " tracing ?boolean?\"", (char *) NULL);
  657.         return TCL_ERROR;
  658.     }
  659.     if (argc == 2) {
  660.         interp->result = (wmTracing) ? "on" : "off";
  661.         return TCL_OK;
  662.     }
  663.     return Tcl_GetBoolean(interp, argv[2], &wmTracing);
  664.     }
  665.  
  666.     if (argc < 3) {
  667.     goto wrongNumArgs;
  668.     }
  669.     winPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
  670.     if (winPtr == NULL) {
  671.     return TCL_ERROR;
  672.     }
  673.     if (!(winPtr->flags & TK_TOP_LEVEL)) {
  674.     Tcl_AppendResult(interp, "window \"", winPtr->pathName,
  675.         "\" isn't a top-level window", (char *) NULL);
  676.     return TCL_ERROR;
  677.     }
  678.     wmPtr = winPtr->wmInfoPtr;
  679.     if ((c == 'a') && (strncmp(argv[1], "aspect", length) == 0)) {
  680.     int numer1, denom1, numer2, denom2;
  681.  
  682.     if ((argc != 3) && (argc != 7)) {
  683.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  684.             argv[0], " aspect window ?minNumer minDenom ",
  685.             "maxNumer maxDenom?\"", (char *) NULL);
  686.         return TCL_ERROR;
  687.     }
  688.     if (argc == 3) {
  689.         if (wmPtr->sizeHintsFlags & PAspect) {
  690.         sprintf(interp->result, "%d %d %d %d", wmPtr->minAspect.x,
  691.             wmPtr->minAspect.y, wmPtr->maxAspect.x,
  692.             wmPtr->maxAspect.y);
  693.         }
  694.         return TCL_OK;
  695.     }
  696.     if (*argv[3] == '\0') {
  697.         wmPtr->sizeHintsFlags &= ~PAspect;
  698.     } else {
  699.         if ((Tcl_GetInt(interp, argv[3], &numer1) != TCL_OK)
  700.             || (Tcl_GetInt(interp, argv[4], &denom1) != TCL_OK)
  701.             || (Tcl_GetInt(interp, argv[5], &numer2) != TCL_OK)
  702.             || (Tcl_GetInt(interp, argv[6], &denom2) != TCL_OK)) {
  703.         return TCL_ERROR;
  704.         }
  705.         if ((numer1 <= 0) || (denom1 <= 0) || (numer2 <= 0) ||
  706.             (denom2 <= 0)) {
  707.         interp->result = "aspect number can't be <= 0";
  708.         return TCL_ERROR;
  709.         }
  710.         wmPtr->minAspect.x = numer1;
  711.         wmPtr->minAspect.y = denom1;
  712.         wmPtr->maxAspect.x = numer2;
  713.         wmPtr->maxAspect.y = denom2;
  714.         wmPtr->sizeHintsFlags |= PAspect;
  715.     }
  716.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  717.     goto updateGeom;
  718.     } else if ((c == 'c') && (strncmp(argv[1], "client", length) == 0)
  719.         && (length >= 2)) {
  720.     if ((argc != 3) && (argc != 4)) {
  721.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  722.             argv[0], " client window ?name?\"",
  723.             (char *) NULL);
  724.         return TCL_ERROR;
  725.     }
  726.     if (argc == 3) {
  727.         if (wmPtr->clientMachine != NULL) {
  728.         interp->result = wmPtr->clientMachine;
  729.         }
  730.         return TCL_OK;
  731.     }
  732.     if (argv[3][0] == 0) {
  733.         if (wmPtr->clientMachine != NULL) {
  734.         ckfree((char *) wmPtr->clientMachine);
  735.         wmPtr->clientMachine = NULL;
  736.         if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  737.             XDeleteProperty(winPtr->display, winPtr->window,
  738.                 Tk_InternAtom((Tk_Window) winPtr,
  739.                 "WM_CLIENT_MACHINE"));
  740.         }
  741.         }
  742.         return TCL_OK;
  743.     }
  744.     if (wmPtr->clientMachine != NULL) {
  745.         ckfree((char *) wmPtr->clientMachine);
  746.     }
  747.     wmPtr->clientMachine = (char *)
  748.         ckalloc((unsigned) (strlen(argv[3]) + 1));
  749.     strcpy(wmPtr->clientMachine, argv[3]);
  750.     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  751.         XTextProperty textProp;
  752.         if (XStringListToTextProperty(&wmPtr->clientMachine, 1, &textProp)
  753.             != 0) {
  754.         XSetWMClientMachine(winPtr->display, winPtr->window,
  755.             &textProp);
  756.         XFree((char *) textProp.value);
  757.         }
  758.     }
  759.     } else if ((c == 'c') && (strncmp(argv[1], "command", length) == 0)
  760.         && (length >= 2)) {
  761.     int cmdArgc;
  762.     char **cmdArgv;
  763.  
  764.     if ((argc != 3) && (argc != 4)) {
  765.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  766.             argv[0], " command window ?value?\"",
  767.             (char *) NULL);
  768.         return TCL_ERROR;
  769.     }
  770.     if (argc == 3) {
  771.         if (wmPtr->cmdArgv != NULL) {
  772.         interp->result = Tcl_Merge(wmPtr->cmdArgc, wmPtr->cmdArgv);
  773.         interp->freeProc = (Tcl_FreeProc *) free;
  774.         }
  775.         return TCL_OK;
  776.     }
  777.     if (argv[3][0] == 0) {
  778.         if (wmPtr->cmdArgv != NULL) {
  779.         ckfree((char *) wmPtr->cmdArgv);
  780.         wmPtr->cmdArgv = NULL;
  781.         if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  782.             XDeleteProperty(winPtr->display, winPtr->window,
  783.                 Tk_InternAtom((Tk_Window) winPtr, "WM_COMMAND"));
  784.         }
  785.         }
  786.         return TCL_OK;
  787.     }
  788.     if (Tcl_SplitList(interp, argv[3], &cmdArgc, &cmdArgv) != TCL_OK) {
  789.         return TCL_ERROR;
  790.     }
  791.     if (wmPtr->cmdArgv != NULL) {
  792.         ckfree((char *) wmPtr->cmdArgv);
  793.     }
  794.     wmPtr->cmdArgc = cmdArgc;
  795.     wmPtr->cmdArgv = cmdArgv;
  796.     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  797.         XSetCommand(winPtr->display, winPtr->window, cmdArgv, cmdArgc);
  798.     }
  799.     } else if ((c == 'd') && (strncmp(argv[1], "deiconify", length) == 0)) {
  800.     if (argc != 3) {
  801.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  802.             argv[0], " deiconify window\"", (char *) NULL);
  803.         return TCL_ERROR;
  804.     }
  805.     wmPtr->hints.initial_state = NormalState;
  806.     wmPtr->withdrawn = 0;
  807.     if (wmPtr->flags & WM_NEVER_MAPPED) {
  808.         return TCL_OK;
  809.     }
  810.     UpdateHints(winPtr);
  811.     Tk_MapWindow((Tk_Window) winPtr);
  812.     } else if ((c == 'f') && (strncmp(argv[1], "focusmodel", length) == 0)
  813.         && (length >= 2)) {
  814.     if ((argc != 3) && (argc != 4)) {
  815.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  816.             argv[0], " focusmodel window ?active|passive?\"",
  817.             (char *) NULL);
  818.         return TCL_ERROR;
  819.     }
  820.     if (argc == 3) {
  821.         interp->result = wmPtr->hints.input ? "passive" : "active";
  822.         return TCL_OK;
  823.     }
  824.     c = argv[3][0];
  825.     length = strlen(argv[3]);
  826.     if ((c == 'a') && (strncmp(argv[3], "active", length) == 0)) {
  827.         wmPtr->hints.input = False;
  828.     } else if ((c == 'p') && (strncmp(argv[3], "passive", length) == 0)) {
  829.         wmPtr->hints.input = True;
  830.     } else {
  831.         Tcl_AppendResult(interp, "bad argument \"", argv[3],
  832.             "\": must be active or passive", (char *) NULL);
  833.         return TCL_ERROR;
  834.     }
  835.     UpdateHints(winPtr);
  836.     } else if ((c == 'f') && (strncmp(argv[1], "frame", length) == 0)
  837.         && (length >= 2)) {
  838.     Window window;
  839.  
  840.     if (argc != 3) {
  841.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  842.             argv[0], " frame window\"", (char *) NULL);
  843.         return TCL_ERROR;
  844.     }
  845.     window = wmPtr->reparent;
  846.     if (window == None) {
  847.         window = Tk_WindowId((Tk_Window) winPtr);
  848.     }
  849.     sprintf(interp->result, "0x%x", window);
  850.     } else if ((c == 'g') && (strncmp(argv[1], "geometry", length) == 0)
  851.         && (length >= 2)) {
  852.     char xSign, ySign;
  853.     int width, height;
  854.  
  855.     if ((argc != 3) && (argc != 4)) {
  856.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  857.             argv[0], " geometry window ?newGeometry?\"",
  858.             (char *) NULL);
  859.         return TCL_ERROR;
  860.     }
  861.     if (argc == 3) {
  862.         xSign = (wmPtr->flags & WM_NEGATIVE_X) ? '-' : '+';
  863.         ySign = (wmPtr->flags & WM_NEGATIVE_Y) ? '-' : '+';
  864.         if (IS_GRIDDED(wmPtr)) {
  865.         width = wmPtr->reqGridWidth + (winPtr->changes.width
  866.             - winPtr->reqWidth)/wmPtr->widthInc;
  867.         height = wmPtr->reqGridHeight + (winPtr->changes.height
  868.             - winPtr->reqHeight)/wmPtr->heightInc;
  869.         } else {
  870.         width = winPtr->changes.width;
  871.         height = winPtr->changes.height;
  872.         }
  873.         sprintf(interp->result, "%dx%d%c%d%c%d", width, height,
  874.             xSign, wmPtr->x, ySign, wmPtr->y);
  875.         return TCL_OK;
  876.     }
  877.     if (*argv[3] == '\0') {
  878.         wmPtr->width = -1;
  879.         wmPtr->height = -1;
  880.         goto updateGeom;
  881.     }
  882.     return ParseGeometry(interp, argv[3], winPtr);
  883.     } else if ((c == 'g') && (strncmp(argv[1], "grid", length) == 0)
  884.         && (length >= 3)) {
  885.     int reqWidth, reqHeight, widthInc, heightInc;
  886.  
  887.     if ((argc != 3) && (argc != 7)) {
  888.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  889.             argv[0], " grid window ?baseWidth baseHeight ",
  890.             "widthInc heightInc?\"", (char *) NULL);
  891.         return TCL_ERROR;
  892.     }
  893.     if (argc == 3) {
  894.         if (wmPtr->sizeHintsFlags & PBaseSize) {
  895.         sprintf(interp->result, "%d %d %d %d", wmPtr->reqGridWidth,
  896.             wmPtr->reqGridHeight, wmPtr->widthInc,
  897.             wmPtr->heightInc);
  898.         }
  899.         return TCL_OK;
  900.     }
  901.     if (*argv[3] == '\0') {
  902.         /*
  903.          * Turn off gridding and reset the width and height
  904.          * to make sense as ungridded numbers.
  905.          */
  906.  
  907.         wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc);
  908.         wmPtr->widthInc = 1;
  909.         wmPtr->heightInc = 1;
  910.         if (wmPtr->width != -1) {
  911.         wmPtr->width = winPtr->reqWidth + (wmPtr->width
  912.             - wmPtr->reqGridWidth)*wmPtr->widthInc;
  913.         wmPtr->height = winPtr->reqHeight + (wmPtr->height
  914.             - wmPtr->reqGridHeight)*wmPtr->heightInc;
  915.         }
  916.     } else {
  917.         if ((Tcl_GetInt(interp, argv[3], &reqWidth) != TCL_OK)
  918.             || (Tcl_GetInt(interp, argv[4], &reqHeight) != TCL_OK)
  919.             || (Tcl_GetInt(interp, argv[5], &widthInc) != TCL_OK)
  920.             || (Tcl_GetInt(interp, argv[6], &heightInc) != TCL_OK)) {
  921.         return TCL_ERROR;
  922.         }
  923.         if (reqWidth < 0) {
  924.         interp->result = "baseWidth can't be < 0";
  925.         return TCL_ERROR;
  926.         }
  927.         if (reqHeight < 0) {
  928.         interp->result = "baseHeight can't be < 0";
  929.         return TCL_ERROR;
  930.         }
  931.         if (widthInc < 0) {
  932.         interp->result = "widthInc can't be < 0";
  933.         return TCL_ERROR;
  934.         }
  935.         if (heightInc < 0) {
  936.         interp->result = "heightInc can't be < 0";
  937.         return TCL_ERROR;
  938.         }
  939.         Tk_SetGrid((Tk_Window) winPtr, reqWidth, reqHeight, widthInc,
  940.             heightInc);
  941.     }
  942.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  943.     goto updateGeom;
  944.     } else if ((c == 'g') && (strncmp(argv[1], "group", length) == 0)
  945.         && (length >= 3)) {
  946.     Tk_Window tkwin2;
  947.  
  948.     if ((argc != 3) && (argc != 4)) {
  949.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  950.             argv[0], " group window ?pathName?\"",
  951.             (char *) NULL);
  952.         return TCL_ERROR;
  953.     }
  954.     if (argc == 3) {
  955.         if (wmPtr->hints.flags & WindowGroupHint) {
  956.         interp->result = wmPtr->leaderName;
  957.         }
  958.         return TCL_OK;
  959.     }
  960.     if (*argv[3] == '\0') {
  961.         wmPtr->hints.flags &= ~WindowGroupHint;
  962.         wmPtr->leaderName = NULL;
  963.     } else {
  964.         tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin);
  965.         if (tkwin2 == NULL) {
  966.         return TCL_ERROR;
  967.         }
  968.         Tk_MakeWindowExist(tkwin2);
  969.         wmPtr->hints.window_group = Tk_WindowId(tkwin2);
  970.         wmPtr->hints.flags |= WindowGroupHint;
  971.         wmPtr->leaderName = Tk_PathName(tkwin2);
  972.     }
  973.     UpdateHints(winPtr);
  974.     } else if ((c == 'i') && (strncmp(argv[1], "iconbitmap", length) == 0)
  975.         && (length >= 5)) {
  976.     Pixmap pixmap;
  977.  
  978.     if ((argc != 3) && (argc != 4)) {
  979.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  980.             argv[0], " iconbitmap window ?bitmap?\"",
  981.             (char *) NULL);
  982.         return TCL_ERROR;
  983.     }
  984.     if (argc == 3) {
  985.         if (wmPtr->hints.flags & IconPixmapHint) {
  986.         interp->result = Tk_NameOfBitmap(winPtr->display,
  987.             wmPtr->hints.icon_pixmap);
  988.         }
  989.         return TCL_OK;
  990.     }
  991.     if (*argv[3] == '\0') {
  992.         if (wmPtr->hints.icon_pixmap != None) {
  993.         Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap);
  994.         }
  995.         wmPtr->hints.flags &= ~IconPixmapHint;
  996.     } else {
  997.         pixmap = Tk_GetBitmap(interp, tkwin, Tk_GetUid(argv[3]));
  998.         if (pixmap == None) {
  999.         return TCL_ERROR;
  1000.         }
  1001.         wmPtr->hints.icon_pixmap = pixmap;
  1002.         wmPtr->hints.flags |= IconPixmapHint;
  1003.     }
  1004.     UpdateHints(winPtr);
  1005.     } else if ((c == 'i') && (strncmp(argv[1], "iconify", length) == 0)
  1006.         && (length >= 5)) {
  1007.     if (argc != 3) {
  1008.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1009.             argv[0], " iconify window\"", (char *) NULL);
  1010.         return TCL_ERROR;
  1011.     }
  1012.     if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) {
  1013.         Tcl_AppendResult(interp, "can't iconify \"", winPtr->pathName,
  1014.             "\": override-redirect flag is set");
  1015.         return TCL_ERROR;
  1016.     }
  1017.     wmPtr->hints.initial_state = IconicState;
  1018.     if (wmPtr->flags & WM_NEVER_MAPPED) {
  1019.         return TCL_OK;
  1020.     }
  1021.     if (wmPtr->withdrawn) {
  1022.         UpdateHints(winPtr);
  1023.         Tk_MapWindow((Tk_Window) winPtr);
  1024.         wmPtr->withdrawn = 0;
  1025.     } else {
  1026.         if (XIconifyWindow(winPtr->display, winPtr->window,
  1027.         winPtr->screenNum) == 0) {
  1028.         interp->result =
  1029.             "couldn't send iconify message to window manager";
  1030.         return TCL_ERROR;
  1031.         }
  1032.         WaitForMapNotify(winPtr, 0);
  1033.     }
  1034.     } else if ((c == 'i') && (strncmp(argv[1], "iconmask", length) == 0)
  1035.         && (length >= 5)) {
  1036.     Pixmap pixmap;
  1037.  
  1038.     if ((argc != 3) && (argc != 4)) {
  1039.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1040.             argv[0], " iconmask window ?bitmap?\"",
  1041.             (char *) NULL);
  1042.         return TCL_ERROR;
  1043.     }
  1044.     if (argc == 3) {
  1045.         if (wmPtr->hints.flags & IconMaskHint) {
  1046.         interp->result = Tk_NameOfBitmap(winPtr->display,
  1047.             wmPtr->hints.icon_mask);
  1048.         }
  1049.         return TCL_OK;
  1050.     }
  1051.     if (*argv[3] == '\0') {
  1052.         if (wmPtr->hints.icon_mask != None) {
  1053.         Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_mask);
  1054.         }
  1055.         wmPtr->hints.flags &= ~IconMaskHint;
  1056.     } else {
  1057.         pixmap = Tk_GetBitmap(interp, tkwin, Tk_GetUid(argv[3]));
  1058.         if (pixmap == None) {
  1059.         return TCL_ERROR;
  1060.         }
  1061.         wmPtr->hints.icon_mask = pixmap;
  1062.         wmPtr->hints.flags |= IconMaskHint;
  1063.     }
  1064.     UpdateHints(winPtr);
  1065.     } else if ((c == 'i') && (strncmp(argv[1], "iconname", length) == 0)
  1066.         && (length >= 5)) {
  1067.     if (argc > 4) {
  1068.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1069.             argv[0], " iconname window ?newName?\"", (char *) NULL);
  1070.         return TCL_ERROR;
  1071.     }
  1072.     if (argc == 3) {
  1073.         interp->result = (wmPtr->iconName != NULL) ? wmPtr->iconName : "";
  1074.         return TCL_OK;
  1075.     } else {
  1076.         wmPtr->iconName = Tk_GetUid(argv[3]);
  1077.         if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  1078.         XSetIconName(winPtr->display, winPtr->window, wmPtr->iconName);
  1079.         }
  1080.     }
  1081.     } else if ((c == 'i') && (strncmp(argv[1], "iconposition", length) == 0)
  1082.         && (length >= 5)) {
  1083.     int x, y;
  1084.  
  1085.     if ((argc != 3) && (argc != 5)) {
  1086.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1087.             argv[0], " iconposition window ?x y?\"",
  1088.             (char *) NULL);
  1089.         return TCL_ERROR;
  1090.     }
  1091.     if (argc == 3) {
  1092.         if (wmPtr->hints.flags & IconPositionHint) {
  1093.         sprintf(interp->result, "%d %d", wmPtr->hints.icon_x,
  1094.             wmPtr->hints.icon_y);
  1095.         }
  1096.         return TCL_OK;
  1097.     }
  1098.     if (*argv[3] == '\0') {
  1099.         wmPtr->hints.flags &= ~IconPositionHint;
  1100.     } else {
  1101.         if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK)
  1102.             || (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)){
  1103.         return TCL_ERROR;
  1104.         }
  1105.         wmPtr->hints.icon_x = x;
  1106.         wmPtr->hints.icon_y = y;
  1107.         wmPtr->hints.flags |= IconPositionHint;
  1108.     }
  1109.     UpdateHints(winPtr);
  1110.     } else if ((c == 'i') && (strncmp(argv[1], "iconwindow", length) == 0)
  1111.         && (length >= 5)) {
  1112.     Tk_Window tkwin2;
  1113.  
  1114.     if ((argc != 3) && (argc != 4)) {
  1115.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1116.             argv[0], " iconwindow window ?pathName?\"",
  1117.             (char *) NULL);
  1118.         return TCL_ERROR;
  1119.     }
  1120.     if (argc == 3) {
  1121.         if (wmPtr->hints.flags & IconWindowHint) {
  1122.         interp->result = wmPtr->iconWindowName;
  1123.         }
  1124.         return TCL_OK;
  1125.     }
  1126.     if (*argv[3] == '\0') {
  1127.         wmPtr->hints.flags &= ~IconWindowHint;
  1128.         wmPtr->iconWindowName = NULL;
  1129.     } else {
  1130.         tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin);
  1131.         if (tkwin2 == NULL) {
  1132.         return TCL_ERROR;
  1133.         }
  1134.         Tk_MakeWindowExist(tkwin2);
  1135.         wmPtr->hints.icon_window = Tk_WindowId(tkwin2);
  1136.         wmPtr->hints.flags |= IconWindowHint;
  1137.         wmPtr->iconWindowName = Tk_PathName(tkwin2);
  1138.     }
  1139.     UpdateHints(winPtr);
  1140.     } else if ((c == 'm') && (strncmp(argv[1], "maxsize", length) == 0)
  1141.         && (length >= 2)) {
  1142.     int width, height;
  1143.     if ((argc != 3) && (argc != 5)) {
  1144.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1145.             argv[0], " maxsize window ?width height?\"", (char *) NULL);
  1146.         return TCL_ERROR;
  1147.     }
  1148.     if (argc == 3) {
  1149.         if (wmPtr->sizeHintsFlags & PMaxSize) {
  1150.         sprintf(interp->result, "%d %d", wmPtr->maxWidth,
  1151.             wmPtr->maxHeight);
  1152.         }
  1153.         return TCL_OK;
  1154.     }
  1155.     if (*argv[3] == '\0') {
  1156.         wmPtr->sizeHintsFlags &= ~PMaxSize;
  1157.     } else {
  1158.         if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK)
  1159.             || (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) {
  1160.         return TCL_ERROR;
  1161.         }
  1162.         wmPtr->maxWidth = width;
  1163.         wmPtr->maxHeight = height;
  1164.         wmPtr->sizeHintsFlags |= PMaxSize;
  1165.     }
  1166.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1167.     goto updateGeom;
  1168.     } else if ((c == 'm') && (strncmp(argv[1], "minsize", length) == 0)
  1169.         && (length >= 2)) {
  1170.     int width, height;
  1171.     if ((argc != 3) && (argc != 5)) {
  1172.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1173.             argv[0], " minsize window ?width height?\"", (char *) NULL);
  1174.         return TCL_ERROR;
  1175.     }
  1176.     if (argc == 3) {
  1177.         if (wmPtr->sizeHintsFlags & PMinSize) {
  1178.         sprintf(interp->result, "%d %d", wmPtr->minWidth,
  1179.             wmPtr->minHeight);
  1180.         }
  1181.         return TCL_OK;
  1182.     }
  1183.     if (*argv[3] == '\0') {
  1184.         wmPtr->sizeHintsFlags &= ~PMinSize;
  1185.     } else {
  1186.         if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK)
  1187.             || (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) {
  1188.         return TCL_ERROR;
  1189.         }
  1190.         wmPtr->minWidth = width;
  1191.         wmPtr->minHeight = height;
  1192.         wmPtr->sizeHintsFlags |= PMinSize;
  1193.     }
  1194.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1195.     goto updateGeom;
  1196.     } else if ((c == 'o')
  1197.         && (strncmp(argv[1], "overrideredirect", length) == 0)) {
  1198.     int boolean;
  1199.     XSetWindowAttributes atts;
  1200.  
  1201.     if ((argc != 3) && (argc != 4)) {
  1202.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1203.             argv[0], " overrideredirect window ?boolean?\"",
  1204.             (char *) NULL);
  1205.         return TCL_ERROR;
  1206.     }
  1207.     if (argc == 3) {
  1208.         if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) {
  1209.         interp->result = "1";
  1210.         } else {
  1211.         interp->result = "0";
  1212.         }
  1213.         return TCL_OK;
  1214.     }
  1215.     if (Tcl_GetBoolean(interp, argv[3], &boolean) != TCL_OK) {
  1216.         return TCL_ERROR;
  1217.     }
  1218.     atts.override_redirect = (boolean) ? True : False;
  1219.     Tk_ChangeWindowAttributes((Tk_Window) winPtr, CWOverrideRedirect,
  1220.         &atts);
  1221.     } else if ((c == 'p') && (strncmp(argv[1], "positionfrom", length) == 0)
  1222.         && (length >= 2)) {
  1223.     if ((argc != 3) && (argc != 4)) {
  1224.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1225.             argv[0], " positionfrom window ?user/program?\"",
  1226.             (char *) NULL);
  1227.         return TCL_ERROR;
  1228.     }
  1229.     if (argc == 3) {
  1230.         if (wmPtr->sizeHintsFlags & USPosition) {
  1231.         interp->result = "user";
  1232.         } else if (wmPtr->sizeHintsFlags & PPosition) {
  1233.         interp->result = "program";
  1234.         }
  1235.         return TCL_OK;
  1236.     }
  1237.     if (*argv[3] == '\0') {
  1238.         wmPtr->sizeHintsFlags &= ~(USPosition|PPosition);
  1239.     } else {
  1240.         c = argv[3][0];
  1241.         length = strlen(argv[3]);
  1242.         if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) {
  1243.         wmPtr->sizeHintsFlags &= ~PPosition;
  1244.         wmPtr->sizeHintsFlags |= USPosition;
  1245.         } else if ((c == 'p') && (strncmp(argv[3], "program", length) == 0)) {
  1246.         wmPtr->sizeHintsFlags &= ~USPosition;
  1247.         wmPtr->sizeHintsFlags |= PPosition;
  1248.         } else {
  1249.         Tcl_AppendResult(interp, "bad argument \"", argv[3],
  1250.             "\": must be program or user", (char *) NULL);
  1251.         return TCL_ERROR;
  1252.         }
  1253.     }
  1254.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1255.     goto updateGeom;
  1256.     } else if ((c == 'p') && (strncmp(argv[1], "protocol", length) == 0)
  1257.         && (length >= 2)) {
  1258.     register ProtocolHandler *protPtr, *prevPtr;
  1259.     Atom protocol;
  1260.     int cmdLength;
  1261.  
  1262.     if ((argc < 3) || (argc > 5)) {
  1263.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1264.             argv[0], " protocol window ?name? ?command?\"",
  1265.             (char *) NULL);
  1266.         return TCL_ERROR;
  1267.     }
  1268.     if (argc == 3) {
  1269.         /*
  1270.          * Return a list of all defined protocols for the window.
  1271.          */
  1272.         for (protPtr = wmPtr->protPtr; protPtr != NULL;
  1273.             protPtr = protPtr->nextPtr) {
  1274.         Tcl_AppendElement(interp,
  1275.             Tk_GetAtomName((Tk_Window) winPtr, protPtr->protocol));
  1276.         }
  1277.         return TCL_OK;
  1278.     }
  1279.     protocol = Tk_InternAtom((Tk_Window) winPtr, argv[3]);
  1280.     if (argc == 4) {
  1281.         /*
  1282.          * Return the command to handle a given protocol.
  1283.          */
  1284.         for (protPtr = wmPtr->protPtr; protPtr != NULL;
  1285.             protPtr = protPtr->nextPtr) {
  1286.         if (protPtr->protocol == protocol) {
  1287.             interp->result = protPtr->command;
  1288.             return TCL_OK;
  1289.         }
  1290.         }
  1291.         return TCL_OK;
  1292.     }
  1293.  
  1294.     /*
  1295.      * Delete any current protocol handler, then create a new
  1296.      * one with the specified command, unless the command is
  1297.      * empty.
  1298.      */
  1299.  
  1300.     for (protPtr = wmPtr->protPtr, prevPtr = NULL; protPtr != NULL;
  1301.         prevPtr = protPtr, protPtr = protPtr->nextPtr) {
  1302.         if (protPtr->protocol == protocol) {
  1303.         if (prevPtr == NULL) {
  1304.             wmPtr->protPtr = protPtr->nextPtr;
  1305.         } else {
  1306.             prevPtr->nextPtr = protPtr->nextPtr;
  1307.         }
  1308.         Tk_EventuallyFree((ClientData) protPtr, (Tk_FreeProc *) free);
  1309.         break;
  1310.         }
  1311.     }
  1312.     cmdLength = strlen(argv[4]);
  1313.     if (cmdLength > 0) {
  1314.         protPtr = (ProtocolHandler *) ckalloc(HANDLER_SIZE(cmdLength));
  1315.         protPtr->protocol = protocol;
  1316.         protPtr->nextPtr = wmPtr->protPtr;
  1317.         wmPtr->protPtr = protPtr;
  1318.         protPtr->interp = interp;
  1319.         strcpy(protPtr->command, argv[4]);
  1320.     }
  1321.     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  1322.         UpdateWmProtocols(wmPtr);
  1323.     }
  1324.     } else if ((c == 's') && (strncmp(argv[1], "sizefrom", length) == 0)
  1325.         && (length >= 2)) {
  1326.     if ((argc != 3) && (argc != 4)) {
  1327.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1328.             argv[0], " sizefrom window ?user|program?\"",
  1329.             (char *) NULL);
  1330.         return TCL_ERROR;
  1331.     }
  1332.     if (argc == 3) {
  1333.         if (wmPtr->sizeHintsFlags & USSize) {
  1334.         interp->result = "user";
  1335.         } else if (wmPtr->sizeHintsFlags & PSize) {
  1336.         interp->result = "program";
  1337.         }
  1338.         return TCL_OK;
  1339.     }
  1340.     if (*argv[3] == '\0') {
  1341.         wmPtr->sizeHintsFlags &= ~(USSize|PSize);
  1342.     } else {
  1343.         c = argv[3][0];
  1344.         length = strlen(argv[3]);
  1345.         if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) {
  1346.         wmPtr->sizeHintsFlags &= ~PSize;
  1347.         wmPtr->sizeHintsFlags |= USSize;
  1348.         } else if ((c == 'p')
  1349.             && (strncmp(argv[3], "program", length) == 0)) {
  1350.         wmPtr->sizeHintsFlags &= ~USSize;
  1351.         wmPtr->sizeHintsFlags |= PSize;
  1352.         } else {
  1353.         Tcl_AppendResult(interp, "bad argument \"", argv[3],
  1354.             "\": must be program or user", (char *) NULL);
  1355.         return TCL_ERROR;
  1356.         }
  1357.     }
  1358.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1359.     goto updateGeom;
  1360.     } else if ((c == 's') && (strncmp(argv[1], "state", length) == 0)
  1361.         && (length >= 2)) {
  1362.     if (argc != 3) {
  1363.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1364.             argv[0], " state window\"", (char *) NULL);
  1365.         return TCL_ERROR;
  1366.     }
  1367.     if (wmPtr->withdrawn) {
  1368.         interp->result = "withdrawn";
  1369.     } else if (Tk_IsMapped((Tk_Window) winPtr)) {
  1370.         interp->result = "normal";
  1371.     } else {
  1372.         interp->result = "iconic";
  1373.     }
  1374.     } else if ((c == 't') && (strncmp(argv[1], "title", length) == 0)
  1375.         && (length >= 2)) {
  1376.     if (argc > 4) {
  1377.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1378.             argv[0], " title window ?newTitle?\"", (char *) NULL);
  1379.         return TCL_ERROR;
  1380.     }
  1381.     if (argc == 3) {
  1382.         interp->result = (wmPtr->titleUid != NULL) ? wmPtr->titleUid
  1383.             : winPtr->nameUid;
  1384.         return TCL_OK;
  1385.     } else {
  1386.         wmPtr->titleUid = Tk_GetUid(argv[3]);
  1387.         if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  1388.         XTextProperty textProp;
  1389.  
  1390.         if (XStringListToTextProperty(&wmPtr->titleUid, 1,
  1391.             &textProp)  != 0) {
  1392.             XSetWMName(winPtr->display, winPtr->window, &textProp);
  1393.             XFree((char *) textProp.value);
  1394.         }
  1395.         }
  1396.     }
  1397.     } else if ((c == 't') && (strncmp(argv[1], "transient", length) == 0)
  1398.         && (length >= 3)) {
  1399.     Tk_Window master;
  1400.  
  1401.     if ((argc != 3) && (argc != 4)) {
  1402.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1403.             argv[0], " transient window ?master?\"", (char *) NULL);
  1404.         return TCL_ERROR;
  1405.     }
  1406.     if (argc == 3) {
  1407.         if (wmPtr->master != None) {
  1408.         interp->result = wmPtr->masterWindowName;
  1409.         }
  1410.         return TCL_OK;
  1411.     }
  1412.     if (argv[3][0] == '\0') {
  1413.         wmPtr->master = None;
  1414.         wmPtr->masterWindowName = NULL;
  1415.     } else {
  1416.         master = Tk_NameToWindow(interp, argv[3], tkwin);
  1417.         if (master == NULL) {
  1418.         return TCL_ERROR;
  1419.         }
  1420.         Tk_MakeWindowExist(master);
  1421.         wmPtr->master = Tk_WindowId(master);
  1422.         wmPtr->masterWindowName = Tk_PathName(master);
  1423.     }
  1424.     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  1425.         XSetTransientForHint(winPtr->display, winPtr->window,
  1426.             wmPtr->master);
  1427.     }
  1428.     } else if ((c == 'w') && (strncmp(argv[1], "withdraw", length) == 0)) {
  1429.     if (argc != 3) {
  1430.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1431.             argv[0], " withdraw window\"", (char *) NULL);
  1432.         return TCL_ERROR;
  1433.     }
  1434.     wmPtr->hints.initial_state = WithdrawnState;
  1435.     wmPtr->withdrawn = 1;
  1436.     if (wmPtr->flags & WM_NEVER_MAPPED) {
  1437.         return TCL_OK;
  1438.     }
  1439.     if (XWithdrawWindow(winPtr->display, winPtr->window,
  1440.         winPtr->screenNum) == 0) {
  1441.         interp->result =
  1442.             "couldn't send withdraw message to window manager";
  1443.         return TCL_ERROR;
  1444.     }
  1445.     WaitForMapNotify(winPtr, 0);
  1446.     } else {
  1447.     Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],
  1448.         "\": must be aspect, client, command, deiconify, ",
  1449.         "focusmodel, frame, geometry, grid, group, iconbitmap, ",
  1450.         "iconify, iconmask, iconname, iconposition, ",
  1451.         "iconwindow, maxsize, minsize, overrideredirect, ",
  1452.         "positionfrom, protocol, sizefrom, state, title, ",
  1453.         "trace, transient, or withdraw",
  1454.         (char *) NULL);
  1455.     return TCL_ERROR;
  1456.     }
  1457.     return TCL_OK;
  1458.  
  1459.     updateGeom:
  1460.     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
  1461.     Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
  1462.     wmPtr->flags |= WM_UPDATE_PENDING;
  1463.     }
  1464.     return TCL_OK;
  1465. }
  1466.  
  1467. /*
  1468.  *----------------------------------------------------------------------
  1469.  *
  1470.  * Tk_SetGrid --
  1471.  *
  1472.  *    This procedure is invoked by a widget when it wishes to set a grid
  1473.  *    coordinate system that controls the size of a top-level window.
  1474.  *    It provides a C interface equivalent to the "wm grid" command and
  1475.  *    is usually asscoiated with the -setgrid option.
  1476.  *
  1477.  * Results:
  1478.  *    None.
  1479.  *
  1480.  * Side effects:
  1481.  *    Grid-related information will be passed to the window manager, so
  1482.  *    that the top-level window associated with tkwin will resize on
  1483.  *    even grid units.
  1484.  *
  1485.  *----------------------------------------------------------------------
  1486.  */
  1487.  
  1488. void
  1489. Tk_SetGrid(tkwin, reqWidth, reqHeight, widthInc, heightInc)
  1490.     Tk_Window tkwin;        /* Token for window.  New window mgr info
  1491.                  * will be posted for the top-level window
  1492.                  * associated with this window. */
  1493.     int reqWidth;        /* Width (in grid units) corresponding to
  1494.                  * the requested geometry for tkwin. */
  1495.     int reqHeight;        /* Height (in grid units) corresponding to
  1496.                  * the requested geometry for tkwin. */
  1497.     int widthInc, heightInc;    /* Pixel increments corresponding to a
  1498.                  * change of one grid unit. */
  1499. {
  1500.     TkWindow *winPtr = (TkWindow *) tkwin;
  1501.     register WmInfo *wmPtr;
  1502.  
  1503.     /*
  1504.      * Find the top-level window for tkwin, plus the window manager
  1505.      * information.
  1506.      */
  1507.  
  1508.     while (!(winPtr->flags & TK_TOP_LEVEL)) {
  1509.     winPtr = winPtr->parentPtr;
  1510.     }
  1511.     wmPtr = winPtr->wmInfoPtr;
  1512.  
  1513.     if ((wmPtr->reqGridWidth == reqWidth)
  1514.         && (wmPtr->reqGridHeight == reqHeight)
  1515.         && (wmPtr->widthInc == widthInc)
  1516.         && (wmPtr->heightInc == heightInc)
  1517.         && ((wmPtr->sizeHintsFlags & (PBaseSize|PResizeInc))
  1518.             == PBaseSize|PResizeInc)) {
  1519.     return;
  1520.     }
  1521.  
  1522.     /*
  1523.      * If gridding was previously off, then forget about any window
  1524.      * size requests made by the user or via "wm geometry":  these are
  1525.      * in pixel units and there's no easy way to translate them to
  1526.      * grid units since the new requested size of the top-level window in
  1527.      * pixels may not yet have been registered yet (it may filter up
  1528.      * the hierarchy in DoWhenIdle handlers).
  1529.      */
  1530.  
  1531.     if (!(wmPtr->sizeHintsFlags & PBaseSize)) {
  1532.     wmPtr->width = -1;
  1533.     wmPtr->height = -1;
  1534.     }
  1535.  
  1536.     /* 
  1537.      * Set the new gridding information, and start the process of passing
  1538.      * all of this information to the window manager.
  1539.      */
  1540.  
  1541.     wmPtr->reqGridWidth = reqWidth;
  1542.     wmPtr->reqGridHeight = reqHeight;
  1543.     wmPtr->widthInc = widthInc;
  1544.     wmPtr->heightInc = heightInc;
  1545.     wmPtr->sizeHintsFlags |= PBaseSize|PResizeInc;
  1546.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1547.     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
  1548.     Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
  1549.     wmPtr->flags |= WM_UPDATE_PENDING;
  1550.     }
  1551. }
  1552.  
  1553. /*
  1554.  *----------------------------------------------------------------------
  1555.  *
  1556.  * ConfigureEvent --
  1557.  *
  1558.  *    This procedure is called to handle ConfigureNotify events on
  1559.  *    top-level windows.
  1560.  *
  1561.  * Results:
  1562.  *    None.
  1563.  *
  1564.  * Side effects:
  1565.  *    Information gets updated in the WmInfo structure for the window.
  1566.  *
  1567.  *----------------------------------------------------------------------
  1568.  */
  1569.  
  1570. static void
  1571. ConfigureEvent(winPtr, configEventPtr)
  1572.     TkWindow *winPtr;            /* Top-level window. */
  1573.     XConfigureEvent *configEventPtr;    /* Event that just occurred for
  1574.                      * winPtr. */
  1575. {
  1576.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  1577.  
  1578.     /* 
  1579.      * Update size information from the event.  There are a couple of
  1580.      * tricky points here:
  1581.      *
  1582.      * 1. If the user changed the size externally then set wmPtr->width
  1583.      *    and wmPtr->height just as if a "wm geometry" command had been
  1584.      *    invoked with the same information.
  1585.      * 2. However, if the size is changing in response to a request
  1586.      *    coming from us (WM_SYNC_PENDING is set), then don't set wmPtr->width
  1587.      *    or wmPtr->height if they were previously -1 (otherwise the
  1588.      *    window will stop tracking geometry manager requests).
  1589.      */
  1590.  
  1591.     if (((winPtr->changes.width != configEventPtr->width)
  1592.         || (winPtr->changes.height != configEventPtr->height))
  1593.         && !(wmPtr->flags & WM_SYNC_PENDING)){
  1594.     if (wmTracing) {
  1595.         printf("TopLevelEventProc: user changed %s size to %dx%d\n",
  1596.             winPtr->pathName, configEventPtr->width,
  1597.             configEventPtr->height);
  1598.     }
  1599.     if (IS_GRIDDED(wmPtr)) {
  1600.         wmPtr->width = wmPtr->reqGridWidth
  1601.             + (configEventPtr->width
  1602.             - winPtr->reqWidth)/wmPtr->widthInc;
  1603.         if (wmPtr->width < 0) {
  1604.         wmPtr->width = 0;
  1605.         }
  1606.         wmPtr->height = wmPtr->reqGridHeight
  1607.             + (configEventPtr->height
  1608.             - winPtr->reqHeight)/wmPtr->heightInc;
  1609.         if (wmPtr->height < 0) {
  1610.         wmPtr->height = 0;
  1611.         }
  1612.     } else {
  1613.         wmPtr->width = configEventPtr->width;
  1614.         wmPtr->height = configEventPtr->height;
  1615.     }
  1616.     wmPtr->configWidth = configEventPtr->width;
  1617.     wmPtr->configHeight = configEventPtr->height;
  1618.     }
  1619.  
  1620.     if (wmTracing) {
  1621.     printf("ConfigureEvent: %s x = %d y = %d, width = %d, height = %d",
  1622.         winPtr->pathName, configEventPtr->x, configEventPtr->y,
  1623.         configEventPtr->width, configEventPtr->height);
  1624.     printf(" send_event = %d, serial = %d\n", configEventPtr->send_event,
  1625.         configEventPtr->serial);
  1626.     }
  1627.     winPtr->changes.width = configEventPtr->width;
  1628.     winPtr->changes.height = configEventPtr->height;
  1629.     winPtr->changes.border_width = configEventPtr->border_width;
  1630.     winPtr->changes.sibling = configEventPtr->above;
  1631.     winPtr->changes.stack_mode = Above;
  1632.  
  1633.     /*
  1634.      * Reparenting window managers make life difficult.  If the
  1635.      * window manager reparents a top-level window then the x and y
  1636.      * information that comes in events for the window is wrong:
  1637.      * it gives the location of the window inside its decorative
  1638.      * parent, rather than the location of the window in root
  1639.      * coordinates, which is what we want.  Window managers
  1640.      * are supposed to send synthetic events with the correct
  1641.      * information, but ICCCM doesn't require them to do this
  1642.      * under all conditions, and the information provided doesn't
  1643.      * include everything we need here.  So, the code below
  1644.      * maintains a bunch of information about the parent window.
  1645.      * If the window hasn't been reparented, we pretend that
  1646.      * there is a parent shrink-wrapped around the window.
  1647.      */
  1648.  
  1649.     if ((wmPtr->reparent == None) || !ComputeReparentGeometry(winPtr)) {
  1650.     wmPtr->parentWidth = configEventPtr->width
  1651.         + 2*configEventPtr->border_width;
  1652.     wmPtr->parentHeight = configEventPtr->height
  1653.         + 2*configEventPtr->border_width;
  1654.     winPtr->changes.x = wmPtr->x = configEventPtr->x;
  1655.     winPtr->changes.y = wmPtr->y = configEventPtr->y;
  1656.     if (wmPtr->flags & WM_NEGATIVE_X) {
  1657.         wmPtr->x = wmPtr->vRootWidth - (wmPtr->x + wmPtr->parentWidth);
  1658.     }
  1659.     if (wmPtr->flags & WM_NEGATIVE_Y) {
  1660.         wmPtr->y = wmPtr->vRootHeight - (wmPtr->y + wmPtr->parentHeight);
  1661.     }
  1662.     }
  1663. }
  1664.  
  1665. /*
  1666.  *----------------------------------------------------------------------
  1667.  *
  1668.  * ReparentEvent --
  1669.  *
  1670.  *    This procedure is called to handle ReparentNotify events on
  1671.  *    top-level windows.
  1672.  *
  1673.  * Results:
  1674.  *    None.
  1675.  *
  1676.  * Side effects:
  1677.  *    Information gets updated in the WmInfo structure for the window.
  1678.  *
  1679.  *----------------------------------------------------------------------
  1680.  */
  1681.  
  1682. static void
  1683. ReparentEvent(winPtr, reparentEventPtr)
  1684.     TkWindow *winPtr;            /* Top-level window. */
  1685.     XReparentEvent *reparentEventPtr;    /* Event that just occurred for
  1686.                      * winPtr. */
  1687. {
  1688.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  1689.     Window vRoot, ancestor, *children, dummy2, *virtualRootPtr;
  1690.     Atom actualType;
  1691.     int actualFormat;
  1692.     unsigned long numItems, bytesAfter;
  1693.     unsigned int dummy;
  1694.     Tk_ErrorHandler handler;
  1695.  
  1696.     /*
  1697.      * Identify the root window for winPtr.  This is tricky because of
  1698.      * virtual root window managers like tvtwm.  If the window has a
  1699.      * property named __SWM_ROOT or __WM_ROOT then this property gives
  1700.      * the id for a virtual root window that should be used instead of
  1701.      * the root window of the screen.
  1702.      */
  1703.  
  1704.     vRoot = RootWindow(winPtr->display, winPtr->screenNum);
  1705.     wmPtr->vRoot = None;
  1706.     if (((XGetWindowProperty(winPtr->display, winPtr->window,
  1707.         Tk_InternAtom((Tk_Window) winPtr, "__WM_ROOT"), 0, (long) 1,
  1708.         False, XA_WINDOW, &actualType, &actualFormat, &numItems,
  1709.         &bytesAfter, (unsigned char **) &virtualRootPtr) == Success)
  1710.         && (actualType == XA_WINDOW))
  1711.         || ((XGetWindowProperty(winPtr->display, winPtr->window,
  1712.         Tk_InternAtom((Tk_Window) winPtr, "__SWM_ROOT"), 0, (long) 1,
  1713.         False, XA_WINDOW, &actualType, &actualFormat, &numItems,
  1714.         &bytesAfter, (unsigned char **) &virtualRootPtr) == Success)
  1715.         && (actualType == XA_WINDOW))) {
  1716.     if ((actualFormat == 32) && (numItems == 1)) {
  1717.         vRoot = wmPtr->vRoot = *virtualRootPtr;
  1718.     } else if (wmTracing) {
  1719.         printf("%s format %d numItems %d\n",
  1720.             "ReparentEvent got bogus VROOT property:", actualFormat,
  1721.             numItems);
  1722.     }
  1723.     XFree((char *) virtualRootPtr);
  1724.     }
  1725.  
  1726.     if (wmTracing) {
  1727.     printf("ReparentEvent: %s reparented to 0x%x, vRoot = 0x%x\n",
  1728.         winPtr->pathName, reparentEventPtr->parent, vRoot);
  1729.     }
  1730.  
  1731.     /*
  1732.      * Fetch correct geometry information for the new virtual root.
  1733.      */
  1734.  
  1735.     UpdateVRootGeometry(wmPtr);
  1736.  
  1737.     /*
  1738.      * If the window's new parent is the root window, then mark it as
  1739.      * no longer reparented.
  1740.      */
  1741.  
  1742.     if (reparentEventPtr->parent == vRoot) {
  1743.     noReparent:
  1744.     wmPtr->reparent = None;
  1745.     wmPtr->parentWidth = winPtr->changes.width
  1746.         + 2*winPtr->changes.border_width;
  1747.     wmPtr->parentHeight = winPtr->changes.height
  1748.         + 2*winPtr->changes.border_width;
  1749.     wmPtr->xInParent = wmPtr->yInParent = 0;
  1750.     winPtr->changes.x = reparentEventPtr->x;
  1751.     winPtr->changes.y = reparentEventPtr->y;
  1752.     return;
  1753.     }
  1754.  
  1755.     /*
  1756.      * Search up the window hierarchy to find the ancestor of this
  1757.      * window that is just below the (virtual) root.  This is tricky
  1758.      * because it's possible that things have changed since the event
  1759.      * was generated so that the ancestry indicated by the event no
  1760.      * longer exists.  If this happens then an error will occur and
  1761.      * we just discard the event (there will be a more up-to-date
  1762.      * ReparentNotify event coming later).
  1763.      */
  1764.  
  1765.     handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
  1766.         (Tk_ErrorProc *) NULL, (ClientData) NULL);
  1767.     wmPtr->reparent = reparentEventPtr->parent;
  1768.     while (1) {
  1769.     if (XQueryTree(winPtr->display, wmPtr->reparent, &dummy2, &ancestor,
  1770.         &children, &dummy) == 0) {
  1771.         Tk_DeleteErrorHandler(handler);
  1772.         goto noReparent;
  1773.     }
  1774.     XFree((char *) children);
  1775.     if ((ancestor == vRoot) ||
  1776.         (ancestor == RootWindow(winPtr->display, winPtr->screenNum))) {
  1777.         break;
  1778.     }
  1779.     wmPtr->reparent = ancestor;
  1780.     }
  1781.     Tk_DeleteErrorHandler(handler);
  1782.  
  1783.     if (!ComputeReparentGeometry(winPtr)) {
  1784.     goto noReparent;
  1785.     }
  1786. }
  1787.  
  1788. /*
  1789.  *----------------------------------------------------------------------
  1790.  *
  1791.  * ComputeReparentGeometry --
  1792.  *
  1793.  *    This procedure is invoked to recompute geometry information
  1794.  *    related to a reparented top-level window, such as the position
  1795.  *    and total size of the parent and the position within it of
  1796.  *    the top-level window.
  1797.  *
  1798.  * Results:
  1799.  *    The return value is 1 if everything completed successfully
  1800.  *    and 0 if an error occurred while querying information about
  1801.  *    winPtr's parents.  In this case winPtr is marked as no longer
  1802.  *    being reparented.
  1803.  *
  1804.  * Side effects:
  1805.  *    Geometry information in winPtr and winPtr->wmPtr gets updated.
  1806.  *
  1807.  *----------------------------------------------------------------------
  1808.  */
  1809.  
  1810. static int
  1811. ComputeReparentGeometry(winPtr)
  1812.     TkWindow *winPtr;        /* Top-level window whose reparent info
  1813.                  * is to be recomputed. */
  1814. {
  1815.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  1816.     unsigned int width, height, bd, dummy;
  1817.     int xOffset, yOffset, x, y;
  1818.     Window dummy2;
  1819.     Status status;
  1820.     Tk_ErrorHandler handler;
  1821.  
  1822.     handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
  1823.         (Tk_ErrorProc *) NULL, (ClientData) NULL);
  1824.     (void) XTranslateCoordinates(winPtr->display, winPtr->window,
  1825.         wmPtr->reparent, 0, 0, &xOffset, &yOffset, &dummy2);
  1826.     status = XGetGeometry(winPtr->display, wmPtr->reparent,
  1827.         &dummy2, &x, &y, &width, &height, &bd, &dummy);
  1828.     Tk_DeleteErrorHandler(handler);
  1829.     if (status == 0) {
  1830.     /*
  1831.      * It appears that the reparented parent went away and
  1832.      * no-one told us.  Reset the window to indicate that
  1833.      * it's not reparented.
  1834.      */
  1835.     wmPtr->reparent = None;
  1836.     wmPtr->xInParent = wmPtr->yInParent = 0;
  1837.     return 0;
  1838.     }
  1839.     wmPtr->xInParent = xOffset + bd - winPtr->changes.border_width;
  1840.     wmPtr->yInParent = yOffset + bd - winPtr->changes.border_width;
  1841.     wmPtr->parentWidth = width + 2*bd;
  1842.     wmPtr->parentHeight = height + 2*bd;
  1843.  
  1844.     /*
  1845.      * Some tricky issues in updating wmPtr->x and wmPtr->y:
  1846.      *
  1847.      * 1. Don't update them if the event occurred because of something
  1848.      * we did (i.e. WM_SYNC_PENDING and WM_MOVE_PENDING are both set).
  1849.      * This is because window managers treat coords differently than Tk,
  1850.      * and no two window managers are alike. If the window manager moved
  1851.      * the window because we told it to, remember the coordinates we told
  1852.      * it, not the ones it actually moved it to.  This allows us to move
  1853.      * the window back to the same coordinates later and get the same
  1854.      * result. Without this check, windows can "walk" across the screen
  1855.      * under some conditions.
  1856.      *
  1857.      * 2. Don't update wmPtr->x and wmPtr->y unless winPtr->changes.x
  1858.      * or winPtr->changes.y has changed (otherwise a size change can
  1859.      * spoof us into thinking that the position changed too and defeat
  1860.      * the intent of (1) above.
  1861.      *
  1862.      * 3. Ignore size changes coming from the window system if we're
  1863.      * about to change the size ourselves but haven't seen the event for
  1864.      * it yet:  our size change is supposed to take priority.
  1865.      */
  1866.  
  1867.     if (!(wmPtr->flags & WM_MOVE_PENDING)
  1868.         && ((winPtr->changes.x != (x + wmPtr->xInParent))
  1869.         || (winPtr->changes.y != (y + wmPtr->yInParent)))) {
  1870.     wmPtr->x = x;
  1871.     if (wmPtr->flags & WM_NEGATIVE_X) {
  1872.         wmPtr->x = wmPtr->vRootWidth - (wmPtr->x + wmPtr->parentWidth);
  1873.     }
  1874.     wmPtr->y = y;
  1875.     if (wmPtr->flags & WM_NEGATIVE_Y) {
  1876.         wmPtr->y = wmPtr->vRootHeight - (wmPtr->y + wmPtr->parentHeight);
  1877.     }
  1878.     }
  1879.  
  1880.     winPtr->changes.x = x + wmPtr->xInParent;
  1881.     winPtr->changes.y = y + wmPtr->yInParent;
  1882.     if (wmTracing) {
  1883.     printf("winPtr coords %d,%d, wmPtr coords %d,%d, offsets %d %d\n",
  1884.         winPtr->changes.x, winPtr->changes.y, wmPtr->x, wmPtr->y,
  1885.         wmPtr->xInParent, wmPtr->yInParent);
  1886.     }
  1887.     return 1;
  1888. }
  1889.  
  1890. /*
  1891.  *----------------------------------------------------------------------
  1892.  *
  1893.  * TopLevelEventProc --
  1894.  *
  1895.  *    This procedure is invoked when a top-level (or other externally-
  1896.  *    managed window) is restructured in any way.
  1897.  *
  1898.  * Results:
  1899.  *    None.
  1900.  *
  1901.  * Side effects:
  1902.  *    Tk's internal data structures for the window get modified to
  1903.  *    reflect the structural change.
  1904.  *
  1905.  *----------------------------------------------------------------------
  1906.  */
  1907.  
  1908. static void
  1909. TopLevelEventProc(clientData, eventPtr)
  1910.     ClientData clientData;        /* Window for which event occurred. */
  1911.     XEvent *eventPtr;            /* Event that just happened. */
  1912. {
  1913.     register TkWindow *winPtr = (TkWindow *) clientData;
  1914.  
  1915.     winPtr->wmInfoPtr->flags |= WM_VROOT_OFFSET_STALE;
  1916.     if (eventPtr->type == DestroyNotify) {
  1917.     if (!(winPtr->flags & TK_ALREADY_DEAD)) {
  1918.         Tk_DestroyWindow((Tk_Window) winPtr);
  1919.     }
  1920.     if (wmTracing) {
  1921.         printf("TopLevelEventProc: %s deleted\n", winPtr->pathName);
  1922.     }
  1923.     } else if (eventPtr->type == ConfigureNotify) {
  1924.     ConfigureEvent(winPtr, &eventPtr->xconfigure);
  1925.     } else if (eventPtr->type == MapNotify) {
  1926.     winPtr->flags |= TK_MAPPED;
  1927.     if (wmTracing) {
  1928.         printf("TopLevelEventProc: %s mapped\n", winPtr->pathName);
  1929.     }
  1930.     } else if (eventPtr->type == UnmapNotify) {
  1931.     winPtr->flags &= ~TK_MAPPED;
  1932.     if (wmTracing) {
  1933.         printf("TopLevelEventProc: %s unmapped\n", winPtr->pathName);
  1934.     }
  1935.     } else if (eventPtr->type == ReparentNotify) {
  1936.     ReparentEvent(winPtr, &eventPtr->xreparent);
  1937.     }
  1938. }
  1939.  
  1940. /*
  1941.  *----------------------------------------------------------------------
  1942.  *
  1943.  * TopLevelReqProc --
  1944.  *
  1945.  *    This procedure is invoked by the geometry manager whenever
  1946.  *    the requested size for a top-level window is changed.
  1947.  *
  1948.  * Results:
  1949.  *    None.
  1950.  *
  1951.  * Side effects:
  1952.  *    Arrange for the window to be resized to satisfy the request
  1953.  *    (this happens as a when-idle action).
  1954.  *
  1955.  *----------------------------------------------------------------------
  1956.  */
  1957.  
  1958.     /* ARGSUSED */
  1959. static void
  1960. TopLevelReqProc(dummy, tkwin)
  1961.     ClientData dummy;            /* Not used. */
  1962.     Tk_Window tkwin;            /* Information about window. */
  1963. {
  1964.     TkWindow *winPtr = (TkWindow *) tkwin;
  1965.     WmInfo *wmPtr;
  1966.  
  1967.     wmPtr = winPtr->wmInfoPtr;
  1968.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1969.     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
  1970.     Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
  1971.     wmPtr->flags |= WM_UPDATE_PENDING;
  1972.     }
  1973. }
  1974.  
  1975. /*
  1976.  *----------------------------------------------------------------------
  1977.  *
  1978.  * UpdateGeometryInfo --
  1979.  *
  1980.  *    This procedure is invoked when a top-level window is first
  1981.  *    mapped, and also as a when-idle procedure, to bring the
  1982.  *    geometry and/or position of a top-level window back into
  1983.  *    line with what has been requested by the user and/or widgets.
  1984.  *    This procedure doesn't return until the window manager has
  1985.  *    responded to the geometry change.
  1986.  *
  1987.  * Results:
  1988.  *    None.
  1989.  *
  1990.  * Side effects:
  1991.  *    The window's size and location may change, unless the WM prevents
  1992.  *    that from happening.
  1993.  *
  1994.  *----------------------------------------------------------------------
  1995.  */
  1996.  
  1997. static void
  1998. UpdateGeometryInfo(clientData)
  1999.     ClientData clientData;        /* Pointer to the window's record. */
  2000. {
  2001.     register TkWindow *winPtr = (TkWindow *) clientData;
  2002.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  2003.     int x, y, width, height;
  2004.     unsigned long serial;
  2005.  
  2006.     wmPtr->flags &= ~WM_UPDATE_PENDING;
  2007.  
  2008.     /*
  2009.      * Compute the new size for the top-level window.  See the
  2010.      * user documentation for details on this, but the size
  2011.      * requested depends on (a) the size requested internally
  2012.      * by the window's widgets, (b) the size requested by the
  2013.      * user in a "wm geometry" command or via wm-based interactive
  2014.      * resizing (if any), and (c) whether or not the window is
  2015.      * gridded.  Don't permit sizes <= 0 because this upsets
  2016.      * the X server.
  2017.      */
  2018.  
  2019.     if (wmPtr->width == -1) {
  2020.     width = winPtr->reqWidth;
  2021.     height = winPtr->reqHeight;
  2022.     } else if (IS_GRIDDED(wmPtr)) {
  2023.     width = winPtr->reqWidth
  2024.         + (wmPtr->width - wmPtr->reqGridWidth)*wmPtr->widthInc;
  2025.     height = winPtr->reqHeight
  2026.         + (wmPtr->height - wmPtr->reqGridHeight)*wmPtr->heightInc;
  2027.     } else {
  2028.     width = wmPtr->width;
  2029.     height = wmPtr->height;
  2030.     }
  2031.     if (width <= 0) {
  2032.     width = 1;
  2033.     }
  2034.     if (height <= 0) {
  2035.     height = 1;
  2036.     }
  2037.  
  2038.     /*
  2039.      * Compute the new position for the upper-left pixel of the window's
  2040.      * decorative frame.  This is tricky, because we need to include the
  2041.      * border widths supplied by a reparented parent in this calculation,
  2042.      * but can't use the parent's current overall size since that may
  2043.      * change as a result of this code.
  2044.      */
  2045.  
  2046.     if (wmPtr->flags & WM_NEGATIVE_X) {
  2047.     x = wmPtr->vRootWidth - wmPtr->x
  2048.         - (width + (wmPtr->parentWidth - winPtr->changes.width));
  2049.     } else {
  2050.     x =  wmPtr->x;
  2051.     }
  2052.     if (wmPtr->flags & WM_NEGATIVE_Y) {
  2053.     y = wmPtr->vRootHeight - wmPtr->y
  2054.         - (height + (wmPtr->parentHeight - winPtr->changes.height));
  2055.     } else {
  2056.     y =  wmPtr->y;
  2057.     }
  2058.  
  2059.     /*
  2060.      * If the window's size is going to change and the window is
  2061.      * supposed to not be resizable by the user, then we have to
  2062.      * update the size hints.  There may also be a size-hint-update
  2063.      * request pending from somewhere else, too.
  2064.      */
  2065.  
  2066.     if (((width != winPtr->changes.width)
  2067.         || (height != winPtr->changes.height))
  2068.         && !IS_GRIDDED(wmPtr)
  2069.         && ((wmPtr->sizeHintsFlags & (PMinSize|PMaxSize)) == 0)) {
  2070.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  2071.     }
  2072.     if (wmPtr->flags & WM_UPDATE_SIZE_HINTS) {
  2073.     UpdateSizeHints(winPtr);
  2074.     }
  2075.  
  2076.     /*
  2077.      * Reconfigure the window if it isn't already configured correctly.
  2078.      * A few tricky points:
  2079.      *
  2080.      * 1. Sometimes the window manager will give us a different size
  2081.      *    than we asked for (e.g. mwm has a minimum size for windows), so
  2082.      *    base the size check on what we *asked for* last time, not what we
  2083.      *    got.
  2084.      * 2. Can't just reconfigure always, because we may not get a
  2085.      *    ConfigureNotify event back if nothing changed, so
  2086.      *    WaitForConfigureNotify will hang a long time.
  2087.      * 3. Don't move window unless a new position has been requested for
  2088.      *      it.  This is because of "features" in some window managers (e.g.
  2089.      *    twm, as of 4/24/91) where they don't interpret coordinates
  2090.      *    according to ICCCM.  Moving a window to its current location may
  2091.      *    cause it to shift position on the screen.
  2092.      */
  2093.  
  2094.     serial = NextRequest(winPtr->display);
  2095.     if (wmPtr->flags & WM_MOVE_PENDING) {
  2096.     wmPtr->configWidth = width;
  2097.     wmPtr->configHeight = height;
  2098.     if (wmTracing) {
  2099.         printf("UpdateGeometryInfo moving to %d %d, resizing to %d x %d,\n",
  2100.             x, y, width, height);
  2101.     }
  2102.     Tk_MoveResizeWindow((Tk_Window) winPtr, x, y, (unsigned) width,
  2103.         (unsigned) height);
  2104.     } else if ((width != wmPtr->configWidth)
  2105.         || (height != wmPtr->configHeight)) {
  2106.     wmPtr->configWidth = width;
  2107.     wmPtr->configHeight = height;
  2108.     if (wmTracing) {
  2109.         printf("UpdateGeometryInfo resizing to %d x %d\n", width, height);
  2110.     }
  2111.     Tk_ResizeWindow((Tk_Window) winPtr, (unsigned) width,
  2112.         (unsigned) height);
  2113.     } else {
  2114.     return;
  2115.     }
  2116.  
  2117.     /*
  2118.      * Wait for the configure operation to complete.  Don't need to do
  2119.      * this, however, if the window is about to be mapped:  it will be
  2120.      * taken care of elsewhere.
  2121.      */
  2122.  
  2123.     if (!(wmPtr->flags & WM_ABOUT_TO_MAP)) {
  2124.     WaitForConfigureNotify(winPtr, serial);
  2125.     }
  2126. }
  2127.  
  2128. /*
  2129.  *--------------------------------------------------------------
  2130.  *
  2131.  * UpdateSizeHints --
  2132.  *
  2133.  *    This procedure is called to update the window manager's
  2134.  *    size hints information from the information in a WmInfo
  2135.  *    structure.
  2136.  *
  2137.  * Results:
  2138.  *    None.
  2139.  *
  2140.  * Side effects:
  2141.  *    Properties get changed for winPtr.
  2142.  *
  2143.  *--------------------------------------------------------------
  2144.  */
  2145.  
  2146. static void
  2147. UpdateSizeHints(winPtr)
  2148.     TkWindow *winPtr;
  2149. {
  2150.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  2151.     XSizeHints *hintsPtr;
  2152.  
  2153.     wmPtr->flags &= ~WM_UPDATE_SIZE_HINTS;
  2154.  
  2155.     hintsPtr = XAllocSizeHints();
  2156.     if (hintsPtr == NULL) {
  2157.     return;
  2158.     }
  2159.  
  2160.     /*
  2161.      * Compute the pixel-based sizes for the various fields in the
  2162.      * size hints structure, based on the grid-based sizes in
  2163.      * our structure.
  2164.      */
  2165.  
  2166.     if (IS_GRIDDED(wmPtr)) {
  2167.     hintsPtr->base_width = winPtr->reqWidth
  2168.         - (wmPtr->reqGridWidth * wmPtr->widthInc);
  2169.     if (hintsPtr->base_width < 0) {
  2170.         hintsPtr->base_width = 0;
  2171.     }
  2172.     hintsPtr->base_height = winPtr->reqHeight
  2173.         - (wmPtr->reqGridHeight * wmPtr->heightInc);
  2174.     if (hintsPtr->base_height < 0) {
  2175.         hintsPtr->base_height = 0;
  2176.     }
  2177.     hintsPtr->min_width = hintsPtr->base_width
  2178.         + (wmPtr->minWidth * wmPtr->widthInc);
  2179.     hintsPtr->min_height = hintsPtr->base_height
  2180.         + (wmPtr->minHeight * wmPtr->heightInc);
  2181.     hintsPtr->max_width = hintsPtr->base_width
  2182.         + (wmPtr->maxWidth * wmPtr->widthInc);
  2183.     hintsPtr->max_height = hintsPtr->base_height
  2184.         + (wmPtr->maxHeight * wmPtr->heightInc);
  2185.     } else {
  2186.     hintsPtr->min_width = wmPtr->minWidth;
  2187.     hintsPtr->min_height = wmPtr->minHeight;
  2188.     hintsPtr->max_width = wmPtr->maxWidth;
  2189.     hintsPtr->max_height = wmPtr->maxHeight;
  2190.     hintsPtr->base_width = 0;
  2191.     hintsPtr->base_height = 0;
  2192.     }
  2193.     hintsPtr->width_inc = wmPtr->widthInc;
  2194.     hintsPtr->height_inc = wmPtr->heightInc;
  2195.     hintsPtr->min_aspect.x = wmPtr->minAspect.x;
  2196.     hintsPtr->min_aspect.y = wmPtr->minAspect.y;
  2197.     hintsPtr->max_aspect.x = wmPtr->maxAspect.x;
  2198.     hintsPtr->max_aspect.y = wmPtr->maxAspect.y;
  2199.     hintsPtr->win_gravity = wmPtr->gravity;
  2200.     hintsPtr->flags = wmPtr->sizeHintsFlags;
  2201.  
  2202.     /*
  2203.      * If a window is non-gridded and no minimum or maximum size has
  2204.      * been specified, don't let the window be resized at all.
  2205.      */
  2206.  
  2207.     if (!IS_GRIDDED(wmPtr)
  2208.         && ((wmPtr->sizeHintsFlags & (PMinSize|PMaxSize)) == 0)) {
  2209.     int width, height;
  2210.  
  2211.     width = wmPtr->width;
  2212.     height = wmPtr->height;
  2213.     if (width < 0) {
  2214.         width = winPtr->reqWidth;
  2215.         height = winPtr->reqHeight;
  2216.     }
  2217.     hintsPtr->min_width = hintsPtr->max_width = width;
  2218.     hintsPtr->min_height = hintsPtr->max_height = height;
  2219.     hintsPtr->flags |= PMinSize|PMaxSize;
  2220.     }
  2221.  
  2222.     /*
  2223.      * If min or max size isn't specified, fill in with extreme values
  2224.      * rather than leaving unspecified.  Otherwise window manager may
  2225.      * do someting counter-intuitive like the last value ever specified.
  2226.      */
  2227.  
  2228.     if (!(hintsPtr->flags & PMinSize)) {
  2229.     hintsPtr->min_width = hintsPtr->min_height = 0;
  2230.     hintsPtr->flags |= PMinSize;
  2231.     }
  2232.     if (!(hintsPtr->flags & PMaxSize)) {
  2233.     /*
  2234.      * Set the maximum size to the display dimensions, minus a guess
  2235.      * about how much space is needed for window manager decorations.
  2236.      */
  2237.  
  2238.     hintsPtr->max_width =
  2239.         DisplayWidth(winPtr->display, winPtr->screenNum) - 15;
  2240.     hintsPtr->max_height = 
  2241.         DisplayHeight(winPtr->display, winPtr->screenNum) - 30;
  2242.     hintsPtr->flags |= PMaxSize;
  2243.     }
  2244.  
  2245.     XSetWMNormalHints(winPtr->display, winPtr->window, hintsPtr);
  2246.  
  2247.     XFree((char *) hintsPtr);
  2248. }
  2249.  
  2250. /*
  2251.  *----------------------------------------------------------------------
  2252.  *
  2253.  * WaitForConfigureNotify --
  2254.  *
  2255.  *    This procedure is invoked in order to synchronize with the
  2256.  *    window manager.  It waits for a ConfigureNotify event to
  2257.  *    arrive, signalling that the window manager has seen an attempt
  2258.  *    on our part to move or resize a top-level window.
  2259.  *
  2260.  * Results:
  2261.  *    None.
  2262.  *
  2263.  * Side effects:
  2264.  *    Delays the execution of the process until a ConfigureNotify event
  2265.  *    arrives with serial number at least as great as serial.  This
  2266.  *    is useful for two reasons:
  2267.  *
  2268.  *    1. It's important to distinguish ConfigureNotify events that are
  2269.  *       coming in response to a request we've made from those generated
  2270.  *       spontaneously by the user.  The reason for this is that if the
  2271.  *       user resizes the window we take that as an order to ignore
  2272.  *       geometry requests coming from inside the window hierarchy.  If
  2273.  *       we accidentally interpret a response to our request as a
  2274.  *       user-initiated action, the window will stop responding to
  2275.  *       new geometry requests.  To make this distinction, (a) this
  2276.  *       procedure sets a flag for TopLevelEventProc to indicate that
  2277.  *       we're waiting to sync with the wm, and (b) all changes to
  2278.  *       the size of a top-level window are followed by calls to this
  2279.  *       procedure.
  2280.  *    2. Races and confusion can come about if there are multiple
  2281.  *       operations outstanding at a time (e.g. two different resizes
  2282.  *       of the top-level window:  it's hard to tell which of the
  2283.  *       ConfigureNotify events coming back is for which request).
  2284.  *    While waiting, all events covered by StructureNotifyMask are
  2285.  *    processed and all others are deferred.
  2286.  *
  2287.  *----------------------------------------------------------------------
  2288.  */
  2289.  
  2290. static void
  2291. WaitForConfigureNotify(winPtr, serial)
  2292.     TkWindow *winPtr;        /* Top-level window for which we want
  2293.                  * to see a ConfigureNotify. */
  2294.     unsigned long serial;    /* Serial number of resize request.  Want to
  2295.                  * be sure wm has seen this. */
  2296. {
  2297.     WmInfo *wmPtr = winPtr->wmInfoPtr;
  2298.     XEvent event;
  2299.     int diff;
  2300.     int gotConfig = 0;
  2301.  
  2302.     /*
  2303.      * One more tricky detail about this procedure.  In some cases the
  2304.      * window manager will decide to ignore a configure request (e.g.
  2305.      * because it thinks the window is already in the right place).
  2306.      * To avoid hanging in this situation, only wait for a few seconds,
  2307.      * then give up.
  2308.      */
  2309.  
  2310.     while (!gotConfig) {
  2311.     if (WaitForEvent(winPtr->display, winPtr->window, StructureNotifyMask,
  2312.         &event) != TCL_OK) {
  2313.         if (wmTracing) {
  2314.         printf("WaitForConfigureNotify giving up on %s\n",
  2315.             winPtr->pathName);
  2316.         }
  2317.         break;
  2318.     }
  2319.     wmPtr->flags |= WM_SYNC_PENDING;
  2320.     Tk_HandleEvent(&event);
  2321.     wmPtr->flags &= ~WM_SYNC_PENDING;
  2322.     if (event.type == ConfigureNotify) {
  2323.         diff = event.xconfigure.serial - serial;
  2324.         if (diff >= 0) {
  2325.         gotConfig = 1;
  2326.         }
  2327.     }
  2328.     }
  2329.     wmPtr->flags &= ~WM_MOVE_PENDING;
  2330.     if (wmTracing) {
  2331.     printf("WaitForConfigureNotify finished with %s, serial %d\n",
  2332.         winPtr->pathName, serial);
  2333.     }
  2334. }
  2335.  
  2336. /*
  2337.  *----------------------------------------------------------------------
  2338.  *
  2339.  * WaitForEvent --
  2340.  *
  2341.  *    This procedure is used by WaitForConfigureNotify and
  2342.  *    WaitForMapNotify to wait for an event of a certain type
  2343.  *    to arrive.
  2344.  *
  2345.  * Results:
  2346.  *    Under normal conditions, TCL_OK is returned and an event for
  2347.  *    display and window that matches "mask" is stored in *eventPtr.
  2348.  *    If a long time goes by with no event of the right type arriving,
  2349.  *    or if an error occurs while waiting for the event to arrive,
  2350.  *    then TCL_ERROR is returned.
  2351.  *
  2352.  * Side effects:
  2353.  *    Events are removed from the display's event queue.  Time goes by.
  2354.  *
  2355.  *----------------------------------------------------------------------
  2356.  */
  2357.  
  2358. static int
  2359. WaitForEvent(display, window, mask, eventPtr)
  2360.     Display *display;        /* Display event is coming from. */
  2361.     Window window;        /* Window for which event is desired. */
  2362.     long mask;            /* Selects events of interest. */
  2363.     XEvent *eventPtr;        /* Place to store event. */
  2364. {
  2365. #define TIMEOUT_SECS 5
  2366.     fd_mask masks[MASK_SIZE];
  2367.     struct timeval timeout, startTime, curTime;
  2368.     int i, numFound, fd;
  2369.  
  2370.     for (i = 0; i < MASK_SIZE; i++) {
  2371.     masks[i] = 0;
  2372.     }
  2373.     fd = ConnectionNumber(display);
  2374.     (void) gettimeofday(&startTime, (struct timezone *) NULL);
  2375.     while (1) {
  2376.     if (XCheckWindowEvent(display, window, mask, eventPtr)) {
  2377.         return TCL_OK;
  2378.     }
  2379.     masks[fd/(NBBY*sizeof(fd_mask))] = 1 << fd%(NBBY*sizeof(fd_mask));
  2380.     timeout.tv_sec = 1;
  2381.     timeout.tv_usec = 0;
  2382.     numFound = select(fd+1, (SELECT_MASK *) masks,
  2383.         (SELECT_MASK *) NULL, (SELECT_MASK *) NULL,
  2384.         &timeout);
  2385.     if ((numFound == -1) && (errno != EINTR)) {
  2386.         return TCL_ERROR;
  2387.     }
  2388.     (void) gettimeofday(&curTime, (struct timezone *) NULL);
  2389.     i = curTime.tv_sec - startTime.tv_sec;
  2390.     if (i >= 5) {
  2391.         return TCL_ERROR;
  2392.     }
  2393.     }
  2394. }
  2395.  
  2396. /*
  2397.  *----------------------------------------------------------------------
  2398.  *
  2399.  * WaitForMapNotify --
  2400.  *
  2401.  *    This procedure is invoked in order to synchronize with the
  2402.  *    window manager.  It waits for the window's mapped state to
  2403.  *    reach the value given by mapped.
  2404.  *
  2405.  * Results:
  2406.  *    None.
  2407.  *
  2408.  * Side effects:
  2409.  *    Delays the execution of the process until winPtr becomes mapped
  2410.  *    or unmapped, depending on the "mapped" argument.  This allows us
  2411.  *    to synchronize with the window manager, and allows us to
  2412.  *    identify changes in window size that come about when the window
  2413.  *    manager first starts managing the window (as opposed to those
  2414.  *    requested interactively by the user later).  See the comments
  2415.  *    for WaitForConfigureNotify and WM_SYNC_PENDING.  While waiting,
  2416.  *    all events covered by StructureNotifyMask are processed and all
  2417.  *    others are deferred.
  2418.  *
  2419.  *----------------------------------------------------------------------
  2420.  */
  2421.  
  2422. static void
  2423. WaitForMapNotify(winPtr, mapped)
  2424.     TkWindow *winPtr;        /* Top-level window for which we want
  2425.                  * to see a particular mapping state. */
  2426.     int mapped;            /* If non-zero, wait for window to become
  2427.                  * mapped, otherwise wait for it to become
  2428.                  * unmapped. */
  2429. {
  2430.     WmInfo *wmPtr = winPtr->wmInfoPtr;
  2431.     XEvent event;
  2432.  
  2433.     while (1) {
  2434.     if (mapped) {
  2435.         if (winPtr->flags & TK_MAPPED) {
  2436.         break;
  2437.         }
  2438.     } else if (!(winPtr->flags & TK_MAPPED)) {
  2439.         break;
  2440.     }
  2441.     if (WaitForEvent(winPtr->display, winPtr->window, StructureNotifyMask,
  2442.         &event) != TCL_OK) {
  2443.         /*
  2444.          * There are some bizarre situations in which the window
  2445.          * manager can't respond or chooses not to (e.g. if we've
  2446.          * got a grab set it can't respond).  If this happens then
  2447.          * just quit.
  2448.          */
  2449.  
  2450.         if (wmTracing) {
  2451.         printf("WaitForMapNotify giving up on %s\n", winPtr->pathName);
  2452.         }
  2453.         break;
  2454.     }
  2455.     wmPtr->flags |= WM_SYNC_PENDING;
  2456.     Tk_HandleEvent(&event);
  2457.     wmPtr->flags &= ~WM_SYNC_PENDING;
  2458.     }
  2459.     wmPtr->flags &= ~WM_MOVE_PENDING;
  2460.     if (wmTracing) {
  2461.     printf("WaitForMapNotify finished with %s\n", winPtr->pathName);
  2462.     }
  2463. }
  2464.  
  2465. /*
  2466.  *--------------------------------------------------------------
  2467.  *
  2468.  * UpdateHints --
  2469.  *
  2470.  *    This procedure is called to update the window manager's
  2471.  *    hints information from the information in a WmInfo
  2472.  *    structure.
  2473.  *
  2474.  * Results:
  2475.  *    None.
  2476.  *
  2477.  * Side effects:
  2478.  *    Properties get changed for winPtr.
  2479.  *
  2480.  *--------------------------------------------------------------
  2481.  */
  2482.  
  2483. static void
  2484. UpdateHints(winPtr)
  2485.     TkWindow *winPtr;
  2486. {
  2487.     WmInfo *wmPtr = winPtr->wmInfoPtr;
  2488.  
  2489.     if (wmPtr->flags & WM_NEVER_MAPPED) {
  2490.     return;
  2491.     }
  2492.     XSetWMHints(winPtr->display, winPtr->window, &wmPtr->hints);
  2493. }
  2494.  
  2495. /*
  2496.  *--------------------------------------------------------------
  2497.  *
  2498.  * ParseGeometry --
  2499.  *
  2500.  *    This procedure parses a geometry string and updates
  2501.  *    information used to control the geometry of a top-level
  2502.  *    window.
  2503.  *
  2504.  * Results:
  2505.  *    A standard Tcl return value, plus an error message in
  2506.  *    interp->result if an error occurs.
  2507.  *
  2508.  * Side effects:
  2509.  *    The size and/or location of winPtr may change.
  2510.  *
  2511.  *--------------------------------------------------------------
  2512.  */
  2513.  
  2514. static int
  2515. ParseGeometry(interp, string, winPtr)
  2516.     Tcl_Interp *interp;        /* Used for error reporting. */
  2517.     char *string;        /* String containing new geometry.  Has the
  2518.                  * standard form "=wxh+x+y". */
  2519.     TkWindow *winPtr;        /* Pointer to top-level window whose
  2520.                  * geometry is to be changed. */
  2521. {
  2522.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  2523.     int x, y, width, height, flags;
  2524.     char *end;
  2525.     register char *p = string;
  2526.  
  2527.     /*
  2528.      * The leading "=" is optional.
  2529.      */
  2530.  
  2531.     if (*p == '=') {
  2532.     p++;
  2533.     }
  2534.  
  2535.     /*
  2536.      * Parse the width and height, if they are present.  Don't
  2537.      * actually update any of the fields of wmPtr until we've
  2538.      * successfully parsed the entire geometry string.
  2539.      */
  2540.  
  2541.     width = wmPtr->width;
  2542.     height = wmPtr->height;
  2543.     x = wmPtr->x;
  2544.     y = wmPtr->y;
  2545.     flags = wmPtr->flags;
  2546.     if (isdigit(*p)) {
  2547.     width = strtoul(p, &end, 10);
  2548.     p = end;
  2549.     if (*p != 'x') {
  2550.         goto error;
  2551.     }
  2552.     p++;
  2553.     if (!isdigit(*p)) {
  2554.         goto error;
  2555.     }
  2556.     height = strtoul(p, &end, 10);
  2557.     p = end;
  2558.     }
  2559.  
  2560.     /*
  2561.      * Parse the X and Y coordinates, if they are present.
  2562.      */
  2563.  
  2564.     if (*p != '\0') {
  2565.     flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y);
  2566.     if (*p == '-') {
  2567.         flags |= WM_NEGATIVE_X;
  2568.     } else if (*p != '+') {
  2569.         goto error;
  2570.     }
  2571.     x = strtol(p+1, &end, 10);
  2572.     p = end;
  2573.     if (*p == '-') {
  2574.         flags |= WM_NEGATIVE_Y;
  2575.     } else if (*p != '+') {
  2576.         goto error;
  2577.     }
  2578.     y = strtol(p+1, &end, 10);
  2579.     if (*end != '\0') {
  2580.         goto error;
  2581.     }
  2582.  
  2583.     /*
  2584.      * Assume that the geometry information came from the user,
  2585.      * unless an explicit source has been specified.  Otherwise
  2586.      * most window managers assume that the size hints were
  2587.      * program-specified and they ignore them.
  2588.      */
  2589.  
  2590.     if ((wmPtr->sizeHintsFlags & (USPosition|PPosition)) == 0) {
  2591.         wmPtr->sizeHintsFlags |= USPosition;
  2592.         flags |= WM_UPDATE_SIZE_HINTS;
  2593.     }
  2594.     }
  2595.  
  2596.     /*
  2597.      * Everything was parsed OK.  Update the fields of *wmPtr and
  2598.      * arrange for the appropriate information to be percolated out
  2599.      * to the window manager at the next idle moment.
  2600.      */
  2601.  
  2602.     wmPtr->width = width;
  2603.     wmPtr->height = height;
  2604.     if ((x != wmPtr->x) || (y != wmPtr->y)
  2605.         || ((flags & (WM_NEGATIVE_X|WM_NEGATIVE_Y))
  2606.         != (wmPtr->flags & (WM_NEGATIVE_X|WM_NEGATIVE_Y)))) {
  2607.     wmPtr->x = x;
  2608.     wmPtr->y = y;
  2609.     flags |= WM_MOVE_PENDING;
  2610.     }
  2611.     wmPtr->flags = flags;
  2612.  
  2613.     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
  2614.     Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
  2615.     wmPtr->flags |= WM_UPDATE_PENDING;
  2616.     }
  2617.     return TCL_OK;
  2618.  
  2619.     error:
  2620.     Tcl_AppendResult(interp, "bad geometry specifier \"",
  2621.         string, "\"", (char *) NULL);
  2622.     return TCL_ERROR;
  2623. }
  2624.  
  2625. /*
  2626.  *----------------------------------------------------------------------
  2627.  *
  2628.  * Tk_GetRootCoords --
  2629.  *
  2630.  *    Given a token for a window, this procedure traces through the
  2631.  *    window's lineage to find the (virtual) root-window coordinates
  2632.  *    corresponding to point (0,0) in the window.
  2633.  *
  2634.  * Results:
  2635.  *    The locations pointed to by xPtr and yPtr are filled in with
  2636.  *    the root coordinates of the (0,0) point in tkwin.  If a virtual
  2637.  *    root window is in effect for the window, then the coordinates
  2638.  *    in the virtual root are returned.
  2639.  *
  2640.  * Side effects:
  2641.  *    None.
  2642.  *
  2643.  *----------------------------------------------------------------------
  2644.  */
  2645.  
  2646. void
  2647. Tk_GetRootCoords(tkwin, xPtr, yPtr)
  2648.     Tk_Window tkwin;        /* Token for window. */
  2649.     int *xPtr;            /* Where to store x-displacement of (0,0). */
  2650.     int *yPtr;            /* Where to store y-displacement of (0,0). */
  2651. {
  2652.     int x, y;
  2653.     register TkWindow *winPtr = (TkWindow *) tkwin;
  2654.  
  2655.     /*
  2656.      * Search back through this window's parents all the way to a
  2657.      * top-level window, combining the offsets of each window within
  2658.      * its parent.
  2659.      */
  2660.  
  2661.     x = y = 0;
  2662.     while (1) {
  2663.     x += winPtr->changes.x + winPtr->changes.border_width;
  2664.     y += winPtr->changes.y + winPtr->changes.border_width;
  2665.     if (winPtr->flags & TK_TOP_LEVEL) {
  2666.         break;
  2667.     }
  2668.     winPtr = winPtr->parentPtr;
  2669.     }
  2670.     *xPtr = x;
  2671.     *yPtr = y;
  2672. }
  2673.  
  2674. /*
  2675.  *----------------------------------------------------------------------
  2676.  *
  2677.  * Tk_CoordsToWindow --
  2678.  *
  2679.  *    Given the (virtual) root coordinates of a point, this procedure
  2680.  *    returns the token for the top-most window covering that point,
  2681.  *    if there exists such a window in this application.
  2682.  *
  2683.  * Results:
  2684.  *    The return result is either a token for the window corresponding
  2685.  *    to rootX and rootY, or else NULL to indicate that there is no such
  2686.  *    window.
  2687.  *
  2688.  * Side effects:
  2689.  *    None.
  2690.  *
  2691.  *----------------------------------------------------------------------
  2692.  */
  2693.  
  2694. Tk_Window
  2695. Tk_CoordsToWindow(rootX, rootY, tkwin)
  2696.     int rootX, rootY;        /* Coordinates of point in root window.  If
  2697.                  * a virtual-root window manager is in use,
  2698.                  * these coordinates refer to the virtual
  2699.                  * root, not the real root. */
  2700.     Tk_Window tkwin;        /* Token for any window in application;
  2701.                  * used to identify the display. */
  2702. {
  2703.     Window rootChild, dummy3, dummy4, vRoot;
  2704.     int i, dummy1, dummy2;
  2705.     register WmInfo *wmPtr;
  2706.     register TkWindow *winPtr, *childPtr;
  2707.     TkWindow *nextPtr;        /* Coordinates of highest child found so
  2708.                  * far that contains point. */
  2709.     int x, y;            /* Coordinates in winPtr. */
  2710.     int tmpx, tmpy, bd;
  2711.     Window *children;        /* Children of winPtr, or NULL. */
  2712.     unsigned int numChildren;    /* Size of children array. */
  2713.  
  2714.     /*
  2715.      * Step 1: find any top-level window for the right screen, then
  2716.      * use it to find the virtual root.
  2717.      */
  2718.  
  2719.  
  2720.     while (!Tk_IsTopLevel(tkwin)) {
  2721.     tkwin = Tk_Parent(tkwin);
  2722.     }
  2723.     vRoot = ((TkWindow *) tkwin)->wmInfoPtr->vRoot;
  2724.     if (vRoot == None) {
  2725.     vRoot = RootWindowOfScreen(Tk_Screen(tkwin));
  2726.     }
  2727.  
  2728.     /*
  2729.      * Step 2:  find the top-level window that contains the desired
  2730.      * coordinates.
  2731.      */
  2732.  
  2733.     if (XTranslateCoordinates(Tk_Display(tkwin), vRoot, vRoot, rootX,
  2734.         rootY, &dummy1, &dummy2, &rootChild) == False) {
  2735.     panic("Tk_CoordsToWindow get False return from XTranslateCoordinates");
  2736.     }
  2737.     for (wmPtr = firstWmPtr; ; wmPtr = wmPtr->nextPtr) {
  2738.     if (wmPtr == NULL) {
  2739.         return NULL;
  2740.     }
  2741.     if ((wmPtr->reparent == rootChild) || ((wmPtr->reparent == None)
  2742.         && (wmPtr->winPtr->window == rootChild))) {
  2743.         break;
  2744.     }
  2745.     }
  2746.     winPtr = wmPtr->winPtr;
  2747.     if (winPtr->mainPtr != ((TkWindow *) tkwin)->mainPtr) {
  2748.     return NULL;
  2749.     }
  2750.  
  2751.     /*
  2752.      * Step 3: work down through the hierarchy underneath this window.
  2753.      * At each level, scan through all the children to see if any contain
  2754.      * the point.  If none do, then we're done.  If one does, then do the
  2755.      * same thing on that child.  If two or more do, then fetch enough
  2756.      * information from the window server to figure out which is on top,
  2757.      * and repeat on that child.
  2758.      */
  2759.  
  2760.     x = rootX;
  2761.     y = rootY;
  2762.     while (1) {
  2763.     x -= winPtr->changes.x;
  2764.     y -= winPtr->changes.y;
  2765.     nextPtr = NULL;
  2766.     children = NULL;
  2767.     for (childPtr = winPtr->childList; childPtr != NULL;
  2768.         childPtr = childPtr->nextPtr) {
  2769.         if (!Tk_IsMapped(childPtr) || (childPtr->flags & TK_TOP_LEVEL)) {
  2770.         continue;
  2771.         }
  2772.         tmpx = x - childPtr->changes.x;
  2773.         tmpy = y - childPtr->changes.y;
  2774.         bd = childPtr->changes.border_width;
  2775.         if ((tmpx < -bd) || (tmpy < -bd)
  2776.             || (tmpx >= (childPtr->changes.width + bd))
  2777.             || (tmpy >= (childPtr->changes.height + bd))) {
  2778.         continue;
  2779.         }
  2780.         if (nextPtr == NULL) {
  2781.         nextPtr = childPtr;
  2782.         continue;
  2783.         }
  2784.  
  2785.         /*
  2786.          * More than one child of same parent overlaps point.  Must
  2787.          * figure out which is on top.  Keep a cache of the stacking
  2788.          * order for winPtr to help with this, in case there are >2
  2789.          * children overlapping.
  2790.          */
  2791.  
  2792.         if (children == NULL) {
  2793.         if (XQueryTree(winPtr->display, winPtr->window, &dummy3,
  2794.             &dummy4, &children, &numChildren) == 0) {
  2795.             panic("Tk_CoordsToWindow get error return from XQueryTree");
  2796.         }
  2797.         }
  2798.         for (i = 0; i < numChildren; i++) {
  2799.         if (children[i] == childPtr->window) {
  2800.             break;
  2801.         }
  2802.         if (children[i] == nextPtr->window) {
  2803.             nextPtr = childPtr;
  2804.             break;
  2805.         }
  2806.         }
  2807.     }
  2808.     if (children != NULL) {
  2809.         XFree((char *) children);
  2810.     }
  2811.     if (nextPtr == NULL) {
  2812.         break;
  2813.     }
  2814.     winPtr = nextPtr;
  2815.     }
  2816.     return (Tk_Window) winPtr;
  2817. }
  2818.  
  2819. /*
  2820.  *----------------------------------------------------------------------
  2821.  *
  2822.  * UpdateVRootGeometry --
  2823.  *
  2824.  *    This procedure is called to update all the virtual root
  2825.  *    geometry information in wmPtr.
  2826.  *
  2827.  * Results:
  2828.  *    None.
  2829.  *
  2830.  * Side effects:
  2831.  *    The vRootX, vRootY, vRootWidth, and vRootHeight fields in
  2832.  *    wmPtr are filled with the most up-to-date information.
  2833.  *
  2834.  *----------------------------------------------------------------------
  2835.  */
  2836.  
  2837. static void
  2838. UpdateVRootGeometry(wmPtr)
  2839.     WmInfo *wmPtr;        /* Window manager information to be
  2840.                  * updated.  The wmPtr->vRoot field must
  2841.                  * be valid. */
  2842. {
  2843.     TkWindow *winPtr = wmPtr->winPtr;
  2844.     unsigned int bd, dummy;
  2845.     Window dummy2;
  2846.     Status status;
  2847.     Tk_ErrorHandler handler;
  2848.  
  2849.     /*
  2850.      * If this isn't a virtual-root window manager, just return information
  2851.      * about the screen.
  2852.      */
  2853.  
  2854.     wmPtr->flags &= ~WM_VROOT_OFFSET_STALE;
  2855.     if (wmPtr->vRoot == None) {
  2856.     noVRoot:
  2857.     wmPtr->vRootX = wmPtr->vRootY = 0;
  2858.     wmPtr->vRootWidth = DisplayWidth(winPtr->display, winPtr->screenNum);
  2859.     wmPtr->vRootHeight = DisplayHeight(winPtr->display, winPtr->screenNum);
  2860.     return;
  2861.     }
  2862.  
  2863.     /*
  2864.      * Refresh the virtual root information if it's out of date.
  2865.      */
  2866.  
  2867.     handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
  2868.         (Tk_ErrorProc *) NULL, (ClientData) NULL);
  2869.     status = XGetGeometry(winPtr->display, wmPtr->vRoot,
  2870.         &dummy2, &wmPtr->vRootX, &wmPtr->vRootY,
  2871.         &wmPtr->vRootWidth, &wmPtr->vRootHeight, &bd, &dummy);
  2872.     if (wmTracing) {
  2873.     printf("UpdateVRootGeometry: x = %d, y = %d, width = %d, ",
  2874.         wmPtr->vRootX, wmPtr->vRootY, wmPtr->vRootWidth);
  2875.     printf("height = %d, status = %d\n", wmPtr->vRootHeight, status);
  2876.     }
  2877.     Tk_DeleteErrorHandler(handler);
  2878.     if (status == 0) {
  2879.     /*
  2880.      * The virtual root is gone!  Pretend that it never existed.
  2881.      */
  2882.  
  2883.     wmPtr->vRoot = None;
  2884.     goto noVRoot;
  2885.     }
  2886. }
  2887.  
  2888. /*
  2889.  *----------------------------------------------------------------------
  2890.  *
  2891.  * Tk_GetVRootGeometry --
  2892.  *
  2893.  *    This procedure returns information about the virtual root
  2894.  *    window corresponding to a particular Tk window.
  2895.  *
  2896.  * Results:
  2897.  *    The values at xPtr, yPtr, widthPtr, and heightPtr are set
  2898.  *    with the offset and dimensions of the root window corresponding
  2899.  *    to tkwin.  If tkwin is being managed by a virtual root window
  2900.  *    manager these values correspond to the virtual root window being
  2901.  *    used for tkwin;  otherwise the offsets will be 0 and the
  2902.  *    dimensions will be those of the screen.
  2903.  *
  2904.  * Side effects:
  2905.  *    Vroot window information is refreshed if it is out of date.
  2906.  *
  2907.  *----------------------------------------------------------------------
  2908.  */
  2909.  
  2910. void
  2911. Tk_GetVRootGeometry(tkwin, xPtr, yPtr, widthPtr, heightPtr)
  2912.     Tk_Window tkwin;        /* Window whose virtual root is to be
  2913.                  * queried. */
  2914.     int *xPtr, *yPtr;        /* Store x and y offsets of virtual root
  2915.                  * here. */
  2916.     unsigned int *widthPtr, *heightPtr;
  2917.                 /* Store dimensions of virtual root here. */
  2918. {
  2919.     WmInfo *wmPtr;
  2920.     TkWindow *winPtr = (TkWindow *) tkwin;
  2921.  
  2922.     /*
  2923.      * Find the top-level window for tkwin, and locate the window manager
  2924.      * information for that window.
  2925.      */
  2926.  
  2927.     while (!(winPtr->flags & TK_TOP_LEVEL)) {
  2928.     winPtr = winPtr->parentPtr;
  2929.     }
  2930.     wmPtr = winPtr->wmInfoPtr;
  2931.  
  2932.     /*
  2933.      * Make sure that the geometry information is up-to-date, then copy
  2934.      * it out to the caller.
  2935.      */
  2936.  
  2937.     if (wmPtr->flags & WM_VROOT_OFFSET_STALE) {
  2938.     UpdateVRootGeometry(wmPtr);
  2939.     }
  2940.     *xPtr = wmPtr->vRootX;
  2941.     *yPtr = wmPtr->vRootY;
  2942.     *widthPtr = wmPtr->vRootWidth;
  2943.     *heightPtr = wmPtr->vRootHeight;
  2944. }
  2945.  
  2946. /*
  2947.  *----------------------------------------------------------------------
  2948.  *
  2949.  * Tk_MoveToplevelWindow --
  2950.  *
  2951.  *    This procedure is called instead of Tk_MoveWindow to adjust
  2952.  *    the x-y location of a top-level window.  It delays the actual
  2953.  *    move to a later time and keeps window-manager information
  2954.  *    up-to-date with the move
  2955.  *
  2956.  * Results:
  2957.  *    None.
  2958.  *
  2959.  * Side effects:
  2960.  *    The window is eventually moved so that its upper-left corner
  2961.  *    (actually, the upper-left corner of the window's decorative
  2962.  *    frame, if there is one) is at (x,y).
  2963.  *
  2964.  *----------------------------------------------------------------------
  2965.  */
  2966.  
  2967. void
  2968. Tk_MoveToplevelWindow(tkwin, x, y)
  2969.     Tk_Window tkwin;        /* Window to move. */
  2970.     int x, y;            /* New location for window (within
  2971.                  * parent). */
  2972. {
  2973.     TkWindow *winPtr = (TkWindow *) tkwin;
  2974.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  2975.  
  2976.     if (!(winPtr->flags & TK_TOP_LEVEL)) {
  2977.     panic("Tk_MoveToplevelWindow called with non-toplevel window");
  2978.     }
  2979.     wmPtr->x = x;
  2980.     wmPtr->y = y;
  2981.     wmPtr->flags |= WM_MOVE_PENDING;
  2982.     wmPtr->flags &= ~(WM_NEGATIVE_X|WM_NEGATIVE_Y);
  2983.     if ((wmPtr->sizeHintsFlags & (USPosition|PPosition)) == 0) {
  2984.     wmPtr->sizeHintsFlags |= USPosition;
  2985.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  2986.     }
  2987.  
  2988.     /*
  2989.      * If the window has already been mapped, must bring its geometry
  2990.      * up-to-date immediately, otherwise an event might arrive from the
  2991.      * server that would overwrite wmPtr->x and wmPtr->y and lose the
  2992.      * new position.
  2993.      */
  2994.  
  2995.     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  2996.     if (wmPtr->flags & WM_UPDATE_PENDING) {
  2997.         Tk_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
  2998.     }
  2999.     UpdateGeometryInfo((ClientData) winPtr);
  3000.     }
  3001. }
  3002.  
  3003. /*
  3004.  *----------------------------------------------------------------------
  3005.  *
  3006.  * UpdateWmProtocols --
  3007.  *
  3008.  *    This procedure transfers the most up-to-date information about
  3009.  *    window manager protocols from the WmInfo structure to the actual
  3010.  *    property on the top-level window.
  3011.  *
  3012.  * Results:
  3013.  *    None.
  3014.  *
  3015.  * Side effects:
  3016.  *    The WM_PROTOCOLS property gets changed for wmPtr's window.
  3017.  *
  3018.  *----------------------------------------------------------------------
  3019.  */
  3020.  
  3021. static void
  3022. UpdateWmProtocols(wmPtr)
  3023.     register WmInfo *wmPtr;    /* Information about top-level window. */
  3024. {
  3025.     register ProtocolHandler *protPtr;
  3026.     Atom deleteWindowAtom;
  3027.     int count;
  3028.     Atom *arrayPtr, *atomPtr;
  3029.  
  3030.     /*
  3031.      * There are only two tricky parts here.  First, there could be any
  3032.      * number of atoms for the window, so count them and malloc an array
  3033.      * to hold all of their atoms.  Second, we *always* want to respond
  3034.      * to the WM_DELETE_WINDOW protocol, even if no-one's officially asked.
  3035.      */
  3036.  
  3037.     for (protPtr = wmPtr->protPtr, count = 1; protPtr != NULL;
  3038.         protPtr = protPtr->nextPtr, count++) {
  3039.     /* Empty loop body;  we're just counting the handlers. */
  3040.     }
  3041.     arrayPtr = (Atom *) ckalloc((unsigned) (count * sizeof(Atom)));
  3042.     deleteWindowAtom = Tk_InternAtom((Tk_Window) wmPtr->winPtr,
  3043.         "WM_DELETE_WINDOW");
  3044.     arrayPtr[0] = deleteWindowAtom;
  3045.     for (protPtr = wmPtr->protPtr, atomPtr = &arrayPtr[1];
  3046.         protPtr != NULL; protPtr = protPtr->nextPtr) {
  3047.     if (protPtr->protocol != deleteWindowAtom) {
  3048.         *atomPtr = protPtr->protocol;
  3049.         atomPtr++;
  3050.     }
  3051.     }
  3052.     XChangeProperty(wmPtr->winPtr->display, wmPtr->winPtr->window,
  3053.         Tk_InternAtom((Tk_Window) wmPtr->winPtr, "WM_PROTOCOLS"),
  3054.         XA_ATOM, 32, PropModeReplace, (unsigned char *) arrayPtr,
  3055.         atomPtr-arrayPtr);
  3056.     ckfree((char *) arrayPtr);
  3057. }
  3058.  
  3059. /*
  3060.  *----------------------------------------------------------------------
  3061.  *
  3062.  * TkWmProtocolEventProc --
  3063.  *
  3064.  *    This procedure is called by the Tk_HandleEvent whenever a
  3065.  *    ClientMessage event arrives whose type is "WM_PROTOCOLS".
  3066.  *    This procedure handles the message from the window manager
  3067.  *    in an appropriate fashion.
  3068.  *
  3069.  * Results:
  3070.  *    None.
  3071.  *
  3072.  * Side effects:
  3073.  *    Depends on what sort of handler, if any, was set up for the
  3074.  *    protocol.
  3075.  *
  3076.  *----------------------------------------------------------------------
  3077.  */
  3078.  
  3079. void
  3080. TkWmProtocolEventProc(winPtr, eventPtr)
  3081.     TkWindow *winPtr;        /* Window to which the event was sent. */
  3082.     XEvent *eventPtr;        /* X event. */
  3083. {
  3084.     WmInfo *wmPtr;
  3085.     register ProtocolHandler *protPtr;
  3086.     Atom protocol;
  3087.     int result;
  3088.  
  3089.     wmPtr = winPtr->wmInfoPtr;
  3090.     if (wmPtr == NULL) {
  3091.     return;
  3092.     }
  3093.     protocol = (Atom) eventPtr->xclient.data.l[0];
  3094.     for (protPtr = wmPtr->protPtr; protPtr != NULL;
  3095.         protPtr = protPtr->nextPtr) {
  3096.     if (protocol == protPtr->protocol) {
  3097.         Tk_Preserve((ClientData) protPtr);
  3098.         result = Tcl_GlobalEval(protPtr->interp, protPtr->command);
  3099.         if (result != TCL_OK) {
  3100.         Tcl_AddErrorInfo(protPtr->interp, "\n    (command for \"");
  3101.         Tcl_AddErrorInfo(protPtr->interp,
  3102.             Tk_GetAtomName((Tk_Window) winPtr, protocol));
  3103.         Tcl_AddErrorInfo(protPtr->interp,
  3104.             "\" window manager protocol)");
  3105.         Tk_BackgroundError(protPtr->interp);
  3106.         }
  3107.         Tk_Release((ClientData) protPtr);
  3108.         return;
  3109.     }
  3110.     }
  3111.  
  3112.     /*
  3113.      * No handler was present for this protocol.  If this is a
  3114.      * WM_DELETE_WINDOW message then just destroy the window.
  3115.      */
  3116.  
  3117.     if (protocol == Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW")) {
  3118.     Tk_DestroyWindow((Tk_Window) winPtr);
  3119.     }
  3120. }
  3121.  
  3122. /*
  3123.  *----------------------------------------------------------------------
  3124.  *
  3125.  * TkWmRestackToplevel --
  3126.  *
  3127.  *    This procedure restacks a top-level window.
  3128.  *
  3129.  * Results:
  3130.  *    None.
  3131.  *
  3132.  * Side effects:
  3133.  *    WinPtr gets restacked  as specified by aboveBelow and otherPtr.
  3134.  *    This procedure doesn't return until the restack has taken
  3135.  *    effect and the ConfigureNotify event for it has been received.
  3136.  *
  3137.  *----------------------------------------------------------------------
  3138.  */
  3139.  
  3140. void
  3141. TkWmRestackToplevel(winPtr, aboveBelow, otherPtr)
  3142.     TkWindow *winPtr;        /* Window to restack. */
  3143.     int aboveBelow;        /* Gives relative position for restacking;
  3144.                  * must be Above or Below. */
  3145.     TkWindow *otherPtr;        /* Window relative to which to restack;
  3146.                  * if NULL, then winPtr gets restacked
  3147.                  * above or below *all* siblings. */
  3148. {
  3149.     XWindowChanges changes;
  3150.     unsigned int mask;
  3151.     Window window, dummy1, dummy2, vRoot;
  3152.     Window *children;
  3153.     unsigned int numChildren;
  3154.     int i;
  3155.     unsigned long serial;
  3156.     XEvent event;
  3157.     int diff;
  3158.  
  3159.     changes.stack_mode = aboveBelow;
  3160.     mask = CWStackMode;
  3161.     if (winPtr->window == None) {
  3162.     Tk_MakeWindowExist((Tk_Window) winPtr);
  3163.     }
  3164.     window = (winPtr->wmInfoPtr->reparent != NULL)
  3165.         ? winPtr->wmInfoPtr->reparent : winPtr->window;
  3166.     if (otherPtr != NULL) {
  3167.     if (otherPtr->window == None) {
  3168.         Tk_MakeWindowExist((Tk_Window) otherPtr);
  3169.     }
  3170.     changes.sibling = (otherPtr->wmInfoPtr->reparent != NULL)
  3171.         ? otherPtr->wmInfoPtr->reparent : otherPtr->window;
  3172.     mask = CWStackMode|CWSibling;
  3173.     }
  3174.  
  3175.     /*
  3176.      * Before actually reconfiguring the window, see if it's already
  3177.      * in the right place.  If so then don't reconfigure it.  The
  3178.      * reason for this extra work is that some window managers will
  3179.      * ignore the reconfigure request if the window is already in
  3180.      * the right place, causing a long delay in WaitForConfigureNotify
  3181.      * while it times out.
  3182.      */
  3183.  
  3184.     vRoot = winPtr->wmInfoPtr->vRoot;
  3185.     if (vRoot == None) {
  3186.     vRoot = RootWindowOfScreen(Tk_Screen((Tk_Window) winPtr));
  3187.     }
  3188.     if (XQueryTree(winPtr->display, vRoot, &dummy1, &dummy2,
  3189.         &children, &numChildren) != 0) {
  3190.     if (mask & CWSibling) {
  3191.         if (aboveBelow == Above) {
  3192.         for (i = 1; i < numChildren; i++) {
  3193.             if ((children[i] == window)
  3194.                 && (children[i-1] == changes.sibling)) {
  3195.             alreadyOK:
  3196.             XFree((char *) children);
  3197.             return;
  3198.             }
  3199.         }
  3200.         } else {
  3201.         for (i = 1; i < numChildren; i++) {
  3202.             if ((children[i] == changes.sibling)
  3203.                 && (children[i-1] == window)) {
  3204.             goto alreadyOK;
  3205.             }
  3206.         }
  3207.         }
  3208.     } else {
  3209.         if (aboveBelow == Above) {
  3210.         if (window == children[numChildren-1]) {
  3211.             goto alreadyOK;
  3212.         }
  3213.         } else {
  3214.         if (window == children[0]) {
  3215.             goto alreadyOK;
  3216.         }
  3217.         }
  3218.     }
  3219.     XFree((char *) children);
  3220.     }
  3221.  
  3222.     /*
  3223.      * Reconfigure the window and wait for the reconfiguration to be
  3224.      * complete.  If we don't wait, then the window may not restack
  3225.      * for a while and the application might observe it before it has
  3226.      * restacked.  Waiting for the reconfiguration is tricky if winPtr
  3227.      * has been reparented, since the window getting the event isn't
  3228.      * one that Tk owns.
  3229.      */
  3230.  
  3231.     serial = NextRequest(winPtr->display);
  3232.     XConfigureWindow(winPtr->display, window, mask, &changes);
  3233.     if (window == winPtr->window) {
  3234.     WaitForConfigureNotify(winPtr, serial);
  3235.     } else {
  3236.     XSelectInput(winPtr->display, window, StructureNotifyMask);
  3237.     while (1) {
  3238.         if (WaitForEvent(winPtr->display, window, StructureNotifyMask,
  3239.             &event) != TCL_OK) {
  3240.         break;
  3241.         }
  3242.         if ((event.type == ConfigureNotify)
  3243.             && (event.xconfigure.window == window)) {
  3244.         diff = event.xconfigure.serial - serial;
  3245.         if (diff >= 0) {
  3246.             break;
  3247.         }
  3248.         }
  3249.     }
  3250.     XSelectInput(winPtr->display, window, (long) 0);
  3251.     }
  3252. }
  3253.