/* * tkWm.c -- * * This module takes care of the interactions between a Tk-based * application and the window manager. Among other things, it * implements the "wm" command and passes geometry information * to the window manager. * * Copyright 1991 Regents of the University of California. * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without * fee is hereby granted, provided that the above copyright * notice appear in all copies. The University of California * makes no representations about the suitability of this * software for any purpose. It is provided "as is" without * express or implied warranty. */ #ifndef lint static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkWm.c,v 1.32 92/08/21 16:26:31 ouster Exp $ SPRITE (Berkeley)"; #endif #include "tkConfig.h" #include "tkInt.h" /* * The definitions below compensate for the lack of some definitions * under X11R3. */ #ifdef X11R3 #define PBaseSize (1L<<8) #endif /* * A data structure of the following type holds window-manager-related * information for each top-level window in an application. */ typedef struct TkWmInfo { TkWindow *winPtr; /* Pointer to main Tk information for * this window. */ Window reparent; /* If the window has been reparented, this * gives the ID of the ancestor of the window * that is a child of the root window (may * not be window's immediate parent). If * the window isn't reparented, this has the * value None. */ Tk_Uid titleUid; /* Title to display in window caption. If * NULL, use name of widget. */ Tk_Uid iconName; /* Name to display in icon. */ Window master; /* Master window for TRANSIENT_FOR property, * or None. */ XWMHints hints; /* Various pieces of information for * window manager. */ Tk_Uid leaderName; /* Path name of leader of window group * (corresponds to hints.window_group). * Note: this field doesn't get updated * if leader is destroyed. */ Tk_Uid iconWindowName; /* Path name of window specified as icon * window for this window, or NULL. Note: * this field doesn't get updated if * iconWindowName is destroyed. */ Tk_Uid masterWindowName; /* Path name of window specified as master * in "wm transient" command, or NULL. * Note: this field doesn't get updated if * masterWindowName is destroyed. */ /* * Information used to construct an XSizeHints structure for * the window manager: */ int sizeHintsFlags; /* Flags word for XSizeHints structure. * If the PBaseSize flag is set then the * window is gridded; otherwise it isn't * gridded. */ int minWidth, minHeight; /* Minimum dimensions of window, in * grid units, not pixels. */ int maxWidth, maxHeight; /* Maximum dimensions of window, in * grid units, not pixels. */ int widthInc, heightInc; /* Increments for size changes (# pixels * per step). */ struct { int x; /* numerator */ int y; /* denominator */ } minAspect, maxAspect; /* Min/max aspect ratios for window. */ int reqGridWidth, reqGridHeight; /* The dimensions of the window (in * grid units) requested through * the geometry manager. */ int gravity; /* Desired window gravity. */ /* * Information used to manage the size and location of a window. */ int prevReqWidth, prevReqHeight; /* Last known size preferences, as specified * to Tk_GeometryRequest. Used to tell when * the preferred dimensions have changed. */ int width, height; /* Desired dimensions of window, specified * in grid units. These values are * set by the "wm geometry" command and by * ConfigureNotify events (for when wm * resizes window). -1 means user hasn't * requested dimensions. */ int x, y; /* Desired X and Y coordinates for window. * These values are set by "wm geometry", * plus by ConfigureNotify events (when wm * moves window). These numbers are * different than the numbers stored in * winPtr->changes because (a) they could be * measured from the right or bottom edge * of the screen (see WM_NEGATIVE_X and * WM_NEGATIVE_Y flags) and (b) if the window * has been reparented then they refer to the * parent rather than the window itself. */ int parentWidth, parentHeight; /* Width and height of reparent, in pixels * *including border*. If window hasn't been * reparented then these will be the outer * dimensions of the window, including * border. */ int xInParent, yInParent; /* Offset of window within reparent, measured * from upper-left outer corner of parent's * border. If not reparented then these are * zero. */ unsigned long configRequest;/* Serial number of last request that we * issued to change geometry of window. * Used to discard configure events that * we know will be superceded. */ int configWidth, configHeight; /* Dimensions passed to last request that we * issued to change geometry of window. Used * to eliminate redundant resize operations. */ int flags; /* Miscellaneous flags, defined below. */ struct TkWmInfo *nextPtr; /* Next in list of all top-level windows. */ } WmInfo; /* * Flag values for WmInfo structures: * * WM_NEVER_MAPPED - non-zero means window has never been * mapped; need to update all info when * window is first mapped. * WM_UPDATE_PENDING - non-zero means a call to UpdateGeometryInfo * has already been scheduled for this * window; no need to schedule another one. * WM_NEGATIVE_X - non-zero means x-coordinate is measured in * pixels from right edge of screen, rather * than from left edge. * WM_NEGATIVE_Y - non-zero means y-coordinate is measured in * pixels up from bottom of screen, rather than * down from top. * WM_UPDATE_SIZE_HINTS - non-zero means that new size hints need to be * propagated to window manager. * WM_NESTED_REPARENT - non-zero means that the window has been * reparented several levels deep in a hierarchy * (i.e. reparent isn't the window's immediate * parent). * WM_CONFIG_PENDING - non-zero means we've asked for the top-level * window to be resized but haven't seen a * ConfigureNotify event to indicate that the * resize occurred. * WM_CONFIG_AGAIN - non-zero means we need to reconfigure the * window again as soon as the current configure * request has been processed by the window * manager. */ #define WM_NEVER_MAPPED 1 #define WM_UPDATE_PENDING 2 #define WM_NEGATIVE_X 4 #define WM_NEGATIVE_Y 8 #define WM_UPDATE_SIZE_HINTS 0x10 #define WM_NESTED_REPARENT 0x20 #define WM_CONFIG_PENDING 0x40 #define WM_CONFIG_AGAIN 0x100 /* * This module keeps a list of all top-level windows, primarily to * simplify the job of Tk_CoordsToWindow. */ static WmInfo *firstWmPtr = NULL; /* Points to first top-level window. */ #define IS_GRIDDED(wmPtr) ((wmPtr)->sizeHintsFlags & PBaseSize) /* * Forward declarations for procedures defined in this file: */ static int ParseGeometry _ANSI_ARGS_ ((Tcl_Interp *interp, char *string, TkWindow *winPtr)); static void TopLevelEventProc _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr)); static void TopLevelReqProc _ANSI_ARGS_((ClientData dummy, Tk_Window tkwin)); static void UpdateGeometryInfo _ANSI_ARGS_(( ClientData clientData)); static void UpdateHints _ANSI_ARGS_((TkWindow *winPtr)); static void UpdateSizeHints _ANSI_ARGS_((TkWindow *winPtr)); /* *-------------------------------------------------------------- * * TkWmNewWindow -- * * This procedure is invoked whenever a new top-level * window is created. Its job is to initialize the WmInfo * structure for the window. * * Results: * None. * * Side effects: * A WmInfo structure gets allocated and initialized. * *-------------------------------------------------------------- */ void TkWmNewWindow(winPtr) TkWindow *winPtr; /* Newly-created top-level window. */ { register WmInfo *wmPtr; wmPtr = (WmInfo *) ckalloc(sizeof(WmInfo)); wmPtr->winPtr = winPtr; wmPtr->reparent = None; wmPtr->titleUid = NULL; wmPtr->iconName = NULL; wmPtr->master = None; wmPtr->hints.flags = InputHint | StateHint; wmPtr->hints.input = True; wmPtr->hints.initial_state = NormalState; wmPtr->hints.icon_pixmap = None; wmPtr->hints.icon_window = None; wmPtr->hints.icon_x = wmPtr->hints.icon_y = 0; wmPtr->hints.icon_mask = None; wmPtr->hints.window_group = None; wmPtr->leaderName = NULL; wmPtr->iconWindowName = NULL; wmPtr->masterWindowName = NULL; wmPtr->sizeHintsFlags = 0; wmPtr->minWidth = wmPtr->minHeight = 0; wmPtr->maxWidth = wmPtr->maxHeight = 10000; wmPtr->widthInc = wmPtr->heightInc = 1; wmPtr->minAspect.x = wmPtr->minAspect.y = 1; wmPtr->maxAspect.x = wmPtr->maxAspect.y = 1; wmPtr->reqGridWidth = wmPtr->reqGridHeight = -1; wmPtr->prevReqWidth = wmPtr->prevReqHeight = -1; wmPtr->gravity = NorthWestGravity; wmPtr->width = -1; wmPtr->height = -1; wmPtr->x = winPtr->changes.x; wmPtr->y = winPtr->changes.y; wmPtr->parentWidth = winPtr->changes.width + 2*winPtr->changes.border_width; wmPtr->parentHeight = winPtr->changes.height + 2*winPtr->changes.border_width; wmPtr->xInParent = wmPtr->yInParent = 0; wmPtr->configRequest = 0; wmPtr->configWidth = -1; wmPtr->configHeight = -1; wmPtr->flags = WM_NEVER_MAPPED; wmPtr->nextPtr = firstWmPtr; firstWmPtr = wmPtr; winPtr->wmInfoPtr = wmPtr; /* * Tk must monitor certain events for top-level windows: * (a) structure events, in order to detect size and position changes * caused by window managers. * (b) enter/level events, in order to perform focussing correctly. */ Tk_CreateEventHandler((Tk_Window) winPtr, StructureNotifyMask|EnterWindowMask|LeaveWindowMask, TopLevelEventProc, (ClientData) winPtr); /* * Arrange for geometry requests to be reflected from the window * to the window manager. */ Tk_ManageGeometry((Tk_Window) winPtr, TopLevelReqProc, (ClientData) 0); } /* *-------------------------------------------------------------- * * TkWmMapWindow -- * * This procedure is invoked just before a top-level window * is mapped. It gives this module a chance to update all * window-manager-related information in properties before * the window manager sees the map event and checks the * properties. * * Results: * Returns non-zero if it's OK for the window to be mapped, 0 * if the caller shouldn't map the window after all (e.g. because * it has been withdrawn). * * Side effects: * Properties of winPtr may get updated to provide up-to-date * information to the window manager. * *-------------------------------------------------------------- */ int TkWmMapWindow(winPtr) TkWindow *winPtr; /* Top-level window that's about to * be mapped. */ { register WmInfo *wmPtr = winPtr->wmInfoPtr; #ifndef X11R3 XTextProperty textProp; #endif /* * Set the MAPPED flag if the window is going to appear in its normal * state: if it's going to be iconified or withdrawn then it won't * ever be mapped. */ if (wmPtr->hints.initial_state == NormalState) { winPtr->flags |= TK_MAPPED; } if (!(wmPtr->flags & WM_NEVER_MAPPED)) { return 1; } wmPtr->flags &= ~WM_NEVER_MAPPED; /* * This is the first time this window has ever been mapped. * Store all the window-manager-related information for the * window. */ #ifndef X11R3 if (wmPtr->titleUid == NULL) { wmPtr->titleUid = winPtr->nameUid; } if (XStringListToTextProperty(&wmPtr->titleUid, 1, &textProp) != 0) { XSetWMName(winPtr->display, winPtr->window, &textProp); XFree((char *) textProp.value); } #endif TkWmSetClass(winPtr); if (wmPtr->iconName != NULL) { XSetIconName(winPtr->display, winPtr->window, wmPtr->iconName); } if (wmPtr->master != None) { XSetTransientForHint(winPtr->display, winPtr->window, wmPtr->master); } wmPtr->flags |= WM_UPDATE_SIZE_HINTS; UpdateGeometryInfo((ClientData) winPtr); UpdateHints(winPtr); if (wmPtr->hints.initial_state == WithdrawnState) { return 0; } return 1; } /* *-------------------------------------------------------------- * * TkWmDeadWindow -- * * This procedure is invoked when a top-level window is * about to be deleted. It cleans up the wm-related data * structures for the window. * * Results: * None. * * Side effects: * The WmInfo structure for winPtr gets freed up. * *-------------------------------------------------------------- */ void TkWmDeadWindow(winPtr) TkWindow *winPtr; /* Newly-created top-level window. */ { register WmInfo *wmPtr = winPtr->wmInfoPtr; if (wmPtr == NULL) { return; } if (firstWmPtr == wmPtr) { firstWmPtr = wmPtr->nextPtr; } else { register WmInfo *prevPtr; for (prevPtr = firstWmPtr; ; prevPtr = prevPtr->nextPtr) { if (prevPtr == NULL) { panic("couldn't unlink window in TkWmDeadWindow"); } if (prevPtr->nextPtr == wmPtr) { prevPtr->nextPtr = wmPtr->nextPtr; break; } } } if (wmPtr->hints.flags & IconPixmapHint) { Tk_FreeBitmap(wmPtr->hints.icon_pixmap); } if (wmPtr->hints.flags & IconMaskHint) { Tk_FreeBitmap(wmPtr->hints.icon_mask); } if (wmPtr->flags & WM_UPDATE_PENDING) { Tk_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr); } ckfree((char *) wmPtr); winPtr->wmInfoPtr = NULL; } /* *-------------------------------------------------------------- * * TkWmSetClass -- * * This procedure is invoked whenever a top-level window's * class is changed. If the window has been mapped then this * procedure updates the window manager property for the * class. If the window hasn't been mapped, the update is * deferred until just before the first mapping. * * Results: * None. * * Side effects: * A window property may get updated. * *-------------------------------------------------------------- */ void TkWmSetClass(winPtr) TkWindow *winPtr; /* Newly-created top-level window. */ { if (winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) { return; } #ifndef X11R3 if (winPtr->classUid != NULL) { XClassHint *classPtr; classPtr = XAllocClassHint(); classPtr->res_name = winPtr->nameUid; classPtr->res_class = winPtr->classUid; XSetClassHint(winPtr->display, winPtr->window, classPtr); XFree((char *) classPtr); } #endif } /* *---------------------------------------------------------------------- * * Tk_WmCmd -- * * This procedure is invoked to process the "wm" Tcl command. * See the user documentation for details on what it does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * *---------------------------------------------------------------------- */ /* ARGSUSED */ int Tk_WmCmd(clientData, interp, argc, argv) ClientData clientData; /* Main window associated with * interpreter. */ Tcl_Interp *interp; /* Current interpreter. */ int argc; /* Number of arguments. */ char **argv; /* Argument strings. */ { Tk_Window tkwin = (Tk_Window) clientData; TkWindow *winPtr; register WmInfo *wmPtr; char c; int length; if (argc < 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " option window ?arg ...?\"", (char *) NULL); return TCL_ERROR; } winPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin); if (winPtr == NULL) { return TCL_ERROR; } if (!(winPtr->flags & TK_TOP_LEVEL)) { Tcl_AppendResult(interp, "window \"", winPtr->pathName, "\" isn't a top-level window", (char *) NULL); return TCL_ERROR; } wmPtr = winPtr->wmInfoPtr; c = argv[1][0]; length = strlen(argv[1]); if ((c == 'a') && (strncmp(argv[1], "aspect", length) == 0)) { int numer1, denom1, numer2, denom2; if ((argc != 3) && (argc != 7)) { Tcl_AppendResult(interp, "wrong # arguments: must be \"", argv[0], " aspect window ?minNumer minDenom ", "maxNumer maxDenom?\"", (char *) NULL); return TCL_ERROR; } if (argc == 3) { if (wmPtr->sizeHintsFlags & PAspect) { sprintf(interp->result, "%d %d %d %d", wmPtr->minAspect.x, wmPtr->minAspect.y, wmPtr->maxAspect.x, wmPtr->maxAspect.y); } return TCL_OK; } if (*argv[3] == '\0') { wmPtr->sizeHintsFlags &= ~PAspect; } else { if ((Tcl_GetInt(interp, argv[3], &numer1) != TCL_OK) || (Tcl_GetInt(interp, argv[4], &denom1) != TCL_OK) || (Tcl_GetInt(interp, argv[5], &numer2) != TCL_OK) || (Tcl_GetInt(interp, argv[6], &denom2) != TCL_OK)) { return TCL_ERROR; } if ((numer1 <= 0) || (denom1 <= 0) || (numer2 <= 0) || (denom2 <= 0)) { interp->result = "aspect number can't be <= 0"; return TCL_ERROR; } wmPtr->minAspect.x = numer1; wmPtr->minAspect.y = denom1; wmPtr->maxAspect.x = numer2; wmPtr->maxAspect.y = denom2; wmPtr->sizeHintsFlags |= PAspect; } wmPtr->flags |= WM_UPDATE_SIZE_HINTS; goto updateGeom; } else if ((c == 'd') && (strncmp(argv[1], "deiconify", length) == 0)) { if (argc != 3) { Tcl_AppendResult(interp, "wrong # arguments: must be \"", argv[0], " deiconify window\"", (char *) NULL); return TCL_ERROR; } wmPtr->hints.initial_state = NormalState; if (wmPtr->flags & WM_NEVER_MAPPED) { return TCL_OK; } Tk_MapWindow((Tk_Window) winPtr); } else if ((c == 'f') && (strncmp(argv[1], "focusmodel", length) == 0)) { if ((argc != 3) && (argc != 4)) { Tcl_AppendResult(interp, "wrong # arguments: must be \"", argv[0], " focusmodel window ?active|passive?\"", (char *) NULL); return TCL_ERROR; } if (argc == 3) { interp->result = wmPtr->hints.input ? "passive" : "active"; return TCL_OK; } c = argv[3][0]; length = strlen(argv[3]); if ((c == 'a') && (strncmp(argv[3], "active", length) == 0)) { wmPtr->hints.input = False; } else if ((c == 'p') && (strncmp(argv[3], "passive", length) == 0)) { wmPtr->hints.input = True; } else { Tcl_AppendResult(interp, "bad argument \"", argv[3], "\": must be active or passive", (char *) NULL); return TCL_ERROR; } UpdateHints(winPtr); } else if ((c == 'g') && (strncmp(argv[1], "geometry", length) == 0) && (length >= 2)) { char xSign, ySign; int width, height; if ((argc != 3) && (argc != 4)) { Tcl_AppendResult(interp, "wrong # arguments: must be \"", argv[0], " geometry window ?newGeometry?\"", (char *) NULL); return TCL_ERROR; } if (argc == 3) { xSign = (wmPtr->flags & WM_NEGATIVE_X) ? '-' : '+'; ySign = (wmPtr->flags & WM_NEGATIVE_Y) ? '-' : '+'; if (wmPtr->width != -1) { width = wmPtr->width; height = wmPtr->height; } else if (IS_GRIDDED(wmPtr)) { width = wmPtr->reqGridWidth; height = wmPtr->reqGridHeight; } else { width = winPtr->reqWidth; height = winPtr->reqHeight; } sprintf(interp->result, "%dx%d%c%d%c%d", width, height, xSign, wmPtr->x, ySign, wmPtr->y); return TCL_OK; } if (*argv[3] == '\0') { wmPtr->width = -1; wmPtr->height = -1; goto updateGeom; } return ParseGeometry(interp, argv[3], winPtr); } else if ((c == 'g') && (strncmp(argv[1], "grid", length) == 0) && (length >= 3)) { int reqWidth, reqHeight, widthInc, heightInc; if ((argc != 3) && (argc != 7)) { Tcl_AppendResult(interp, "wrong # arguments: must be \"", argv[0], " reqsize window ?baseWidth baseHeight ", "widthInc heightInc?\"", (char *) NULL); return TCL_ERROR; } if (argc == 3) { if (wmPtr->sizeHintsFlags & PBaseSize) { sprintf(interp->result, "%d %d %d %d", wmPtr->reqGridWidth, wmPtr->reqGridHeight, wmPtr->widthInc, wmPtr->heightInc); } return TCL_OK; } if (*argv[3] == '\0') { /* * Turn off gridding and reset the width and height * to make sense as ungridded numbers. */ wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc); wmPtr->widthInc = 1; wmPtr->heightInc = 1; if (wmPtr->width != -1) { wmPtr->width = winPtr->reqWidth + (wmPtr->width - wmPtr->reqGridWidth)*wmPtr->widthInc; wmPtr->height = winPtr->reqHeight + (wmPtr->height - wmPtr->reqGridHeight)*wmPtr->heightInc; } } else { if ((Tcl_GetInt(interp, argv[3], &reqWidth) != TCL_OK) || (Tcl_GetInt(interp, argv[4], &reqHeight) != TCL_OK) || (Tcl_GetInt(interp, argv[5], &widthInc) != TCL_OK) || (Tcl_GetInt(interp, argv[6], &heightInc) != TCL_OK)) { return TCL_ERROR; } if (reqWidth < 0) { interp->result = "baseWidth can't be < 0"; return TCL_ERROR; } if (reqHeight < 0) { interp->result = "baseHeight can't be < 0"; return TCL_ERROR; } if (widthInc < 0) { interp->result = "widthInc can't be < 0"; return TCL_ERROR; } if (heightInc < 0) { interp->result = "heightInc can't be < 0"; return TCL_ERROR; } Tk_SetGrid((Tk_Window) tkwin, reqWidth, reqHeight, widthInc, heightInc); } wmPtr->flags |= WM_UPDATE_SIZE_HINTS; goto updateGeom; } else if ((c == 'g') && (strncmp(argv[1], "group", length) == 0) && (length >= 3)) { Tk_Window tkwin2; if ((argc != 3) && (argc != 4)) { Tcl_AppendResult(interp, "wrong # arguments: must be \"", argv[0], " group window ?pathName?\"", (char *) NULL); return TCL_ERROR; } if (argc == 3) { if (wmPtr->hints.flags & WindowGroupHint) { interp->result = wmPtr->leaderName; } return TCL_OK; } if (*argv[3] == '\0') { wmPtr->hints.flags &= ~WindowGroupHint; wmPtr->leaderName = NULL; } else { tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin); if (tkwin2 == NULL) { return TCL_ERROR; } Tk_MakeWindowExist(tkwin2); wmPtr->hints.window_group = Tk_WindowId(tkwin2); wmPtr->hints.flags |= WindowGroupHint; wmPtr->leaderName = Tk_PathName(tkwin2); } UpdateHints(winPtr); } else if ((c == 'i') && (strncmp(argv[1], "iconbitmap", length) == 0) && (length >= 5)) { Pixmap pixmap; if ((argc != 3) && (argc != 4)) { Tcl_AppendResult(interp, "wrong # arguments: must be \"", argv[0], " iconbitmap window ?bitmap?\"", (char *) NULL); return TCL_ERROR; } if (argc == 3) { if (wmPtr->hints.flags & IconPixmapHint) { interp->result = Tk_NameOfBitmap(wmPtr->hints.icon_pixmap); } return TCL_OK; } if (*argv[3] == '\0') { if (wmPtr->hints.icon_pixmap != None) { Tk_FreeBitmap(wmPtr->hints.icon_pixmap); } wmPtr->hints.flags &= ~IconPixmapHint; } else { pixmap = Tk_GetBitmap(interp, tkwin, Tk_GetUid(argv[3])); if (pixmap == None) { return TCL_ERROR; } wmPtr->hints.icon_pixmap = pixmap; wmPtr->hints.flags |= IconPixmapHint; } UpdateHints(winPtr); } else if ((c == 'i') && (strncmp(argv[1], "iconify", length) == 0) && (length >= 5)) { if (argc != 3) { Tcl_AppendResult(interp, "wrong # arguments: must be \"", argv[0], " iconify window\"", (char *) NULL); return TCL_ERROR; } wmPtr->hints.initial_state = IconicState; if (wmPtr->flags & WM_NEVER_MAPPED) { return TCL_OK; } #ifndef X11R3 if (XIconifyWindow(winPtr->display, winPtr->window, winPtr->screenNum) == 0) { interp->result = "couldn't send iconify message to window manager"; return TCL_ERROR; } #else interp->result = "can't iconify under X11R3"; return TCL_ERROR; #endif } else if ((c == 'i') && (strncmp(argv[1], "iconmask", length) == 0) && (length >= 5)) { Pixmap pixmap; if ((argc != 3) && (argc != 4)) { Tcl_AppendResult(interp, "wrong # arguments: must be \"", argv[0], " iconmask window ?bitmap?\"", (char *) NULL); return TCL_ERROR; } if (argc == 3) { if (wmPtr->hints.flags & IconMaskHint) { interp->result = Tk_NameOfBitmap(wmPtr->hints.icon_mask); } return TCL_OK; } if (*argv[3] == '\0') { if (wmPtr->hints.icon_mask != None) { Tk_FreeBitmap(wmPtr->hints.icon_mask); } wmPtr->hints.flags &= ~IconMaskHint; } else { pixmap = Tk_GetBitmap(interp, tkwin, Tk_GetUid(argv[3])); if (pixmap == None) { return TCL_ERROR; } wmPtr->hints.icon_mask = pixmap; wmPtr->hints.flags |= IconMaskHint; } UpdateHints(winPtr); } else if ((c == 'i') && (strncmp(argv[1], "iconname", length) == 0) && (length >= 5)) { if (argc > 4) { Tcl_AppendResult(interp, "wrong # arguments: must be \"", argv[0], " iconname window ?newName?\"", (char *) NULL); return TCL_ERROR; } if (argc == 3) { interp->result = (wmPtr->iconName != NULL) ? wmPtr->iconName : ""; return TCL_OK; } else { wmPtr->iconName = Tk_GetUid(argv[3]); if (!(wmPtr->flags & WM_NEVER_MAPPED)) { XSetIconName(winPtr->display, winPtr->window, wmPtr->iconName); } } } else if ((c == 'i') && (strncmp(argv[1], "iconposition", length) == 0) && (length >= 5)) { int x, y; if ((argc != 3) && (argc != 5)) { Tcl_AppendResult(interp, "wrong # arguments: must be \"", argv[0], " iconposition window ?x y?\"", (char *) NULL); return TCL_ERROR; } if (argc == 3) { if (wmPtr->hints.flags & IconPositionHint) { sprintf(interp->result, "%d %d", wmPtr->hints.icon_x, wmPtr->hints.icon_y); } return TCL_OK; } if (*argv[3] == '\0') { wmPtr->hints.flags &= ~IconPositionHint; } else { if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK) || (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)){ return TCL_ERROR; } wmPtr->hints.icon_x = x; wmPtr->hints.icon_y = y; wmPtr->hints.flags |= IconPositionHint; } UpdateHints(winPtr); } else if ((c == 'i') && (strncmp(argv[1], "iconwindow", length) == 0) && (length >= 5)) { Tk_Window tkwin2; if ((argc != 3) && (argc != 4)) { Tcl_AppendResult(interp, "wrong # arguments: must be \"", argv[0], " iconwindow window ?pathName?\"", (char *) NULL); return TCL_ERROR; } if (argc == 3) { if (wmPtr->hints.flags & IconWindowHint) { interp->result = wmPtr->iconWindowName; } return TCL_OK; } if (*argv[3] == '\0') { wmPtr->hints.flags &= ~IconWindowHint; wmPtr->iconWindowName = NULL; } else { tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin); if (tkwin2 == NULL) { return TCL_ERROR; } Tk_MakeWindowExist(tkwin2); wmPtr->hints.icon_window = Tk_WindowId(tkwin2); wmPtr->hints.flags |= IconWindowHint; wmPtr->iconWindowName = Tk_PathName(tkwin2); } UpdateHints(winPtr); } else if ((c == 'm') && (strncmp(argv[1], "maxsize", length) == 0) && (length >= 2)) { int width, height; if ((argc != 3) && (argc != 5)) { Tcl_AppendResult(interp, "wrong # arguments: must be \"", argv[0], " maxsize window ?width height?\"", (char *) NULL); return TCL_ERROR; } if (argc == 3) { if (wmPtr->sizeHintsFlags & PMaxSize) { sprintf(interp->result, "%d %d", wmPtr->maxWidth, wmPtr->maxHeight); } return TCL_OK; } if (*argv[3] == '\0') { wmPtr->sizeHintsFlags &= ~PMaxSize; } else { if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK) || (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) { return TCL_ERROR; } wmPtr->maxWidth = width; wmPtr->maxHeight = height; wmPtr->sizeHintsFlags |= PMaxSize; } wmPtr->flags |= WM_UPDATE_SIZE_HINTS; goto updateGeom; } else if ((c == 'm') && (strncmp(argv[1], "minsize", length) == 0) && (length >= 2)) { int width, height; if ((argc != 3) && (argc != 5)) { Tcl_AppendResult(interp, "wrong # arguments: must be \"", argv[0], " minsize window ?width height?\"", (char *) NULL); return TCL_ERROR; } if (argc == 3) { if (wmPtr->sizeHintsFlags & PMinSize) { sprintf(interp->result, "%d %d", wmPtr->minWidth, wmPtr->minHeight); } return TCL_OK; } if (*argv[3] == '\0') { wmPtr->sizeHintsFlags &= ~PMinSize; } else { if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK) || (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) { return TCL_ERROR; } wmPtr->minWidth = width; wmPtr->minHeight = height; wmPtr->sizeHintsFlags |= PMinSize; } wmPtr->flags |= WM_UPDATE_SIZE_HINTS; goto updateGeom; } else if ((c == 'p') && (strncmp(argv[1], "positionfrom", length) == 0)) { if ((argc != 3) && (argc != 4)) { Tcl_AppendResult(interp, "wrong # arguments: must be \"", argv[0], " positionfrom window ?user/program?\"", (char *) NULL); return TCL_ERROR; } if (argc == 3) { if (wmPtr->sizeHintsFlags & USPosition) { interp->result = "user"; } else if (wmPtr->sizeHintsFlags & PPosition) { interp->result = "program"; } return TCL_OK; } if (*argv[3] == '\0') { wmPtr->sizeHintsFlags &= ~(USPosition|PPosition); } else { c = argv[3][0]; length = strlen(argv[3]); if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) { wmPtr->sizeHintsFlags &= ~PPosition; wmPtr->sizeHintsFlags |= USPosition; } else if ((c == 'p') && (strncmp(argv[3], "program", length) == 0)) { wmPtr->sizeHintsFlags &= ~USPosition; wmPtr->sizeHintsFlags |= PPosition; } else { Tcl_AppendResult(interp, "bad argument \"", argv[3], "\": must be program or user", (char *) NULL); return TCL_ERROR; } } wmPtr->flags |= WM_UPDATE_SIZE_HINTS; goto updateGeom; } else if ((c == 's') && (strncmp(argv[1], "sizefrom", length) == 0)) { if ((argc != 3) && (argc != 4)) { Tcl_AppendResult(interp, "wrong # arguments: must be \"", argv[0], " sizefrom window ?user|program?\"", (char *) NULL); return TCL_ERROR; } if (argc == 3) { if (wmPtr->sizeHintsFlags & USSize) { interp->result = "user"; } else if (wmPtr->sizeHintsFlags & PSize) { interp->result = "program"; } return TCL_OK; } if (*argv[3] == '\0') { wmPtr->sizeHintsFlags &= ~(USSize|PSize); } else { c = argv[3][0]; length = strlen(argv[3]); if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) { wmPtr->sizeHintsFlags &= ~PSize; wmPtr->sizeHintsFlags |= USSize; } else if ((c == 'p') && (strncmp(argv[3], "program", length) == 0)) { wmPtr->sizeHintsFlags &= ~USSize; wmPtr->sizeHintsFlags |= PSize; } else { Tcl_AppendResult(interp, "bad argument \"", argv[3], "\": must be program or user", (char *) NULL); return TCL_ERROR; } } wmPtr->flags |= WM_UPDATE_SIZE_HINTS; goto updateGeom; } else if ((c == 't') && (strncmp(argv[1], "title", length) == 0) && (length >= 2)) { if (argc > 4) { Tcl_AppendResult(interp, "wrong # arguments: must be \"", argv[0], " title window ?newTitle?\"", (char *) NULL); return TCL_ERROR; } if (argc == 3) { interp->result = (wmPtr->titleUid != NULL) ? wmPtr->titleUid : winPtr->nameUid; return TCL_OK; } else { wmPtr->titleUid = Tk_GetUid(argv[3]); #ifndef X11R3 if (!(wmPtr->flags & WM_NEVER_MAPPED)) { XTextProperty textProp; if (XStringListToTextProperty(&wmPtr->titleUid, 1, &textProp) != 0) { XSetWMName(winPtr->display, winPtr->window, &textProp); XFree((char *) textProp.value); } } #endif } #ifndef X11R3 } else if ((c == 't') && (strncmp(argv[1], "transient", length) == 0) && (length >= 2)) { Tk_Window master; if ((argc != 3) && (argc != 4)) { Tcl_AppendResult(interp, "wrong # arguments: must be \"", argv[0], " transient window ?master?\"", (char *) NULL); return TCL_ERROR; } if (argc == 3) { if (wmPtr->master != None) { interp->result = wmPtr->masterWindowName; } return TCL_OK; } if (argv[3][0] == '\0') { wmPtr->master = None; wmPtr->masterWindowName = NULL; } else { master = Tk_NameToWindow(interp, argv[3], tkwin); if (master == NULL) { return TCL_ERROR; } Tk_MakeWindowExist(master); wmPtr->master = Tk_WindowId(master); wmPtr->masterWindowName = Tk_PathName(master); } if (!(wmPtr->flags & WM_NEVER_MAPPED)) { XSetTransientForHint(winPtr->display, winPtr->window, wmPtr->master); } } else if ((c == 'w') && (strncmp(argv[1], "withdraw", length) == 0)) { if (argc != 3) { Tcl_AppendResult(interp, "wrong # arguments: must be \"", argv[0], " withdraw window\"", (char *) NULL); return TCL_ERROR; } wmPtr->hints.initial_state = WithdrawnState; if (wmPtr->flags & WM_NEVER_MAPPED) { return TCL_OK; } if (XWithdrawWindow(winPtr->display, winPtr->window, winPtr->screenNum) == 0) { interp->result = "couldn't send withdraw message to window manager"; return TCL_ERROR; } winPtr->flags &= ~TK_MAPPED; #endif } else { Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1], "\": must be aspect, deiconify, focusmodel, ", "geometry, grid, group, iconbitmap, ", "iconify, iconmask, iconname, iconposition, ", "iconwindow, maxsize, minsize, positionfrom, ", "sizefrom, title, transient, or withdraw", (char *) NULL); return TCL_ERROR; } return TCL_OK; updateGeom: if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) { Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr); wmPtr->flags |= WM_UPDATE_PENDING; } return TCL_OK; } /* *---------------------------------------------------------------------- * * Tk_SetGrid -- * * This procedure is invoked by a widget when it wishes to set a grid * coordinate system that controls the size of a top-level window. * It provides a C interface equivalent to the "wm grid" command and * is usually asscoiated with the -setgrid option. * * Results: * None. * * Side effects: * Grid-related information will be passed to the window manager, so * that the top-level window associated with tkwin will resize on * even grid units. * *---------------------------------------------------------------------- */ void Tk_SetGrid(tkwin, reqWidth, reqHeight, widthInc, heightInc) Tk_Window tkwin; /* Token for window. New window mgr info * will be posted for the top-level window * associated with this window. */ int reqWidth; /* Width (in grid units) corresponding to * the requested geometry for tkwin. */ int reqHeight; /* Height (in grid units) corresponding to * the requested geometry for tkwin. */ int widthInc, heightInc; /* Pixel increments corresponding to a * change of one grid unit. */ { TkWindow *winPtr = (TkWindow *) tkwin; register WmInfo *wmPtr; /* * Find the top-level window for tkwin, plus the window manager * information. */ while (!(winPtr->flags & TK_TOP_LEVEL)) { winPtr = winPtr->parentPtr; } wmPtr = winPtr->wmInfoPtr; if ((wmPtr->reqGridWidth == reqWidth) && (wmPtr->reqGridHeight != reqHeight) && (wmPtr->widthInc != widthInc) && (wmPtr->heightInc != heightInc) && ((wmPtr->sizeHintsFlags & (PBaseSize|PResizeInc)) == PBaseSize|PResizeInc)) { return; } /* * If gridding was previously off, then forget about any window * size requests made by the user or via "wm geometry": these are * in pixel units and there's no easy way to translate them to * grid units since the new requested size of the top-level window in * pixels may not yet have been registered yet (it may filter up * the hierarchy in DoWhenIdle handlers). */ if (!(wmPtr->sizeHintsFlags & PBaseSize)) { wmPtr->width = -1; wmPtr->height = -1; } /* * Set the new gridding information, and start the process of passing * all of this information to the window manager. */ wmPtr->reqGridWidth = reqWidth; wmPtr->reqGridHeight = reqHeight; wmPtr->widthInc = widthInc; wmPtr->heightInc = heightInc; wmPtr->sizeHintsFlags |= PBaseSize|PResizeInc; wmPtr->flags |= WM_UPDATE_SIZE_HINTS; if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) { Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr); wmPtr->flags |= WM_UPDATE_PENDING; } } /* *---------------------------------------------------------------------- * * TopLevelEventProc -- * * This procedure is invoked when a top-level (or other externally- * managed window) is restructured in any way. * * Results: * None. * * Side effects: * Tk's internal data structures for the window get modified to * reflect the structural change. * *---------------------------------------------------------------------- */ static void TopLevelEventProc(clientData, eventPtr) ClientData clientData; /* Window for which event occurred. */ XEvent *eventPtr; /* Event that just happened. */ { register TkWindow *winPtr = (TkWindow *) clientData; if (eventPtr->type == DestroyNotify) { if (!(winPtr->flags & TK_ALREADY_DEAD)) { Tk_DestroyWindow((Tk_Window) winPtr); } } else if (eventPtr->type == ConfigureNotify) { register WmInfo *wmPtr = winPtr->wmInfoPtr; int diff, x, y; /* * A top-level window has been reconfigured. Problem #1: * discard stale information. If the application has recently * tried to reconfigure itself, ignore all events until the * response to that reconfiguration arrives (the response is * assumed to be the first ConfigureNotify that arrives after * the server has seen the request; this suffers from potential * races with user actions, but it's the best I can think of * right now). */ diff = eventPtr->xconfigure.serial - wmPtr->configRequest; if (diff < 0) { return; } /* * Problem #2: reparenting window managers. If the window * manager reparents a top-level window then the x and y * information that comes in events for the window is wrong: * it gives the location of the window inside its decorative * parent, rather than the location of the window in root * coordinates, which is what we want. Window managers * are supposed to send synthetic events with the correct * information, but ICCCM doesn't require them to do this * under all conditions, and the information provided doesn't * include everything we need here. So, the code below * maintains a bunch of information about the parent window. * If the window hasn't been reparented, we pretend that * there is a parent shrink-wrapped around the window. */ if (wmPtr->reparent == None) { noReparent: winPtr->changes.x = eventPtr->xconfigure.x; winPtr->changes.y = eventPtr->xconfigure.y; wmPtr->parentWidth = eventPtr->xconfigure.width + 2*eventPtr->xconfigure.border_width; wmPtr->parentHeight = eventPtr->xconfigure.height + 2*eventPtr->xconfigure.border_width; } else { unsigned int width, height, bd, dummy; Window dummy2; Status status; Tk_ErrorHandler handler; handler = Tk_CreateErrorHandler(winPtr->display, BadDrawable, -1, -1, (Tk_ErrorProc *) NULL, (ClientData) NULL); status = XGetGeometry(winPtr->display, wmPtr->reparent, &dummy2, &x, &y, &width, &height, &bd, &dummy); Tk_DeleteErrorHandler(handler); if (status == 0) { /* * It appears that the reparented parent went away and * no-one told us. Reset the window to indicate that * it's not reparented, then handle it as a non-reparented * window. */ wmPtr->reparent = None; wmPtr->flags &= ~WM_NESTED_REPARENT; wmPtr->xInParent = wmPtr->yInParent = 0; goto noReparent; } wmPtr->parentWidth = width + 2*bd; wmPtr->parentHeight = height + 2*bd; winPtr->changes.x = x; winPtr->changes.y = y; if (wmPtr->flags & WM_NESTED_REPARENT) { int xOffset, yOffset; (void) XTranslateCoordinates(winPtr->display, winPtr->window, wmPtr->reparent, 0, 0, &xOffset, &yOffset, &dummy2); wmPtr->xInParent = xOffset + bd - winPtr->changes.border_width; wmPtr->yInParent = yOffset + bd - winPtr->changes.border_width; } else { if (!eventPtr->xconfigure.send_event) { wmPtr->xInParent = eventPtr->xconfigure.x + bd; wmPtr->yInParent = eventPtr->xconfigure.y + bd; } } winPtr->changes.x = x + wmPtr->xInParent; winPtr->changes.y = y + wmPtr->yInParent; } /* * Problem #3: if the window size or location was changed * externally, update the geometry information in wmPtr to make * it look just as if the user had typed a "wm geometry" command * to make the change. There are many tricky situations to deal * with: * (a) the event is simply a reflection of an internal geometry * request from the window's widgets (must leave width and * height alone in this case). * (b) the window manager might respond to a size request from * us with a different size than requested (e.g. it might * have a minimum allowable window size). Because of this, * can't just compare new size with requested size to determine * whether this event is a reflection of an internal request * from within the application. Use WM_CONFIG_PENDING flag * instead. * (c) ConfigureNotify events also arise if the window has been * moved, even if its size hasn't changed. Must distinguish * between the user moving the window and the user resizing * the window. */ if (wmPtr->flags & WM_CONFIG_PENDING) { int diff; /* * Size change is just a reflection of something coming from * application. */ diff = eventPtr->xconfigure.serial - wmPtr->configRequest; if (diff >= 0) { if (wmPtr->flags & WM_CONFIG_AGAIN) { if (!(wmPtr->flags & WM_UPDATE_PENDING)) { Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr); wmPtr->flags |= WM_UPDATE_PENDING; } } wmPtr->flags &= ~(WM_CONFIG_PENDING|WM_CONFIG_AGAIN); } } else if ((winPtr->changes.width != eventPtr->xconfigure.width) || (winPtr->changes.height != eventPtr->xconfigure.height)) { wmPtr->configWidth = -1; wmPtr->configHeight = -1; if (IS_GRIDDED(wmPtr)) { wmPtr->width = wmPtr->reqGridWidth + (eventPtr->xconfigure.width - winPtr->reqWidth)/wmPtr->widthInc; if (wmPtr->width < 0) { wmPtr->width = 0; } wmPtr->height = wmPtr->reqGridHeight + (eventPtr->xconfigure.height - winPtr->reqHeight)/wmPtr->heightInc; if (wmPtr->height < 0) { wmPtr->height = 0; } } else if ((eventPtr->xconfigure.width != winPtr->changes.width) || (eventPtr->xconfigure.height != winPtr->changes.height)) { /* * The check above is needed so we don't think the user * requested a new size when all he/she did was to move * the window. */ wmPtr->width = eventPtr->xconfigure.width; wmPtr->height = eventPtr->xconfigure.height; } } winPtr->changes.width = eventPtr->xconfigure.width; winPtr->changes.height = eventPtr->xconfigure.height; winPtr->changes.border_width = eventPtr->xconfigure.border_width; winPtr->changes.sibling = eventPtr->xconfigure.above; winPtr->changes.stack_mode = Above; x = winPtr->changes.x - wmPtr->xInParent; if (wmPtr->flags & WM_NEGATIVE_X) { x = DisplayWidth(winPtr->display, winPtr->screenNum) - (x + wmPtr->parentWidth); } y = winPtr->changes.y - wmPtr->yInParent; if (wmPtr->flags & WM_NEGATIVE_Y) { y = DisplayHeight(winPtr->display, winPtr->screenNum) - (y + wmPtr->parentHeight); } if ((x != wmPtr->x) || (y != wmPtr->y)) { wmPtr->x = x; wmPtr->y = y; } } else if (eventPtr->type == MapNotify) { winPtr->flags |= TK_MAPPED; } else if (eventPtr->type == UnmapNotify) { winPtr->flags &= ~TK_MAPPED; } else if (eventPtr->type == ReparentNotify) { WmInfo *wmPtr = winPtr->wmInfoPtr; Window root, *children, dummy2, *virtualRootPtr; Atom virtualRootAtom, actualType; int actualFormat; unsigned long numItems, bytesAfter; unsigned int dummy; /* * Locate the ancestor of this window that is just below the * root window for the screen (could be the window itself). * This code is a bit tricky because it allows for the * possibility of a virtual root window, which is identified * with a property named __SWM_VROOT. */ virtualRootAtom = Tk_InternAtom((Tk_Window) winPtr, "__SWM_VROOT"); wmPtr->flags &= ~WM_NESTED_REPARENT; wmPtr->reparent = None; root = eventPtr->xreparent.parent; while (root != RootWindow(winPtr->display, winPtr->screenNum)) { virtualRootPtr = NULL; if (XGetWindowProperty(winPtr->display, root, virtualRootAtom, 0, (long) 1, False, XA_WINDOW, &actualType, &actualFormat, &numItems, &bytesAfter, (unsigned char **) &virtualRootPtr) == Success) { if (virtualRootPtr != NULL) { if (*virtualRootPtr != root) { panic("TopLevelEventProc confused over virtual root"); } XFree((char *) virtualRootPtr); break; } } wmPtr->reparent = root; (void) XQueryTree(winPtr->display, root, &dummy2, &root, &children, &dummy); XFree((char *) children); } /* * The ancestor just below the (virtual) root is in wmPtr->reparent * now, and the (virtual) root is in root. */ if (eventPtr->xreparent.parent == root) { wmPtr->reparent = None; wmPtr->flags &= ~WM_NESTED_REPARENT; wmPtr->parentWidth = winPtr->changes.width + 2*winPtr->changes.border_width; wmPtr->parentHeight = winPtr->changes.height + 2*winPtr->changes.border_width; wmPtr->xInParent = wmPtr->yInParent = 0; winPtr->changes.x = eventPtr->xreparent.x; winPtr->changes.y = eventPtr->xreparent.y; } else { int x, y, xOffset, yOffset; unsigned int width, height, bd; if (wmPtr->reparent != eventPtr->xreparent.parent) { wmPtr->flags |= WM_NESTED_REPARENT; } else { wmPtr->flags &= ~WM_NESTED_REPARENT; } /* * Compute and save information about reparent and about * the window's position in reparent. */ (void) XGetGeometry(winPtr->display, wmPtr->reparent, &dummy2, &x, &y, &width, &height, &bd, &dummy); wmPtr->parentWidth = width + 2*bd; wmPtr->parentHeight = height + 2*bd; (void) XTranslateCoordinates(winPtr->display, winPtr->window, wmPtr->reparent, 0, 0, &xOffset, &yOffset, &dummy2); wmPtr->xInParent = xOffset + bd - winPtr->changes.border_width; wmPtr->yInParent = yOffset + bd - winPtr->changes.border_width; winPtr->changes.x = x + xOffset; winPtr->changes.y = y + yOffset; } } else if ((eventPtr->type == EnterNotify) || (eventPtr->type == LeaveNotify)) { TkFocusEventProc(winPtr, eventPtr); } } /* *---------------------------------------------------------------------- * * TopLevelReqProc -- * * This procedure is invoked by the geometry manager whenever * the requested size for a top-level window is changed. * * Results: * None. * * Side effects: * Arrange for the window to be resized to satisfy the request * (this happens as a when-idle action). * *---------------------------------------------------------------------- */ /* ARGSUSED */ static void TopLevelReqProc(dummy, tkwin) ClientData dummy; /* Not used. */ Tk_Window tkwin; /* Information about window. */ { TkWindow *winPtr = (TkWindow *) tkwin; WmInfo *wmPtr; wmPtr = winPtr->wmInfoPtr; if ((wmPtr->prevReqWidth == winPtr->reqWidth) && (wmPtr->prevReqHeight == winPtr->reqHeight)) { return; } wmPtr->prevReqWidth = winPtr->reqWidth; wmPtr->prevReqHeight = winPtr->reqHeight; wmPtr->flags |= WM_UPDATE_SIZE_HINTS; if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) { Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr); wmPtr->flags |= WM_UPDATE_PENDING; } } /* *---------------------------------------------------------------------- * * UpdateGeometryInfo -- * * This procedure is invoked when a top-level window is first * mapped, and also as a when-idle procedure, to bring the * geometry and/or position of a top-level window back into * line with what has been requested by the user and/or widgets. * * Results: * None. * * Side effects: * The window's size and location may change, unless the WM prevents * that from happening. * *---------------------------------------------------------------------- */ static void UpdateGeometryInfo(clientData) ClientData clientData; /* Pointer to the window's record. */ { register TkWindow *winPtr = (TkWindow *) clientData; register WmInfo *wmPtr = winPtr->wmInfoPtr; int x, y, width, height; /* * It isn't safe to issue a new reconfigure request while there is * another reconfigure request outstanding. If this happens, skip * the second reconfigure operation but set a flag so it will get * done with the first one finishes. */ wmPtr->flags &= ~WM_UPDATE_PENDING; if (wmPtr->flags & WM_CONFIG_PENDING) { wmPtr->flags |= WM_CONFIG_AGAIN; return; } /* * Compute the new size for the top-level window. See the * user documentation for details on this, but the size * requested depends on (a) the size requested internally * by the window's widgets, (b) the size requested by the * user in a "wm geometry" command or via wm-based interactive * resizing (if any), and (c) whether or not the window * gridded. Don't permit sizes <= 0 because this upsets * the X server. */ if (wmPtr->width == -1) { width = winPtr->reqWidth; height = winPtr->reqHeight; } else if (IS_GRIDDED(wmPtr)) { width = winPtr->reqWidth + (wmPtr->width - wmPtr->reqGridWidth)*wmPtr->widthInc; height = winPtr->reqHeight + (wmPtr->height - wmPtr->reqGridHeight)*wmPtr->heightInc; } else { width = wmPtr->width; height = wmPtr->height; } if (width <= 0) { width = 1; } if (height <= 0) { height = 1; } /* * Compute the new position for the window. This is tricky, because * we need to include the border widths supplied by a reparented * parent in this calculation, but can't use the parent's current * overall size since that may change as a result of this code. */ if (wmPtr->flags & WM_NEGATIVE_X) { x = DisplayWidth(winPtr->display, winPtr->screenNum) - wmPtr->x - (width + (wmPtr->parentWidth - winPtr->changes.width)) + wmPtr->xInParent; } else { x = wmPtr->x + wmPtr->xInParent; } if (wmPtr->flags & WM_NEGATIVE_Y) { y = DisplayHeight(winPtr->display, winPtr->screenNum) - wmPtr->y - (height + (wmPtr->parentHeight - winPtr->changes.height)) + wmPtr->yInParent; } else { y = wmPtr->y + wmPtr->yInParent; } /* * If the window's size is going to change and the window is * supposed to not be resizable by the user, then we have to * update the size hints. There may also be a size-hint-update * request pending from somewhere else, too. */ if (((width != winPtr->changes.width) || (width != winPtr->changes.width)) && !IS_GRIDDED(wmPtr) && ((wmPtr->sizeHintsFlags & (PMinSize|PMaxSize)) == 0)) { wmPtr->flags |= WM_UPDATE_SIZE_HINTS; } if (wmPtr->flags & WM_UPDATE_SIZE_HINTS) { UpdateSizeHints(winPtr); } /* * If the geometry hasn't changed, be careful to use only a * resize operation. This is because of bugs in some window * managers (e.g. twm, as of 4/24/91) where they don't interpret * coordinates according to ICCCM. */ if ((x != winPtr->changes.x) || (y != winPtr->changes.y)) { wmPtr->configRequest = XNextRequest(winPtr->display); wmPtr->configWidth = width; wmPtr->configHeight = height; Tk_MoveResizeWindow((Tk_Window) winPtr, x, y, (unsigned) width, (unsigned) height); wmPtr->flags |= WM_CONFIG_PENDING; } else if ((width != wmPtr->configWidth) || (height != wmPtr->configHeight)) { wmPtr->configRequest = XNextRequest(winPtr->display); wmPtr->configWidth = width; wmPtr->configHeight = height; Tk_ResizeWindow((Tk_Window) winPtr, (unsigned) width, (unsigned) height); wmPtr->flags |= WM_CONFIG_PENDING; } } /* *-------------------------------------------------------------- * * UpdateSizeHints -- * * This procedure is called to update the window manager's * size hints information from the information in a WmInfo * structure. * * Results: * None. * * Side effects: * Properties get changed for winPtr. * *-------------------------------------------------------------- */ static void UpdateSizeHints(winPtr) TkWindow *winPtr; { register WmInfo *wmPtr = winPtr->wmInfoPtr; XSizeHints *hintsPtr; wmPtr->flags &= ~WM_UPDATE_SIZE_HINTS; #ifndef X11R3 hintsPtr = XAllocSizeHints(); if (hintsPtr == NULL) { return; } /* * Compute the pixel-based sizes for the various fields in the * size hints structure, based on the grid-based sizes in * our structure. */ if (IS_GRIDDED(wmPtr)) { hintsPtr->base_width = winPtr->reqWidth - (wmPtr->reqGridWidth * wmPtr->widthInc); if (hintsPtr->base_width < 0) { hintsPtr->base_width = 0; } hintsPtr->base_height = winPtr->reqHeight - (wmPtr->reqGridHeight * wmPtr->heightInc); if (hintsPtr->base_height < 0) { hintsPtr->base_height = 0; } hintsPtr->min_width = hintsPtr->base_width + (wmPtr->minWidth * wmPtr->widthInc); hintsPtr->min_height = hintsPtr->base_height + (wmPtr->minHeight * wmPtr->heightInc); hintsPtr->max_width = hintsPtr->base_width + (wmPtr->maxWidth * wmPtr->widthInc); hintsPtr->max_height = hintsPtr->base_height + (wmPtr->maxHeight * wmPtr->heightInc); } else { hintsPtr->min_width = wmPtr->minWidth; hintsPtr->min_height = wmPtr->minHeight; hintsPtr->max_width = wmPtr->maxWidth; hintsPtr->max_height = wmPtr->maxHeight; hintsPtr->base_width = 0; hintsPtr->base_height = 0; } hintsPtr->width_inc = wmPtr->widthInc; hintsPtr->height_inc = wmPtr->heightInc; hintsPtr->min_aspect.x = wmPtr->minAspect.x; hintsPtr->min_aspect.y = wmPtr->minAspect.y; hintsPtr->max_aspect.x = wmPtr->maxAspect.x; hintsPtr->max_aspect.y = wmPtr->maxAspect.y; hintsPtr->win_gravity = wmPtr->gravity; hintsPtr->flags = wmPtr->sizeHintsFlags; /* * If a window is non-gridded and no minimum or maximum size has * been specified, don't let the window be resized at all. */ if (!IS_GRIDDED(wmPtr) && ((wmPtr->sizeHintsFlags & (PMinSize|PMaxSize)) == 0)) { int width, height; width = wmPtr->width; height = wmPtr->height; if (width < 0) { width = winPtr->reqWidth; height = winPtr->reqHeight; } hintsPtr->min_width = hintsPtr->max_width = width; hintsPtr->min_height = hintsPtr->max_height = height; hintsPtr->flags |= PMinSize|PMaxSize; } /* * If min or max size isn't specified, fill in with extreme values * rather than leaving unspecified. Otherwise window manager may * do someting counter-intuitive like the last value ever specified. */ if (!(hintsPtr->flags & PMinSize)) { hintsPtr->min_width = hintsPtr->min_height = 0; hintsPtr->flags |= PMinSize; } if (!(hintsPtr->flags & PMaxSize)) { hintsPtr->max_width = hintsPtr->max_height = 1000000; hintsPtr->flags |= PMaxSize; } XSetWMNormalHints(winPtr->display, winPtr->window, hintsPtr); XFree((char *) hintsPtr); #endif /* X11R3 */ } /* *-------------------------------------------------------------- * * UpdateHints -- * * This procedure is called to update the window manager's * hints information from the information in a WmInfo * structure. * * Results: * None. * * Side effects: * Properties get changed for winPtr. * *-------------------------------------------------------------- */ static void UpdateHints(winPtr) TkWindow *winPtr; { WmInfo *wmPtr = winPtr->wmInfoPtr; if (wmPtr->flags & WM_NEVER_MAPPED) { return; } XSetWMHints(winPtr->display, winPtr->window, &wmPtr->hints); } /* *-------------------------------------------------------------- * * ParseGeometry -- * * This procedure parses a geometry string and updates * information used to control the geometry of a top-level * window. * * Results: * A standard Tcl return value, plus an error message in * interp->result if an error occurs. * * Side effects: * The size and/or location of winPtr may change. * *-------------------------------------------------------------- */ static int ParseGeometry(interp, string, winPtr) Tcl_Interp *interp; /* Used for error reporting. */ char *string; /* String containing new geometry. Has the * standard form "=wxh+x+y". */ TkWindow *winPtr; /* Pointer to top-level window whose * geometry is to be changed. */ { register WmInfo *wmPtr = winPtr->wmInfoPtr; int x, y, width, height, flags; char *end; register char *p = string; /* * The leading "=" is optional. */ if (*p == '=') { p++; } /* * Parse the width and height, if they are present. Don't * actually update any of the fields of wmPtr until we've * successfully parsed the entire geometry string. */ width = wmPtr->width; height = wmPtr->height; x = wmPtr->x; y = wmPtr->y; flags = wmPtr->flags; if (isdigit(*p)) { width = strtoul(p, &end, 10); p = end; if (*p != 'x') { goto error; } p++; if (!isdigit(*p)) { goto error; } height = strtoul(p, &end, 10); p = end; } /* * Parse the X and Y coordinates, if they are present. */ if (*p != '\0') { flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y); if (*p == '-') { flags |= WM_NEGATIVE_X; } else if (*p != '+') { goto error; } x = strtol(p+1, &end, 10); p = end; if (*p == '-') { flags |= WM_NEGATIVE_Y; } else if (*p != '+') { goto error; } y = strtol(p+1, &end, 10); if (*end != '\0') { goto error; } /* * Assume that the geometry information came from the user, * unless an explicit source has been specified. Otherwise * most window managers assume that the size hints were * program-specified and they ignore them. */ if ((wmPtr->sizeHintsFlags & (USPosition|PPosition)) == 0) { wmPtr->sizeHintsFlags |= USPosition; wmPtr->flags |= WM_UPDATE_SIZE_HINTS; } } /* * Everything was parsed OK. Update the fields of *wmPtr and * arrange for the appropriate information to be percolated out * to the window manager at the next idle moment. */ wmPtr->width = width; wmPtr->height = height; wmPtr->x = x; wmPtr->y = y; wmPtr->flags = flags; if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) { Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr); wmPtr->flags |= WM_UPDATE_PENDING; } return TCL_OK; error: Tcl_AppendResult(interp, "bad geometry specifier \"", string, "\"", (char *) NULL); return TCL_ERROR; } /* *---------------------------------------------------------------------- * * Tk_GetRootCoords -- * * Given a token for a window, this procedure traces through the * window's lineage to find the root-window coordinates corresponding * to point (0,0) in the window. * * Results: * The locations pointed to by xPtr and yPtr are filled in with * the root coordinates of the (0,0) point in tkwin. * * Side effects: * None. * *---------------------------------------------------------------------- */ void Tk_GetRootCoords(tkwin, xPtr, yPtr) Tk_Window tkwin; /* Token for window. */ int *xPtr; /* Where to store x-displacement of (0,0). */ int *yPtr; /* Where to store y-displacement of (0,0). */ { int x, y; register TkWindow *winPtr = (TkWindow *) tkwin; /* * Search back through this window's parents all the way to a * top-level window, combining the offsets of each window within * its parent. */ x = y = 0; while (1) { x += winPtr->changes.x + winPtr->changes.border_width; y += winPtr->changes.y + winPtr->changes.border_width; if (winPtr->flags & TK_TOP_LEVEL) { break; } winPtr = winPtr->parentPtr; } *xPtr = x; *yPtr = y; } /* *---------------------------------------------------------------------- * * Tk_CoordsToWindow -- * * Given the root coordinates of a point, this procedure * returns the token for the top-most window covering that point, * if there exists such a window in this application. * * Results: * The return result is either a token for the window corresponding * to rootX and rootY, or else NULL to indicate that there is no such * window. * * Side effects: * None. * *---------------------------------------------------------------------- */ Tk_Window Tk_CoordsToWindow(rootX, rootY, tkwin) int rootX, rootY; /* Coordinates of point in root window. */ Tk_Window tkwin; /* Token for any window in application; * used to identify the application. */ { Window rootChild, dummy3, dummy4; int i, dummy1, dummy2; register WmInfo *wmPtr; register TkWindow *winPtr, *childPtr; TkWindow *nextPtr; /* Coordinates of highest child found so * far that contains point. */ int x, y; /* Coordinates in winPtr. */ int tmpx, tmpy, bd; Window *children; /* Children of winPtr, or NULL. */ unsigned int numChildren; /* Size of children array. */ /* * Step 1: find the top-level window that contains the desired * coordinates. */ if (XTranslateCoordinates(Tk_Display(tkwin), RootWindowOfScreen(Tk_Screen(tkwin)), RootWindowOfScreen(Tk_Screen(tkwin)), rootX, rootY, &dummy1, &dummy2, &rootChild) == False) { panic("Tk_CoordsToWindow get False return from XTranslateCoordinates"); } for (wmPtr = firstWmPtr; ; wmPtr = wmPtr->nextPtr) { if (wmPtr == NULL) { return NULL; } if ((wmPtr->reparent == rootChild) || ((wmPtr->reparent == None) && (wmPtr->winPtr->window == rootChild))) { break; } } winPtr = wmPtr->winPtr; if (winPtr->mainPtr != ((TkWindow *) tkwin)->mainPtr) { return NULL; } /* * Step 2: work down through the hierarchy underneath this window. * At each level, scan through all the children to see if any contain * the point. If none do, then we're done. If one does, then do the * same thing on that child. If two or more do, then fetch enough * information from the window server to figure out which is on top, * and repeat on that child. */ x = rootX; y = rootY; while (1) { x -= winPtr->changes.x; y -= winPtr->changes.y; nextPtr = NULL; children = NULL; for (childPtr = winPtr->childList; childPtr != NULL; childPtr = childPtr->nextPtr) { if (!Tk_IsMapped(childPtr) || (childPtr->flags & TK_TOP_LEVEL)) { continue; } tmpx = x - childPtr->changes.x; tmpy = y - childPtr->changes.y; bd = childPtr->changes.border_width; if ((tmpx < -bd) || (tmpy < -bd) || (tmpx >= (childPtr->changes.width + bd)) || (tmpy >= (childPtr->changes.height + bd))) { continue; } if (nextPtr == NULL) { nextPtr = childPtr; continue; } /* * More than one child of same parent overlaps point. Must * figure out which is on top. Keep a cache of the stacking * order for winPtr to help with this, in case there are >2 * children overlapping. */ if (children == NULL) { if (XQueryTree(winPtr->display, winPtr->window, &dummy3, &dummy4, &children, &numChildren) == 0) { panic("Tk_CoordsToWindow get error return from XQueryTree"); } } for (i = 0; i < numChildren; i++) { if (children[i] == childPtr->window) { break; } if (children[i] == nextPtr->window) { nextPtr = childPtr; break; } } } if (children != NULL) { XFree((char *) children); } if (nextPtr == NULL) { break; } winPtr = nextPtr; } return (Tk_Window) winPtr; }