/*1* tkUnixWm.c --2*3* This module takes care of the interactions between a Tk-based4* application and the window manager. Among other things, it5* implements the "wm" command and passes geometry information6* to the window manager.7*8* Copyright (c) 1991-1994 The Regents of the University of California.9* Copyright (c) 1994-1996 Sun Microsystems, Inc.10*11* See the file "license.terms" for information on usage and redistribution12* of this file, and for a DISCLAIMER OF ALL WARRANTIES.13*14* SCCS: @(#) tkUnixWm.c 1.124 96/03/29 14:05:4415*/1617#include "tkInt.h"18#include <errno.h>1920#ifndef WithdrawnState21#define WithdrawnState 022#define Ancient 123#endif24#ifndef PBaseSize25#define PBaseSize 026#endif2728/*29* A data structure of the following type holds information for30* each window manager protocol (such as WM_DELETE_WINDOW) for31* which a handler (i.e. a Tcl command) has been defined for a32* particular top-level window.33*/3435typedef struct ProtocolHandler {36Atom protocol; /* Identifies the protocol. */37struct ProtocolHandler *nextPtr;38/* Next in list of protocol handlers for39* the same top-level window, or NULL for40* end of list. */41Tcl_Interp *interp; /* Interpreter in which to invoke command. */42char command[4]; /* Tcl command to invoke when a client43* message for this protocol arrives.44* The actual size of the structure varies45* to accommodate the needs of the actual46* command. THIS MUST BE THE LAST FIELD OF47* THE STRUCTURE. */48} ProtocolHandler;4950#define HANDLER_SIZE(cmdLength) \51((unsigned) (sizeof(ProtocolHandler) - 3 + cmdLength))5253/*54* A data structure of the following type holds window-manager-related55* information for each top-level window in an application.56*/5758typedef struct TkWmInfo {59TkWindow *winPtr; /* Pointer to main Tk information for60* this window. */61Window reparent; /* If the window has been reparented, this62* gives the ID of the ancestor of the window63* that is a child of the root window (may64* not be window's immediate parent). If65* the window isn't reparented, this has the66* value None. */67Tk_Uid titleUid; /* Title to display in window caption. If68* NULL, use name of widget. */69Tk_Uid iconName; /* Name to display in icon. */70Window master; /* Master window for TRANSIENT_FOR property,71* or None. */72XWMHints hints; /* Various pieces of information for73* window manager. */74Tk_Uid leaderName; /* Path name of leader of window group75* (corresponds to hints.window_group).76* Note: this field doesn't get updated77* if leader is destroyed. */78Tk_Uid masterWindowName; /* Path name of window specified as master79* in "wm transient" command, or NULL.80* Note: this field doesn't get updated if81* masterWindowName is destroyed. */82Tk_Window icon; /* Window to use as icon for this window,83* or NULL. */84Tk_Window iconFor; /* Window for which this window is icon, or85* NULL if this isn't an icon for anyone. */86int withdrawn; /* Non-zero means window has been withdrawn. */8788/*89* Information used to construct an XSizeHints structure for90* the window manager:91*/9293int sizeHintsFlags; /* Flags word for XSizeHints structure.94* If the PBaseSize flag is set then the95* window is gridded; otherwise it isn't96* gridded. */97int minWidth, minHeight; /* Minimum dimensions of window, in98* grid units, not pixels. */99int maxWidth, maxHeight; /* Maximum dimensions of window, in100* grid units, not pixels. */101Tk_Window gridWin; /* Identifies the window that controls102* gridding for this top-level, or NULL if103* the top-level isn't currently gridded. */104int widthInc, heightInc; /* Increments for size changes (# pixels105* per step). */106struct {107int x; /* numerator */108int y; /* denominator */109} minAspect, maxAspect; /* Min/max aspect ratios for window. */110int reqGridWidth, reqGridHeight;111/* The dimensions of the window (in112* grid units) requested through113* the geometry manager. */114int gravity; /* Desired window gravity. */115116/*117* Information used to manage the size and location of a window.118*/119120int width, height; /* Desired dimensions of window, specified121* in grid units. These values are122* set by the "wm geometry" command and by123* ConfigureNotify events (for when wm124* resizes window). -1 means user hasn't125* requested dimensions. */126int x, y; /* Desired X and Y coordinates for window.127* These values are set by "wm geometry",128* plus by ConfigureNotify events (when wm129* moves window). These numbers are130* different than the numbers stored in131* winPtr->changes because (a) they could be132* measured from the right or bottom edge133* of the screen (see WM_NEGATIVE_X and134* WM_NEGATIVE_Y flags) and (b) if the window135* has been reparented then they refer to the136* parent rather than the window itself. */137int parentWidth, parentHeight;138/* Width and height of reparent, in pixels139* *including border*. If window hasn't been140* reparented then these will be the outer141* dimensions of the window, including142* border. */143int xInParent, yInParent; /* Offset of window within reparent, measured144* from upper-left outer corner of parent's145* border to upper-left outer corner of child's146* border. If not reparented then these are147* zero. */148int configWidth, configHeight;149/* Dimensions passed to last request that we150* issued to change geometry of window. Used151* to eliminate redundant resize operations. */152153/*154* Information about the virtual root window for this top-level,155* if there is one.156*/157158Window vRoot; /* Virtual root window for this top-level,159* or None if there is no virtual root160* window (i.e. just use the screen's root). */161int vRootX, vRootY; /* Position of the virtual root inside the162* root window. If the WM_VROOT_OFFSET_STALE163* flag is set then this information may be164* incorrect and needs to be refreshed from165* the X server. If vRoot is None then these166* values are both 0. */167int vRootWidth, vRootHeight;/* Dimensions of the virtual root window.168* If vRoot is None, gives the dimensions169* of the containing screen. This information170* is never stale, even though vRootX and171* vRootY can be. */172173/*174* Miscellaneous information.175*/176177ProtocolHandler *protPtr; /* First in list of protocol handlers for178* this window (NULL means none). */179int cmdArgc; /* Number of elements in cmdArgv below. */180char **cmdArgv; /* Array of strings to store in the181* WM_COMMAND property. NULL means nothing182* available. */183char *clientMachine; /* String to store in WM_CLIENT_MACHINE184* property, or NULL. */185int flags; /* Miscellaneous flags, defined below. */186struct TkWmInfo *nextPtr; /* Next in list of all top-level windows. */187} WmInfo;188189/*190* Flag values for WmInfo structures:191*192* WM_NEVER_MAPPED - non-zero means window has never been193* mapped; need to update all info when194* window is first mapped.195* WM_UPDATE_PENDING - non-zero means a call to UpdateGeometryInfo196* has already been scheduled for this197* window; no need to schedule another one.198* WM_NEGATIVE_X - non-zero means x-coordinate is measured in199* pixels from right edge of screen, rather200* than from left edge.201* WM_NEGATIVE_Y - non-zero means y-coordinate is measured in202* pixels up from bottom of screen, rather than203* down from top.204* WM_UPDATE_SIZE_HINTS - non-zero means that new size hints need to be205* propagated to window manager.206* WM_SYNC_PENDING - set to non-zero while waiting for the window207* manager to respond to some state change.208* WM_VROOT_OFFSET_STALE - non-zero means that (x,y) offset information209* about the virtual root window is stale and210* needs to be fetched fresh from the X server.211* WM_ABOUT_TO_MAP - non-zero means that the window is about to212* be mapped by TkWmMapWindow. This is used213* by UpdateGeometryInfo to modify its behavior.214* WM_MOVE_PENDING - non-zero means the application has requested215* a new position for the window, but it hasn't216* been reflected through the window manager217* yet.218* WM_COLORMAPS_EXPLICIT - non-zero means the colormap windows were219* set explicitly via "wm colormapwindows".220* WM_ADDED_TOPLEVEL_COLORMAP - non-zero means that when "wm colormapwindows"221* was called the top-level itself wasn't222* specified, so we added it implicitly at223* the end of the list.224* WM_WIDTH_NOT_RESIZABLE - non-zero means that we're not supposed to225* allow the user to change the width of the226* window (controlled by "wm resizable"227* command).228* WM_HEIGHT_NOT_RESIZABLE - non-zero means that we're not supposed to229* allow the user to change the height of the230* window (controlled by "wm resizable"231* command).232*/233234#define WM_NEVER_MAPPED 1235#define WM_UPDATE_PENDING 2236#define WM_NEGATIVE_X 4237#define WM_NEGATIVE_Y 8238#define WM_UPDATE_SIZE_HINTS 0x10239#define WM_SYNC_PENDING 0x20240#define WM_VROOT_OFFSET_STALE 0x40241#define WM_ABOUT_TO_MAP 0x100242#define WM_MOVE_PENDING 0x200243#define WM_COLORMAPS_EXPLICIT 0x400244#define WM_ADDED_TOPLEVEL_COLORMAP 0x800245#define WM_WIDTH_NOT_RESIZABLE 0x1000246#define WM_HEIGHT_NOT_RESIZABLE 0x2000247248/*249* This module keeps a list of all top-level windows, primarily to250* simplify the job of Tk_CoordsToWindow.251*/252253static WmInfo *firstWmPtr = NULL; /* Points to first top-level window. */254255256/*257* The variable below is used to enable or disable tracing in this258* module. If tracing is enabled, then information is printed on259* standard output about interesting interactions with the window260* manager.261*/262263static int wmTracing = 0;264265/*266* The following structure is the official type record for geometry267* management of top-level windows.268*/269270static void TopLevelReqProc _ANSI_ARGS_((ClientData dummy,271Tk_Window tkwin));272273static Tk_GeomMgr wmMgrType = {274"wm", /* name */275TopLevelReqProc, /* requestProc */276(Tk_GeomLostSlaveProc *) NULL, /* lostSlaveProc */277};278279/*280* Structures of the following type are used for communication between281* WaitForEvent, WaitRestrictProc, and WaitTimeoutProc.282*/283284typedef struct WaitRestrictInfo {285Display *display; /* Window belongs to this display. */286Window window; /* We're waiting for events on this window. */287int type; /* We only care about this type of event. */288XEvent *eventPtr; /* Where to store the event when it's found. */289int foundEvent; /* Non-zero means that an event of the290* desired type has been found. */291int timeout; /* Non-zero means that too much time elapsed292* while waiting, and we should just give293* up. */294} WaitRestrictInfo;295296/*297* Forward declarations for procedures defined in this file:298*/299300static int ComputeReparentGeometry _ANSI_ARGS_((TkWindow *winPtr));301static void ConfigureEvent _ANSI_ARGS_((TkWindow *winPtr,302XConfigureEvent *eventPtr));303static void GetMaxSize _ANSI_ARGS_((WmInfo *wmPtr,304int *maxWidthPtr, int *maxHeightPtr));305static int ParseGeometry _ANSI_ARGS_((Tcl_Interp *interp,306char *string, TkWindow *winPtr));307static void ReparentEvent _ANSI_ARGS_((TkWindow *winPtr,308XReparentEvent *eventPtr));309static void TopLevelEventProc _ANSI_ARGS_((ClientData clientData,310XEvent *eventPtr));311static void TopLevelReqProc _ANSI_ARGS_((ClientData dummy,312Tk_Window tkwin));313static void UpdateGeometryInfo _ANSI_ARGS_((314ClientData clientData));315static void UpdateHints _ANSI_ARGS_((TkWindow *winPtr));316static void UpdateSizeHints _ANSI_ARGS_((TkWindow *winPtr));317static void UpdateVRootGeometry _ANSI_ARGS_((WmInfo *wmPtr));318static void UpdateWmProtocols _ANSI_ARGS_((WmInfo *wmPtr));319static void WaitForConfigureNotify _ANSI_ARGS_((TkWindow *winPtr,320unsigned long serial));321static int WaitForEvent _ANSI_ARGS_((Display *display,322Window window, int type, XEvent *eventPtr));323static void WaitForMapNotify _ANSI_ARGS_((TkWindow *winPtr,324int mapped));325static Tk_RestrictAction326WaitRestrictProc _ANSI_ARGS_((ClientData clientData,327XEvent *eventPtr));328static void WaitTimeoutProc _ANSI_ARGS_((ClientData clientData));329330/*331*--------------------------------------------------------------332*333* TkWmNewWindow --334*335* This procedure is invoked whenever a new top-level336* window is created. Its job is to initialize the WmInfo337* structure for the window.338*339* Results:340* None.341*342* Side effects:343* A WmInfo structure gets allocated and initialized.344*345*--------------------------------------------------------------346*/347348void349TkWmNewWindow(winPtr)350TkWindow *winPtr; /* Newly-created top-level window. */351{352register WmInfo *wmPtr;353354wmPtr = (WmInfo *) ckalloc(sizeof(WmInfo));355wmPtr->winPtr = winPtr;356wmPtr->reparent = None;357wmPtr->titleUid = NULL;358wmPtr->iconName = NULL;359wmPtr->master = None;360wmPtr->hints.flags = InputHint | StateHint;361wmPtr->hints.input = True;362wmPtr->hints.initial_state = NormalState;363wmPtr->hints.icon_pixmap = None;364wmPtr->hints.icon_window = None;365wmPtr->hints.icon_x = wmPtr->hints.icon_y = 0;366wmPtr->hints.icon_mask = None;367wmPtr->hints.window_group = None;368wmPtr->leaderName = NULL;369wmPtr->masterWindowName = NULL;370wmPtr->icon = NULL;371wmPtr->iconFor = NULL;372wmPtr->withdrawn = 0;373wmPtr->sizeHintsFlags = 0;374wmPtr->minWidth = wmPtr->minHeight = 1;375376/*377* Default the maximum dimensions to the size of the display, minus378* a guess about how space is needed for window manager decorations.379*/380381wmPtr->maxWidth = 0;382wmPtr->maxHeight = 0;383wmPtr->gridWin = NULL;384wmPtr->widthInc = wmPtr->heightInc = 1;385wmPtr->minAspect.x = wmPtr->minAspect.y = 1;386wmPtr->maxAspect.x = wmPtr->maxAspect.y = 1;387wmPtr->reqGridWidth = wmPtr->reqGridHeight = -1;388wmPtr->gravity = NorthWestGravity;389wmPtr->width = -1;390wmPtr->height = -1;391wmPtr->x = winPtr->changes.x;392wmPtr->y = winPtr->changes.y;393wmPtr->parentWidth = winPtr->changes.width394+ 2*winPtr->changes.border_width;395wmPtr->parentHeight = winPtr->changes.height396+ 2*winPtr->changes.border_width;397wmPtr->xInParent = wmPtr->yInParent = 0;398wmPtr->configWidth = -1;399wmPtr->configHeight = -1;400wmPtr->vRoot = None;401wmPtr->protPtr = NULL;402wmPtr->cmdArgv = NULL;403wmPtr->clientMachine = NULL;404wmPtr->flags = WM_NEVER_MAPPED;405wmPtr->nextPtr = firstWmPtr;406firstWmPtr = wmPtr;407winPtr->wmInfoPtr = wmPtr;408409UpdateVRootGeometry(wmPtr);410411/*412* Tk must monitor structure events for top-level windows, in order413* to detect size and position changes caused by window managers.414*/415416Tk_CreateEventHandler((Tk_Window) winPtr, StructureNotifyMask,417TopLevelEventProc, (ClientData) winPtr);418419/*420* Arrange for geometry requests to be reflected from the window421* to the window manager.422*/423424Tk_ManageGeometry((Tk_Window) winPtr, &wmMgrType, (ClientData) 0);425}426427/*428*--------------------------------------------------------------429*430* TkWmMapWindow --431*432* This procedure is invoked to map a top-level window. This433* module gets a chance to update all window-manager-related434* information in properties before the window manager sees435* the map event and checks the properties. It also gets to436* decide whether or not to even map the window after all.437*438* Results:439* None.440*441* Side effects:442* Properties of winPtr may get updated to provide up-to-date443* information to the window manager. The window may also get444* mapped, but it may not be if this procedure decides that445* isn't appropriate (e.g. because the window is withdrawn).446*447*--------------------------------------------------------------448*/449450void451TkWmMapWindow(winPtr)452TkWindow *winPtr; /* Top-level window that's about to453* be mapped. */454{455register WmInfo *wmPtr = winPtr->wmInfoPtr;456#if !Ancient457XTextProperty textProp;458459if (wmPtr->flags & WM_NEVER_MAPPED) {460wmPtr->flags &= ~WM_NEVER_MAPPED;461462/*463* This is the first time this window has ever been mapped.464* Store all the window-manager-related information for the465* window.466*/467468if (wmPtr->titleUid == NULL) {469wmPtr->titleUid = winPtr->nameUid;470}471if (XStringListToTextProperty(&wmPtr->titleUid, 1, &textProp) != 0) {472XSetWMName(winPtr->display, winPtr->window, &textProp);473XFree((char *) textProp.value);474}475476TkWmSetClass(winPtr);477478if (wmPtr->iconName != NULL) {479XSetIconName(winPtr->display, winPtr->window, wmPtr->iconName);480}481482if (wmPtr->master != None) {483XSetTransientForHint(winPtr->display, winPtr->window,484wmPtr->master);485}486487wmPtr->flags |= WM_UPDATE_SIZE_HINTS;488UpdateHints(winPtr);489UpdateWmProtocols(wmPtr);490if (wmPtr->cmdArgv != NULL) {491XSetCommand(winPtr->display, winPtr->window, wmPtr->cmdArgv,492wmPtr->cmdArgc);493}494if (wmPtr->clientMachine != NULL) {495if (XStringListToTextProperty(&wmPtr->clientMachine, 1, &textProp)496!= 0) {497XSetWMClientMachine(winPtr->display, winPtr->window,498&textProp);499XFree((char *) textProp.value);500}501}502}503#endif504if (wmPtr->hints.initial_state == WithdrawnState) {505return;506}507if (wmPtr->iconFor != NULL) {508/*509* This window is an icon for somebody else. Make sure that510* the geometry is up-to-date, then return without mapping511* the window.512*/513514if (wmPtr->flags & WM_UPDATE_PENDING) {515Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);516}517UpdateGeometryInfo((ClientData) winPtr);518return;519}520wmPtr->flags |= WM_ABOUT_TO_MAP;521if (wmPtr->flags & WM_UPDATE_PENDING) {522Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);523}524UpdateGeometryInfo((ClientData) winPtr);525wmPtr->flags &= ~WM_ABOUT_TO_MAP;526527/*528* Map the window, then wait to be sure that the window manager has529* processed the map operation.530*/531532XMapWindow(winPtr->display, winPtr->window);533if (wmPtr->hints.initial_state == NormalState) {534WaitForMapNotify(winPtr, 1);535}536}537538/*539*--------------------------------------------------------------540*541* TkWmUnmapWindow --542*543* This procedure is invoked to unmap a top-level window. The544* only thing it does special is to wait for the window actually545* to be unmapped.546*547* Results:548* None.549*550* Side effects:551* Unmaps the window.552*553*--------------------------------------------------------------554*/555556void557TkWmUnmapWindow(winPtr)558TkWindow *winPtr; /* Top-level window that's about to559* be mapped. */560{561/*562* It seems to be important to wait after unmapping a top-level563* window until the window really gets unmapped. I don't completely564* understand all the interactions with the window manager, but if565* we go on without waiting, and if the window is then mapped again566* quickly, events seem to get lost so that we think the window isn't567* mapped when in fact it is mapped. I suspect that this has something568* to do with the window manager filtering Map events (and possily not569* filtering Unmap events?).570*/571XUnmapWindow(winPtr->display, winPtr->window);572WaitForMapNotify(winPtr, 0);573}574575/*576*--------------------------------------------------------------577*578* TkWmDeadWindow --579*580* This procedure is invoked when a top-level window is581* about to be deleted. It cleans up the wm-related data582* structures for the window.583*584* Results:585* None.586*587* Side effects:588* The WmInfo structure for winPtr gets freed up.589*590*--------------------------------------------------------------591*/592593void594TkWmDeadWindow(winPtr)595TkWindow *winPtr; /* Top-level window that's being deleted. */596{597register WmInfo *wmPtr = winPtr->wmInfoPtr;598WmInfo *wmPtr2;599600if (wmPtr == NULL) {601return;602}603if (firstWmPtr == wmPtr) {604firstWmPtr = wmPtr->nextPtr;605} else {606register WmInfo *prevPtr;607608for (prevPtr = firstWmPtr; ; prevPtr = prevPtr->nextPtr) {609if (prevPtr == NULL) {610panic("couldn't unlink window in TkWmDeadWindow");611}612if (prevPtr->nextPtr == wmPtr) {613prevPtr->nextPtr = wmPtr->nextPtr;614break;615}616}617}618if (wmPtr->hints.flags & IconPixmapHint) {619Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap);620}621if (wmPtr->hints.flags & IconMaskHint) {622Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_mask);623}624if (wmPtr->icon != NULL) {625wmPtr2 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;626wmPtr2->iconFor = NULL;627wmPtr2->withdrawn = 1;628}629if (wmPtr->iconFor != NULL) {630wmPtr2 = ((TkWindow *) wmPtr->iconFor)->wmInfoPtr;631wmPtr2->icon = NULL;632wmPtr2->hints.flags &= ~IconWindowHint;633UpdateHints((TkWindow *) wmPtr->iconFor);634}635while (wmPtr->protPtr != NULL) {636ProtocolHandler *protPtr;637638protPtr = wmPtr->protPtr;639wmPtr->protPtr = protPtr->nextPtr;640Tcl_EventuallyFree((ClientData) protPtr, TCL_DYNAMIC);641}642if (wmPtr->cmdArgv != NULL) {643ckfree((char *) wmPtr->cmdArgv);644}645if (wmPtr->clientMachine != NULL) {646ckfree((char *) wmPtr->clientMachine);647}648if (wmPtr->flags & WM_UPDATE_PENDING) {649Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);650}651ckfree((char *) wmPtr);652winPtr->wmInfoPtr = NULL;653}654655/*656*--------------------------------------------------------------657*658* TkWmSetClass --659*660* This procedure is invoked whenever a top-level window's661* class is changed. If the window has been mapped then this662* procedure updates the window manager property for the663* class. If the window hasn't been mapped, the update is664* deferred until just before the first mapping.665*666* Results:667* None.668*669* Side effects:670* A window property may get updated.671*672*--------------------------------------------------------------673*/674675void676TkWmSetClass(winPtr)677TkWindow *winPtr; /* Newly-created top-level window. */678{679if (winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {680return;681}682683if (winPtr->classUid != NULL) {684XClassHint *classPtr;685686classPtr = XAllocClassHint();687classPtr->res_name = winPtr->nameUid;688classPtr->res_class = winPtr->classUid;689XSetClassHint(winPtr->display, winPtr->window, classPtr);690XFree((char *) classPtr);691}692}693694/*695*----------------------------------------------------------------------696*697* Tk_WmCmd --698*699* This procedure is invoked to process the "wm" Tcl command.700* See the user documentation for details on what it does.701*702* Results:703* A standard Tcl result.704*705* Side effects:706* See the user documentation.707*708*----------------------------------------------------------------------709*/710711/* ARGSUSED */712int713Tk_WmCmd(clientData, interp, argc, argv)714ClientData clientData; /* Main window associated with715* interpreter. */716Tcl_Interp *interp; /* Current interpreter. */717int argc; /* Number of arguments. */718char **argv; /* Argument strings. */719{720Tk_Window tkwin = (Tk_Window) clientData;721TkWindow *winPtr;722register WmInfo *wmPtr;723int c;724size_t length;725726if (argc < 2) {727wrongNumArgs:728Tcl_AppendResult(interp, "wrong # args: should be \"",729argv[0], " option window ?arg ...?\"", (char *) NULL);730return TCL_ERROR;731}732c = argv[1][0];733length = strlen(argv[1]);734if ((c == 't') && (strncmp(argv[1], "tracing", length) == 0)735&& (length >= 3)) {736if ((argc != 2) && (argc != 3)) {737Tcl_AppendResult(interp, "wrong # arguments: must be \"",738argv[0], " tracing ?boolean?\"", (char *) NULL);739return TCL_ERROR;740}741if (argc == 2) {742interp->result = (wmTracing) ? "on" : "off";743return TCL_OK;744}745return Tcl_GetBoolean(interp, argv[2], &wmTracing);746}747748if (argc < 3) {749goto wrongNumArgs;750}751winPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);752if (winPtr == NULL) {753return TCL_ERROR;754}755if (!(winPtr->flags & TK_TOP_LEVEL)) {756Tcl_AppendResult(interp, "window \"", winPtr->pathName,757"\" isn't a top-level window", (char *) NULL);758return TCL_ERROR;759}760wmPtr = winPtr->wmInfoPtr;761if ((c == 'a') && (strncmp(argv[1], "aspect", length) == 0)) {762int numer1, denom1, numer2, denom2;763764if ((argc != 3) && (argc != 7)) {765Tcl_AppendResult(interp, "wrong # arguments: must be \"",766argv[0], " aspect window ?minNumer minDenom ",767"maxNumer maxDenom?\"", (char *) NULL);768return TCL_ERROR;769}770if (argc == 3) {771if (wmPtr->sizeHintsFlags & PAspect) {772sprintf(interp->result, "%d %d %d %d", wmPtr->minAspect.x,773wmPtr->minAspect.y, wmPtr->maxAspect.x,774wmPtr->maxAspect.y);775}776return TCL_OK;777}778if (*argv[3] == '\0') {779wmPtr->sizeHintsFlags &= ~PAspect;780} else {781if ((Tcl_GetInt(interp, argv[3], &numer1) != TCL_OK)782|| (Tcl_GetInt(interp, argv[4], &denom1) != TCL_OK)783|| (Tcl_GetInt(interp, argv[5], &numer2) != TCL_OK)784|| (Tcl_GetInt(interp, argv[6], &denom2) != TCL_OK)) {785return TCL_ERROR;786}787if ((numer1 <= 0) || (denom1 <= 0) || (numer2 <= 0) ||788(denom2 <= 0)) {789interp->result = "aspect number can't be <= 0";790return TCL_ERROR;791}792wmPtr->minAspect.x = numer1;793wmPtr->minAspect.y = denom1;794wmPtr->maxAspect.x = numer2;795wmPtr->maxAspect.y = denom2;796wmPtr->sizeHintsFlags |= PAspect;797}798wmPtr->flags |= WM_UPDATE_SIZE_HINTS;799goto updateGeom;800} else if ((c == 'c') && (strncmp(argv[1], "client", length) == 0)801&& (length >= 2)) {802if ((argc != 3) && (argc != 4)) {803Tcl_AppendResult(interp, "wrong # arguments: must be \"",804argv[0], " client window ?name?\"",805(char *) NULL);806return TCL_ERROR;807}808if (argc == 3) {809if (wmPtr->clientMachine != NULL) {810interp->result = wmPtr->clientMachine;811}812return TCL_OK;813}814if (argv[3][0] == 0) {815if (wmPtr->clientMachine != NULL) {816ckfree((char *) wmPtr->clientMachine);817wmPtr->clientMachine = NULL;818if (!(wmPtr->flags & WM_NEVER_MAPPED)) {819XDeleteProperty(winPtr->display, winPtr->window,820Tk_InternAtom((Tk_Window) winPtr,821"WM_CLIENT_MACHINE"));822}823}824return TCL_OK;825}826if (wmPtr->clientMachine != NULL) {827ckfree((char *) wmPtr->clientMachine);828}829wmPtr->clientMachine = (char *)830ckalloc((unsigned) (strlen(argv[3]) + 1));831strcpy(wmPtr->clientMachine, argv[3]);832#if !Ancient833if (!(wmPtr->flags & WM_NEVER_MAPPED)) {834XTextProperty textProp;835if (XStringListToTextProperty(&wmPtr->clientMachine, 1, &textProp)836!= 0) {837XSetWMClientMachine(winPtr->display, winPtr->window,838&textProp);839XFree((char *) textProp.value);840}841}842#endif843} else if ((c == 'c') && (strncmp(argv[1], "colormapwindows", length) == 0)844&& (length >= 3)) {845Window *cmapList;846TkWindow *winPtr2;847int count, i, windowArgc, gotToplevel;848char buffer[20], **windowArgv;849850if ((argc != 3) && (argc != 4)) {851Tcl_AppendResult(interp, "wrong # arguments: must be \"",852argv[0], " colormapwindows window ?windowList?\"",853(char *) NULL);854return TCL_ERROR;855}856if (argc == 3) {857Tk_MakeWindowExist((Tk_Window) winPtr);858if (XGetWMColormapWindows(winPtr->display, winPtr->window,859&cmapList, &count) == 0) {860return TCL_OK;861}862for (i = 0; i < count; i++) {863if ((i == (count-1))864&& (wmPtr->flags & WM_ADDED_TOPLEVEL_COLORMAP)) {865break;866}867winPtr2 = (TkWindow *) Tk_IdToWindow(winPtr->display,868cmapList[i]);869if (winPtr2 == NULL) {870sprintf(buffer, "0x%lx", cmapList[i]);871Tcl_AppendElement(interp, buffer);872} else {873Tcl_AppendElement(interp, winPtr2->pathName);874}875}876XFree((char *) cmapList);877return TCL_OK;878}879if (Tcl_SplitList(interp, argv[3], &windowArgc, &windowArgv)880!= TCL_OK) {881return TCL_ERROR;882}883cmapList = (Window *) ckalloc((unsigned)884((windowArgc+1)*sizeof(Window)));885gotToplevel = 0;886for (i = 0; i < windowArgc; i++) {887winPtr2 = (TkWindow *) Tk_NameToWindow(interp, windowArgv[i],888tkwin);889if (winPtr2 == NULL) {890ckfree((char *) cmapList);891ckfree((char *) windowArgv);892return TCL_ERROR;893}894if (winPtr2 == winPtr) {895gotToplevel = 1;896}897if (winPtr2->window == None) {898Tk_MakeWindowExist((Tk_Window) winPtr2);899}900cmapList[i] = winPtr2->window;901}902if (!gotToplevel) {903wmPtr->flags |= WM_ADDED_TOPLEVEL_COLORMAP;904cmapList[windowArgc] = winPtr->window;905windowArgc++;906} else {907wmPtr->flags &= ~WM_ADDED_TOPLEVEL_COLORMAP;908}909wmPtr->flags |= WM_COLORMAPS_EXPLICIT;910XSetWMColormapWindows(winPtr->display, winPtr->window, cmapList,911windowArgc);912ckfree((char *) cmapList);913ckfree((char *) windowArgv);914return TCL_OK;915} else if ((c == 'c') && (strncmp(argv[1], "command", length) == 0)916&& (length >= 3)) {917int cmdArgc;918char **cmdArgv;919920if ((argc != 3) && (argc != 4)) {921Tcl_AppendResult(interp, "wrong # arguments: must be \"",922argv[0], " command window ?value?\"",923(char *) NULL);924return TCL_ERROR;925}926if (argc == 3) {927if (wmPtr->cmdArgv != NULL) {928interp->result = Tcl_Merge(wmPtr->cmdArgc, wmPtr->cmdArgv);929interp->freeProc = TCL_DYNAMIC;930}931return TCL_OK;932}933if (argv[3][0] == 0) {934if (wmPtr->cmdArgv != NULL) {935ckfree((char *) wmPtr->cmdArgv);936wmPtr->cmdArgv = NULL;937if (!(wmPtr->flags & WM_NEVER_MAPPED)) {938XDeleteProperty(winPtr->display, winPtr->window,939Tk_InternAtom((Tk_Window) winPtr, "WM_COMMAND"));940}941}942return TCL_OK;943}944if (Tcl_SplitList(interp, argv[3], &cmdArgc, &cmdArgv) != TCL_OK) {945return TCL_ERROR;946}947if (wmPtr->cmdArgv != NULL) {948ckfree((char *) wmPtr->cmdArgv);949}950wmPtr->cmdArgc = cmdArgc;951wmPtr->cmdArgv = cmdArgv;952if (!(wmPtr->flags & WM_NEVER_MAPPED)) {953XSetCommand(winPtr->display, winPtr->window, cmdArgv, cmdArgc);954}955} else if ((c == 'd') && (strncmp(argv[1], "deiconify", length) == 0)) {956if (argc != 3) {957Tcl_AppendResult(interp, "wrong # arguments: must be \"",958argv[0], " deiconify window\"", (char *) NULL);959return TCL_ERROR;960}961if (wmPtr->iconFor != NULL) {962Tcl_AppendResult(interp, "can't deiconify ", argv[2],963": it is an icon for ", winPtr->pathName, (char *) NULL);964return TCL_ERROR;965}966wmPtr->hints.initial_state = NormalState;967wmPtr->withdrawn = 0;968if (wmPtr->flags & WM_NEVER_MAPPED) {969return TCL_OK;970}971UpdateHints(winPtr);972Tk_MapWindow((Tk_Window) winPtr);973} else if ((c == 'f') && (strncmp(argv[1], "focusmodel", length) == 0)974&& (length >= 2)) {975if ((argc != 3) && (argc != 4)) {976Tcl_AppendResult(interp, "wrong # arguments: must be \"",977argv[0], " focusmodel window ?active|passive?\"",978(char *) NULL);979return TCL_ERROR;980}981if (argc == 3) {982interp->result = wmPtr->hints.input ? "passive" : "active";983return TCL_OK;984}985c = argv[3][0];986length = strlen(argv[3]);987if ((c == 'a') && (strncmp(argv[3], "active", length) == 0)) {988wmPtr->hints.input = False;989} else if ((c == 'p') && (strncmp(argv[3], "passive", length) == 0)) {990wmPtr->hints.input = True;991} else {992Tcl_AppendResult(interp, "bad argument \"", argv[3],993"\": must be active or passive", (char *) NULL);994return TCL_ERROR;995}996UpdateHints(winPtr);997} else if ((c == 'f') && (strncmp(argv[1], "frame", length) == 0)998&& (length >= 2)) {999Window window;10001001if (argc != 3) {1002Tcl_AppendResult(interp, "wrong # arguments: must be \"",1003argv[0], " frame window\"", (char *) NULL);1004return TCL_ERROR;1005}1006window = wmPtr->reparent;1007if (window == None) {1008window = Tk_WindowId((Tk_Window) winPtr);1009}1010sprintf(interp->result, "0x%x", (unsigned int) window);1011} else if ((c == 'g') && (strncmp(argv[1], "geometry", length) == 0)1012&& (length >= 2)) {1013char xSign, ySign;1014int width, height;10151016if ((argc != 3) && (argc != 4)) {1017Tcl_AppendResult(interp, "wrong # arguments: must be \"",1018argv[0], " geometry window ?newGeometry?\"",1019(char *) NULL);1020return TCL_ERROR;1021}1022if (argc == 3) {1023xSign = (wmPtr->flags & WM_NEGATIVE_X) ? '-' : '+';1024ySign = (wmPtr->flags & WM_NEGATIVE_Y) ? '-' : '+';1025if (wmPtr->gridWin != NULL) {1026width = wmPtr->reqGridWidth + (winPtr->changes.width1027- winPtr->reqWidth)/wmPtr->widthInc;1028height = wmPtr->reqGridHeight + (winPtr->changes.height1029- winPtr->reqHeight)/wmPtr->heightInc;1030} else {1031width = winPtr->changes.width;1032height = winPtr->changes.height;1033}1034sprintf(interp->result, "%dx%d%c%d%c%d", width, height,1035xSign, wmPtr->x, ySign, wmPtr->y);1036return TCL_OK;1037}1038if (*argv[3] == '\0') {1039wmPtr->width = -1;1040wmPtr->height = -1;1041goto updateGeom;1042}1043return ParseGeometry(interp, argv[3], winPtr);1044} else if ((c == 'g') && (strncmp(argv[1], "grid", length) == 0)1045&& (length >= 3)) {1046int reqWidth, reqHeight, widthInc, heightInc;10471048if ((argc != 3) && (argc != 7)) {1049Tcl_AppendResult(interp, "wrong # arguments: must be \"",1050argv[0], " grid window ?baseWidth baseHeight ",1051"widthInc heightInc?\"", (char *) NULL);1052return TCL_ERROR;1053}1054if (argc == 3) {1055if (wmPtr->sizeHintsFlags & PBaseSize) {1056sprintf(interp->result, "%d %d %d %d", wmPtr->reqGridWidth,1057wmPtr->reqGridHeight, wmPtr->widthInc,1058wmPtr->heightInc);1059}1060return TCL_OK;1061}1062if (*argv[3] == '\0') {1063/*1064* Turn off gridding and reset the width and height1065* to make sense as ungridded numbers.1066*/10671068wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc);1069if (wmPtr->width != -1) {1070wmPtr->width = winPtr->reqWidth + (wmPtr->width1071- wmPtr->reqGridWidth)*wmPtr->widthInc;1072wmPtr->height = winPtr->reqHeight + (wmPtr->height1073- wmPtr->reqGridHeight)*wmPtr->heightInc;1074}1075wmPtr->widthInc = 1;1076wmPtr->heightInc = 1;1077} else {1078if ((Tcl_GetInt(interp, argv[3], &reqWidth) != TCL_OK)1079|| (Tcl_GetInt(interp, argv[4], &reqHeight) != TCL_OK)1080|| (Tcl_GetInt(interp, argv[5], &widthInc) != TCL_OK)1081|| (Tcl_GetInt(interp, argv[6], &heightInc) != TCL_OK)) {1082return TCL_ERROR;1083}1084if (reqWidth < 0) {1085interp->result = "baseWidth can't be < 0";1086return TCL_ERROR;1087}1088if (reqHeight < 0) {1089interp->result = "baseHeight can't be < 0";1090return TCL_ERROR;1091}1092if (widthInc < 0) {1093interp->result = "widthInc can't be < 0";1094return TCL_ERROR;1095}1096if (heightInc < 0) {1097interp->result = "heightInc can't be < 0";1098return TCL_ERROR;1099}1100Tk_SetGrid((Tk_Window) winPtr, reqWidth, reqHeight, widthInc,1101heightInc);1102}1103wmPtr->flags |= WM_UPDATE_SIZE_HINTS;1104goto updateGeom;1105} else if ((c == 'g') && (strncmp(argv[1], "group", length) == 0)1106&& (length >= 3)) {1107Tk_Window tkwin2;11081109if ((argc != 3) && (argc != 4)) {1110Tcl_AppendResult(interp, "wrong # arguments: must be \"",1111argv[0], " group window ?pathName?\"",1112(char *) NULL);1113return TCL_ERROR;1114}1115if (argc == 3) {1116if (wmPtr->hints.flags & WindowGroupHint) {1117interp->result = wmPtr->leaderName;1118}1119return TCL_OK;1120}1121if (*argv[3] == '\0') {1122wmPtr->hints.flags &= ~WindowGroupHint;1123wmPtr->leaderName = NULL;1124} else {1125tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin);1126if (tkwin2 == NULL) {1127return TCL_ERROR;1128}1129Tk_MakeWindowExist(tkwin2);1130wmPtr->hints.window_group = Tk_WindowId(tkwin2);1131wmPtr->hints.flags |= WindowGroupHint;1132wmPtr->leaderName = Tk_PathName(tkwin2);1133}1134UpdateHints(winPtr);1135} else if ((c == 'i') && (strncmp(argv[1], "iconbitmap", length) == 0)1136&& (length >= 5)) {1137Pixmap pixmap;11381139if ((argc != 3) && (argc != 4)) {1140Tcl_AppendResult(interp, "wrong # arguments: must be \"",1141argv[0], " iconbitmap window ?bitmap?\"",1142(char *) NULL);1143return TCL_ERROR;1144}1145if (argc == 3) {1146if (wmPtr->hints.flags & IconPixmapHint) {1147interp->result = Tk_NameOfBitmap(winPtr->display,1148wmPtr->hints.icon_pixmap);1149}1150return TCL_OK;1151}1152if (*argv[3] == '\0') {1153if (wmPtr->hints.icon_pixmap != None) {1154Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap);1155wmPtr->hints.icon_pixmap = None;1156}1157wmPtr->hints.flags &= ~IconPixmapHint;1158} else {1159pixmap = Tk_GetBitmap(interp, (Tk_Window) winPtr,1160Tk_GetUid(argv[3]));1161if (pixmap == None) {1162return TCL_ERROR;1163}1164wmPtr->hints.icon_pixmap = pixmap;1165wmPtr->hints.flags |= IconPixmapHint;1166}1167UpdateHints(winPtr);1168} else if ((c == 'i') && (strncmp(argv[1], "iconify", length) == 0)1169&& (length >= 5)) {1170if (argc != 3) {1171Tcl_AppendResult(interp, "wrong # arguments: must be \"",1172argv[0], " iconify window\"", (char *) NULL);1173return TCL_ERROR;1174}1175if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) {1176Tcl_AppendResult(interp, "can't iconify \"", winPtr->pathName,1177"\": override-redirect flag is set", (char *) NULL);1178return TCL_ERROR;1179}1180if (wmPtr->master != None) {1181Tcl_AppendResult(interp, "can't iconify \"", winPtr->pathName,1182"\": it is a transient", (char *) NULL);1183return TCL_ERROR;1184}1185if (wmPtr->iconFor != NULL) {1186Tcl_AppendResult(interp, "can't iconify ", argv[2],1187": it is an icon for ", winPtr->pathName, (char *) NULL);1188return TCL_ERROR;1189}1190wmPtr->hints.initial_state = IconicState;1191if (wmPtr->flags & WM_NEVER_MAPPED) {1192return TCL_OK;1193}1194if (wmPtr->withdrawn) {1195UpdateHints(winPtr);1196Tk_MapWindow((Tk_Window) winPtr);1197wmPtr->withdrawn = 0;1198} else {1199if (XIconifyWindow(winPtr->display, winPtr->window,1200winPtr->screenNum) == 0) {1201interp->result =1202"couldn't send iconify message to window manager";1203return TCL_ERROR;1204}1205WaitForMapNotify(winPtr, 0);1206}1207} else if ((c == 'i') && (strncmp(argv[1], "iconmask", length) == 0)1208&& (length >= 5)) {1209Pixmap pixmap;12101211if ((argc != 3) && (argc != 4)) {1212Tcl_AppendResult(interp, "wrong # arguments: must be \"",1213argv[0], " iconmask window ?bitmap?\"",1214(char *) NULL);1215return TCL_ERROR;1216}1217if (argc == 3) {1218if (wmPtr->hints.flags & IconMaskHint) {1219interp->result = Tk_NameOfBitmap(winPtr->display,1220wmPtr->hints.icon_mask);1221}1222return TCL_OK;1223}1224if (*argv[3] == '\0') {1225if (wmPtr->hints.icon_mask != None) {1226Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_mask);1227}1228wmPtr->hints.flags &= ~IconMaskHint;1229} else {1230pixmap = Tk_GetBitmap(interp, tkwin, Tk_GetUid(argv[3]));1231if (pixmap == None) {1232return TCL_ERROR;1233}1234wmPtr->hints.icon_mask = pixmap;1235wmPtr->hints.flags |= IconMaskHint;1236}1237UpdateHints(winPtr);1238} else if ((c == 'i') && (strncmp(argv[1], "iconname", length) == 0)1239&& (length >= 5)) {1240if (argc > 4) {1241Tcl_AppendResult(interp, "wrong # arguments: must be \"",1242argv[0], " iconname window ?newName?\"", (char *) NULL);1243return TCL_ERROR;1244}1245if (argc == 3) {1246interp->result = (wmPtr->iconName != NULL) ? wmPtr->iconName : "";1247return TCL_OK;1248} else {1249wmPtr->iconName = Tk_GetUid(argv[3]);1250if (!(wmPtr->flags & WM_NEVER_MAPPED)) {1251XSetIconName(winPtr->display, winPtr->window, wmPtr->iconName);1252}1253}1254} else if ((c == 'i') && (strncmp(argv[1], "iconposition", length) == 0)1255&& (length >= 5)) {1256int x, y;12571258if ((argc != 3) && (argc != 5)) {1259Tcl_AppendResult(interp, "wrong # arguments: must be \"",1260argv[0], " iconposition window ?x y?\"",1261(char *) NULL);1262return TCL_ERROR;1263}1264if (argc == 3) {1265if (wmPtr->hints.flags & IconPositionHint) {1266sprintf(interp->result, "%d %d", wmPtr->hints.icon_x,1267wmPtr->hints.icon_y);1268}1269return TCL_OK;1270}1271if (*argv[3] == '\0') {1272wmPtr->hints.flags &= ~IconPositionHint;1273} else {1274if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK)1275|| (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)){1276return TCL_ERROR;1277}1278wmPtr->hints.icon_x = x;1279wmPtr->hints.icon_y = y;1280wmPtr->hints.flags |= IconPositionHint;1281}1282UpdateHints(winPtr);1283} else if ((c == 'i') && (strncmp(argv[1], "iconwindow", length) == 0)1284&& (length >= 5)) {1285Tk_Window tkwin2;1286WmInfo *wmPtr2;1287XSetWindowAttributes atts;12881289if ((argc != 3) && (argc != 4)) {1290Tcl_AppendResult(interp, "wrong # arguments: must be \"",1291argv[0], " iconwindow window ?pathName?\"",1292(char *) NULL);1293return TCL_ERROR;1294}1295if (argc == 3) {1296if (wmPtr->icon != NULL) {1297interp->result = Tk_PathName(wmPtr->icon);1298}1299return TCL_OK;1300}1301if (*argv[3] == '\0') {1302wmPtr->hints.flags &= ~IconWindowHint;1303if (wmPtr->icon != NULL) {1304/*1305* Remove the icon window relationship. In principle we1306* should also re-enable button events for the window, but1307* this doesn't work in general because the window manager1308* is probably selecting on them (we'll get an error if1309* we try to re-enable the events). So, just leave the1310* icon window event-challenged; the user will have to1311* recreate it if they want button events.1312*/13131314wmPtr2 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;1315wmPtr2->iconFor = NULL;1316wmPtr2->withdrawn = 1;1317wmPtr2->hints.initial_state = WithdrawnState;1318}1319wmPtr->icon = NULL;1320} else {1321tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin);1322if (tkwin2 == NULL) {1323return TCL_ERROR;1324}1325if (!Tk_IsTopLevel(tkwin2)) {1326Tcl_AppendResult(interp, "can't use ", argv[3],1327" as icon window: not at top level", (char *) NULL);1328return TCL_ERROR;1329}1330wmPtr2 = ((TkWindow *) tkwin2)->wmInfoPtr;1331if (wmPtr2->iconFor != NULL) {1332Tcl_AppendResult(interp, argv[3], " is already an icon for ",1333Tk_PathName(wmPtr2->iconFor), (char *) NULL);1334return TCL_ERROR;1335}1336if (wmPtr->icon != NULL) {1337WmInfo *wmPtr3 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;1338wmPtr3->iconFor = NULL;1339wmPtr3->withdrawn = 1;13401341/*1342* Let the window use button events again.1343*/13441345atts.event_mask = Tk_Attributes(wmPtr->icon)->event_mask1346| ButtonPressMask;1347Tk_ChangeWindowAttributes(wmPtr->icon, CWEventMask, &atts);1348}13491350/*1351* Disable button events in the icon window: some window1352* managers (like olvwm) want to get the events themselves,1353* but X only allows one application at a time to receive1354* button events for a window.1355*/13561357atts.event_mask = Tk_Attributes(tkwin2)->event_mask1358& ~ButtonPressMask;1359Tk_ChangeWindowAttributes(tkwin2, CWEventMask, &atts);1360Tk_MakeWindowExist(tkwin2);1361wmPtr->hints.icon_window = Tk_WindowId(tkwin2);1362wmPtr->hints.flags |= IconWindowHint;1363wmPtr->icon = tkwin2;1364wmPtr2->iconFor = (Tk_Window) winPtr;1365if (!wmPtr2->withdrawn && !(wmPtr2->flags & WM_NEVER_MAPPED)) {1366wmPtr2->withdrawn = 0;1367if (XWithdrawWindow(Tk_Display(tkwin2), Tk_WindowId(tkwin2),1368Tk_ScreenNumber(tkwin2)) == 0) {1369interp->result =1370"couldn't send withdraw message to window manager";1371return TCL_ERROR;1372}1373WaitForMapNotify((TkWindow *) tkwin2, 0);1374}1375}1376UpdateHints(winPtr);1377} else if ((c == 'm') && (strncmp(argv[1], "maxsize", length) == 0)1378&& (length >= 2)) {1379int width, height;1380if ((argc != 3) && (argc != 5)) {1381Tcl_AppendResult(interp, "wrong # arguments: must be \"",1382argv[0], " maxsize window ?width height?\"", (char *) NULL);1383return TCL_ERROR;1384}1385if (argc == 3) {1386GetMaxSize(wmPtr, &width, &height);1387sprintf(interp->result, "%d %d", width, height);1388return TCL_OK;1389}1390if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK)1391|| (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) {1392return TCL_ERROR;1393}1394wmPtr->maxWidth = width;1395wmPtr->maxHeight = height;1396wmPtr->flags |= WM_UPDATE_SIZE_HINTS;1397goto updateGeom;1398} else if ((c == 'm') && (strncmp(argv[1], "minsize", length) == 0)1399&& (length >= 2)) {1400int width, height;1401if ((argc != 3) && (argc != 5)) {1402Tcl_AppendResult(interp, "wrong # arguments: must be \"",1403argv[0], " minsize window ?width height?\"", (char *) NULL);1404return TCL_ERROR;1405}1406if (argc == 3) {1407sprintf(interp->result, "%d %d", wmPtr->minWidth,1408wmPtr->minHeight);1409return TCL_OK;1410}1411if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK)1412|| (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) {1413return TCL_ERROR;1414}1415wmPtr->minWidth = width;1416wmPtr->minHeight = height;1417wmPtr->flags |= WM_UPDATE_SIZE_HINTS;1418goto updateGeom;1419} else if ((c == 'o')1420&& (strncmp(argv[1], "overrideredirect", length) == 0)) {1421int boolean;1422XSetWindowAttributes atts;14231424if ((argc != 3) && (argc != 4)) {1425Tcl_AppendResult(interp, "wrong # arguments: must be \"",1426argv[0], " overrideredirect window ?boolean?\"",1427(char *) NULL);1428return TCL_ERROR;1429}1430if (argc == 3) {1431if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) {1432interp->result = "1";1433} else {1434interp->result = "0";1435}1436return TCL_OK;1437}1438if (Tcl_GetBoolean(interp, argv[3], &boolean) != TCL_OK) {1439return TCL_ERROR;1440}1441atts.override_redirect = (boolean) ? True : False;1442Tk_ChangeWindowAttributes((Tk_Window) winPtr, CWOverrideRedirect,1443&atts);1444} else if ((c == 'p') && (strncmp(argv[1], "positionfrom", length) == 0)1445&& (length >= 2)) {1446if ((argc != 3) && (argc != 4)) {1447Tcl_AppendResult(interp, "wrong # arguments: must be \"",1448argv[0], " positionfrom window ?user/program?\"",1449(char *) NULL);1450return TCL_ERROR;1451}1452if (argc == 3) {1453if (wmPtr->sizeHintsFlags & USPosition) {1454interp->result = "user";1455} else if (wmPtr->sizeHintsFlags & PPosition) {1456interp->result = "program";1457}1458return TCL_OK;1459}1460if (*argv[3] == '\0') {1461wmPtr->sizeHintsFlags &= ~(USPosition|PPosition);1462} else {1463c = argv[3][0];1464length = strlen(argv[3]);1465if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) {1466wmPtr->sizeHintsFlags &= ~PPosition;1467wmPtr->sizeHintsFlags |= USPosition;1468} else if ((c == 'p') && (strncmp(argv[3], "program", length) == 0)) {1469wmPtr->sizeHintsFlags &= ~USPosition;1470wmPtr->sizeHintsFlags |= PPosition;1471} else {1472Tcl_AppendResult(interp, "bad argument \"", argv[3],1473"\": must be program or user", (char *) NULL);1474return TCL_ERROR;1475}1476}1477wmPtr->flags |= WM_UPDATE_SIZE_HINTS;1478goto updateGeom;1479} else if ((c == 'p') && (strncmp(argv[1], "protocol", length) == 0)1480&& (length >= 2)) {1481register ProtocolHandler *protPtr, *prevPtr;1482Atom protocol;1483int cmdLength;14841485if ((argc < 3) || (argc > 5)) {1486Tcl_AppendResult(interp, "wrong # arguments: must be \"",1487argv[0], " protocol window ?name? ?command?\"",1488(char *) NULL);1489return TCL_ERROR;1490}1491if (argc == 3) {1492/*1493* Return a list of all defined protocols for the window.1494*/1495for (protPtr = wmPtr->protPtr; protPtr != NULL;1496protPtr = protPtr->nextPtr) {1497Tcl_AppendElement(interp,1498Tk_GetAtomName((Tk_Window) winPtr, protPtr->protocol));1499}1500return TCL_OK;1501}1502protocol = Tk_InternAtom((Tk_Window) winPtr, argv[3]);1503if (argc == 4) {1504/*1505* Return the command to handle a given protocol.1506*/1507for (protPtr = wmPtr->protPtr; protPtr != NULL;1508protPtr = protPtr->nextPtr) {1509if (protPtr->protocol == protocol) {1510interp->result = protPtr->command;1511return TCL_OK;1512}1513}1514return TCL_OK;1515}15161517/*1518* Delete any current protocol handler, then create a new1519* one with the specified command, unless the command is1520* empty.1521*/15221523for (protPtr = wmPtr->protPtr, prevPtr = NULL; protPtr != NULL;1524prevPtr = protPtr, protPtr = protPtr->nextPtr) {1525if (protPtr->protocol == protocol) {1526if (prevPtr == NULL) {1527wmPtr->protPtr = protPtr->nextPtr;1528} else {1529prevPtr->nextPtr = protPtr->nextPtr;1530}1531Tcl_EventuallyFree((ClientData) protPtr, TCL_DYNAMIC);1532break;1533}1534}1535cmdLength = strlen(argv[4]);1536if (cmdLength > 0) {1537protPtr = (ProtocolHandler *) ckalloc(HANDLER_SIZE(cmdLength));1538protPtr->protocol = protocol;1539protPtr->nextPtr = wmPtr->protPtr;1540wmPtr->protPtr = protPtr;1541protPtr->interp = interp;1542strcpy(protPtr->command, argv[4]);1543}1544if (!(wmPtr->flags & WM_NEVER_MAPPED)) {1545UpdateWmProtocols(wmPtr);1546}1547} else if ((c == 'r') && (strncmp(argv[1], "resizable", length) == 0)) {1548int width, height;15491550if ((argc != 3) && (argc != 5)) {1551Tcl_AppendResult(interp, "wrong # arguments: must be \"",1552argv[0], " resizable window ?width height?\"",1553(char *) NULL);1554return TCL_ERROR;1555}1556if (argc == 3) {1557sprintf(interp->result, "%d %d",1558(wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) ? 0 : 1,1559(wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE) ? 0 : 1);1560return TCL_OK;1561}1562if ((Tcl_GetBoolean(interp, argv[3], &width) != TCL_OK)1563|| (Tcl_GetBoolean(interp, argv[4], &height) != TCL_OK)) {1564return TCL_ERROR;1565}1566if (width) {1567wmPtr->flags &= ~WM_WIDTH_NOT_RESIZABLE;1568} else {1569wmPtr->flags |= WM_WIDTH_NOT_RESIZABLE;1570}1571if (height) {1572wmPtr->flags &= ~WM_HEIGHT_NOT_RESIZABLE;1573} else {1574wmPtr->flags |= WM_HEIGHT_NOT_RESIZABLE;1575}1576wmPtr->flags |= WM_UPDATE_SIZE_HINTS;1577goto updateGeom;1578} else if ((c == 's') && (strncmp(argv[1], "sizefrom", length) == 0)1579&& (length >= 2)) {1580if ((argc != 3) && (argc != 4)) {1581Tcl_AppendResult(interp, "wrong # arguments: must be \"",1582argv[0], " sizefrom window ?user|program?\"",1583(char *) NULL);1584return TCL_ERROR;1585}1586if (argc == 3) {1587if (wmPtr->sizeHintsFlags & USSize) {1588interp->result = "user";1589} else if (wmPtr->sizeHintsFlags & PSize) {1590interp->result = "program";1591}1592return TCL_OK;1593}1594if (*argv[3] == '\0') {1595wmPtr->sizeHintsFlags &= ~(USSize|PSize);1596} else {1597c = argv[3][0];1598length = strlen(argv[3]);1599if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) {1600wmPtr->sizeHintsFlags &= ~PSize;1601wmPtr->sizeHintsFlags |= USSize;1602} else if ((c == 'p')1603&& (strncmp(argv[3], "program", length) == 0)) {1604wmPtr->sizeHintsFlags &= ~USSize;1605wmPtr->sizeHintsFlags |= PSize;1606} else {1607Tcl_AppendResult(interp, "bad argument \"", argv[3],1608"\": must be program or user", (char *) NULL);1609return TCL_ERROR;1610}1611}1612wmPtr->flags |= WM_UPDATE_SIZE_HINTS;1613goto updateGeom;1614} else if ((c == 's') && (strncmp(argv[1], "state", length) == 0)1615&& (length >= 2)) {1616if (argc != 3) {1617Tcl_AppendResult(interp, "wrong # arguments: must be \"",1618argv[0], " state window\"", (char *) NULL);1619return TCL_ERROR;1620}1621if (wmPtr->iconFor != NULL) {1622interp->result = "icon";1623} else if (wmPtr->withdrawn) {1624interp->result = "withdrawn";1625} else if (Tk_IsMapped((Tk_Window) winPtr)1626|| ((wmPtr->flags & WM_NEVER_MAPPED)1627&& (wmPtr->hints.initial_state == NormalState))) {1628interp->result = "normal";1629} else {1630interp->result = "iconic";1631}1632} else if ((c == 't') && (strncmp(argv[1], "title", length) == 0)1633&& (length >= 2)) {1634if (argc > 4) {1635Tcl_AppendResult(interp, "wrong # arguments: must be \"",1636argv[0], " title window ?newTitle?\"", (char *) NULL);1637return TCL_ERROR;1638}1639if (argc == 3) {1640interp->result = (wmPtr->titleUid != NULL) ? wmPtr->titleUid1641: winPtr->nameUid;1642return TCL_OK;1643} else {1644wmPtr->titleUid = Tk_GetUid(argv[3]);1645#if !Ancient1646if (!(wmPtr->flags & WM_NEVER_MAPPED)) {1647XTextProperty textProp;16481649if (XStringListToTextProperty(&wmPtr->titleUid, 1,1650&textProp) != 0) {1651XSetWMName(winPtr->display, winPtr->window, &textProp);1652XFree((char *) textProp.value);1653}1654}1655#endif1656}1657} else if ((c == 't') && (strncmp(argv[1], "transient", length) == 0)1658&& (length >= 3)) {1659Tk_Window master;16601661if ((argc != 3) && (argc != 4)) {1662Tcl_AppendResult(interp, "wrong # arguments: must be \"",1663argv[0], " transient window ?master?\"", (char *) NULL);1664return TCL_ERROR;1665}1666if (argc == 3) {1667if (wmPtr->master != None) {1668interp->result = wmPtr->masterWindowName;1669}1670return TCL_OK;1671}1672if (argv[3][0] == '\0') {1673wmPtr->master = None;1674wmPtr->masterWindowName = NULL;1675} else {1676master = Tk_NameToWindow(interp, argv[3], tkwin);1677if (master == NULL) {1678return TCL_ERROR;1679}1680Tk_MakeWindowExist(master);1681wmPtr->master = Tk_WindowId(master);1682wmPtr->masterWindowName = Tk_PathName(master);1683}1684if (!(wmPtr->flags & WM_NEVER_MAPPED)) {1685XSetTransientForHint(winPtr->display, winPtr->window,1686wmPtr->master);1687}1688} else if ((c == 'w') && (strncmp(argv[1], "withdraw", length) == 0)) {1689if (argc != 3) {1690Tcl_AppendResult(interp, "wrong # arguments: must be \"",1691argv[0], " withdraw window\"", (char *) NULL);1692return TCL_ERROR;1693}1694if (wmPtr->iconFor != NULL) {1695Tcl_AppendResult(interp, "can't withdraw ", argv[2],1696": it is an icon for ", Tk_PathName(wmPtr->iconFor),1697(char *) NULL);1698return TCL_ERROR;1699}1700wmPtr->hints.initial_state = WithdrawnState;1701wmPtr->withdrawn = 1;1702if (wmPtr->flags & WM_NEVER_MAPPED) {1703return TCL_OK;1704}1705if (XWithdrawWindow(winPtr->display, winPtr->window,1706winPtr->screenNum) == 0) {1707interp->result =1708"couldn't send withdraw message to window manager";1709return TCL_ERROR;1710}1711WaitForMapNotify(winPtr, 0);1712} else {1713Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],1714"\": must be aspect, client, command, deiconify, ",1715"focusmodel, frame, geometry, grid, group, iconbitmap, ",1716"iconify, iconmask, iconname, iconposition, ",1717"iconwindow, maxsize, minsize, overrideredirect, ",1718"positionfrom, protocol, resizable, sizefrom, state, title, ",1719"transient, or withdraw",1720(char *) NULL);1721return TCL_ERROR;1722}1723return TCL_OK;17241725updateGeom:1726if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {1727Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);1728wmPtr->flags |= WM_UPDATE_PENDING;1729}1730return TCL_OK;1731}17321733/*1734*----------------------------------------------------------------------1735*1736* Tk_SetGrid --1737*1738* This procedure is invoked by a widget when it wishes to set a grid1739* coordinate system that controls the size of a top-level window.1740* It provides a C interface equivalent to the "wm grid" command and1741* is usually asscoiated with the -setgrid option.1742*1743* Results:1744* None.1745*1746* Side effects:1747* Grid-related information will be passed to the window manager, so1748* that the top-level window associated with tkwin will resize on1749* even grid units. If some other window already controls gridding1750* for the top-level window then this procedure call has no effect.1751*1752*----------------------------------------------------------------------1753*/17541755void1756Tk_SetGrid(tkwin, reqWidth, reqHeight, widthInc, heightInc)1757Tk_Window tkwin; /* Token for window. New window mgr info1758* will be posted for the top-level window1759* associated with this window. */1760int reqWidth; /* Width (in grid units) corresponding to1761* the requested geometry for tkwin. */1762int reqHeight; /* Height (in grid units) corresponding to1763* the requested geometry for tkwin. */1764int widthInc, heightInc; /* Pixel increments corresponding to a1765* change of one grid unit. */1766{1767TkWindow *winPtr = (TkWindow *) tkwin;1768register WmInfo *wmPtr;17691770/*1771* Find the top-level window for tkwin, plus the window manager1772* information.1773*/17741775while (!(winPtr->flags & TK_TOP_LEVEL)) {1776winPtr = winPtr->parentPtr;1777if (winPtr == NULL) {1778/*1779* The window is being deleted... just skip this operation.1780*/17811782return;1783}1784}1785wmPtr = winPtr->wmInfoPtr;17861787if ((wmPtr->gridWin != NULL) && (wmPtr->gridWin != tkwin)) {1788return;1789}17901791if ((wmPtr->reqGridWidth == reqWidth)1792&& (wmPtr->reqGridHeight == reqHeight)1793&& (wmPtr->widthInc == widthInc)1794&& (wmPtr->heightInc == heightInc)1795&& ((wmPtr->sizeHintsFlags & (PBaseSize|PResizeInc))1796== PBaseSize|PResizeInc)) {1797return;1798}17991800/*1801* If gridding was previously off, then forget about any window1802* size requests made by the user or via "wm geometry": these are1803* in pixel units and there's no easy way to translate them to1804* grid units since the new requested size of the top-level window in1805* pixels may not yet have been registered yet (it may filter up1806* the hierarchy in DoWhenIdle handlers). However, if the window1807* has never been mapped yet then just leave the window size alone:1808* assume that it is intended to be in grid units but just happened1809* to have been specified before this procedure was called.1810*/18111812if ((wmPtr->gridWin == NULL) && !(wmPtr->flags & WM_NEVER_MAPPED)) {1813wmPtr->width = -1;1814wmPtr->height = -1;1815}18161817/*1818* Set the new gridding information, and start the process of passing1819* all of this information to the window manager.1820*/18211822wmPtr->gridWin = tkwin;1823wmPtr->reqGridWidth = reqWidth;1824wmPtr->reqGridHeight = reqHeight;1825wmPtr->widthInc = widthInc;1826wmPtr->heightInc = heightInc;1827wmPtr->sizeHintsFlags |= PBaseSize|PResizeInc;1828wmPtr->flags |= WM_UPDATE_SIZE_HINTS;1829if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {1830Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);1831wmPtr->flags |= WM_UPDATE_PENDING;1832}1833}18341835/*1836*----------------------------------------------------------------------1837*1838* Tk_UnsetGrid --1839*1840* This procedure cancels the effect of a previous call1841* to Tk_SetGrid.1842*1843* Results:1844* None.1845*1846* Side effects:1847* If tkwin currently controls gridding for its top-level window,1848* gridding is cancelled for that top-level window; if some other1849* window controls gridding then this procedure has no effect.1850*1851*----------------------------------------------------------------------1852*/18531854void1855Tk_UnsetGrid(tkwin)1856Tk_Window tkwin; /* Token for window that is currently1857* controlling gridding. */1858{1859TkWindow *winPtr = (TkWindow *) tkwin;1860register WmInfo *wmPtr;18611862/*1863* Find the top-level window for tkwin, plus the window manager1864* information.1865*/18661867while (!(winPtr->flags & TK_TOP_LEVEL)) {1868winPtr = winPtr->parentPtr;1869if (winPtr == NULL) {1870/*1871* The window is being deleted... just skip this operation.1872*/18731874return;1875}1876}1877wmPtr = winPtr->wmInfoPtr;1878if (tkwin != wmPtr->gridWin) {1879return;1880}18811882wmPtr->gridWin = NULL;1883wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc);1884if (wmPtr->width != -1) {1885wmPtr->width = winPtr->reqWidth + (wmPtr->width1886- wmPtr->reqGridWidth)*wmPtr->widthInc;1887wmPtr->height = winPtr->reqHeight + (wmPtr->height1888- wmPtr->reqGridHeight)*wmPtr->heightInc;1889}1890wmPtr->widthInc = 1;1891wmPtr->heightInc = 1;18921893wmPtr->flags |= WM_UPDATE_SIZE_HINTS;1894if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {1895Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);1896wmPtr->flags |= WM_UPDATE_PENDING;1897}1898}18991900/*1901*----------------------------------------------------------------------1902*1903* ConfigureEvent --1904*1905* This procedure is called to handle ConfigureNotify events on1906* top-level windows.1907*1908* Results:1909* None.1910*1911* Side effects:1912* Information gets updated in the WmInfo structure for the window.1913*1914*----------------------------------------------------------------------1915*/19161917static void1918ConfigureEvent(winPtr, configEventPtr)1919TkWindow *winPtr; /* Top-level window. */1920XConfigureEvent *configEventPtr; /* Event that just occurred for1921* winPtr. */1922{1923register WmInfo *wmPtr = winPtr->wmInfoPtr;19241925/*1926* Update size information from the event. There are a couple of1927* tricky points here:1928*1929* 1. If the user changed the size externally then set wmPtr->width1930* and wmPtr->height just as if a "wm geometry" command had been1931* invoked with the same information.1932* 2. However, if the size is changing in response to a request1933* coming from us (WM_SYNC_PENDING is set), then don't set wmPtr->width1934* or wmPtr->height if they were previously -1 (otherwise the1935* window will stop tracking geometry manager requests).1936*/19371938if (((winPtr->changes.width != configEventPtr->width)1939|| (winPtr->changes.height != configEventPtr->height))1940&& !(wmPtr->flags & WM_SYNC_PENDING)){1941if (wmTracing) {1942printf("TopLevelEventProc: user changed %s size to %dx%d\n",1943winPtr->pathName, configEventPtr->width,1944configEventPtr->height);1945}1946if ((wmPtr->width == -1)1947&& (configEventPtr->width == winPtr->reqWidth)) {1948/*1949* Don't set external width, since the user didn't change it1950* from what the widgets asked for.1951*/1952} else {1953if (wmPtr->gridWin != NULL) {1954wmPtr->width = wmPtr->reqGridWidth1955+ (configEventPtr->width1956- winPtr->reqWidth)/wmPtr->widthInc;1957if (wmPtr->width < 0) {1958wmPtr->width = 0;1959}1960} else {1961wmPtr->width = configEventPtr->width;1962}1963}1964if ((wmPtr->height == -1)1965&& (configEventPtr->height == winPtr->reqHeight)) {1966/*1967* Don't set external height, since the user didn't change it1968* from what the widgets asked for.1969*/1970} else {1971if (wmPtr->gridWin != NULL) {1972wmPtr->height = wmPtr->reqGridHeight1973+ (configEventPtr->height1974- winPtr->reqHeight)/wmPtr->heightInc;1975if (wmPtr->height < 0) {1976wmPtr->height = 0;1977}1978} else {1979wmPtr->height = configEventPtr->height;1980}1981}1982wmPtr->configWidth = configEventPtr->width;1983wmPtr->configHeight = configEventPtr->height;1984}19851986if (wmTracing) {1987printf("ConfigureEvent: %s x = %d y = %d, width = %d, height = %d",1988winPtr->pathName, configEventPtr->x, configEventPtr->y,1989configEventPtr->width, configEventPtr->height);1990printf(" send_event = %d, serial = %ld\n", configEventPtr->send_event,1991configEventPtr->serial);1992}1993winPtr->changes.width = configEventPtr->width;1994winPtr->changes.height = configEventPtr->height;1995winPtr->changes.border_width = configEventPtr->border_width;1996winPtr->changes.sibling = configEventPtr->above;1997winPtr->changes.stack_mode = Above;19981999/*2000* Reparenting window managers make life difficult. If the2001* window manager reparents a top-level window then the x and y2002* information that comes in events for the window is wrong:2003* it gives the location of the window inside its decorative2004* parent, rather than the location of the window in root2005* coordinates, which is what we want. Window managers2006* are supposed to send synthetic events with the correct2007* information, but ICCCM doesn't require them to do this2008* under all conditions, and the information provided doesn't2009* include everything we need here. So, the code below2010* maintains a bunch of information about the parent window.2011* If the window hasn't been reparented, we pretend that2012* there is a parent shrink-wrapped around the window.2013*/20142015if ((wmPtr->reparent == None) || !ComputeReparentGeometry(winPtr)) {2016wmPtr->parentWidth = configEventPtr->width2017+ 2*configEventPtr->border_width;2018wmPtr->parentHeight = configEventPtr->height2019+ 2*configEventPtr->border_width;2020winPtr->changes.x = wmPtr->x = configEventPtr->x;2021winPtr->changes.y = wmPtr->y = configEventPtr->y;2022if (wmPtr->flags & WM_NEGATIVE_X) {2023wmPtr->x = wmPtr->vRootWidth - (wmPtr->x + wmPtr->parentWidth);2024}2025if (wmPtr->flags & WM_NEGATIVE_Y) {2026wmPtr->y = wmPtr->vRootHeight - (wmPtr->y + wmPtr->parentHeight);2027}2028}2029}20302031/*2032*----------------------------------------------------------------------2033*2034* ReparentEvent --2035*2036* This procedure is called to handle ReparentNotify events on2037* top-level windows.2038*2039* Results:2040* None.2041*2042* Side effects:2043* Information gets updated in the WmInfo structure for the window.2044*2045*----------------------------------------------------------------------2046*/20472048static void2049ReparentEvent(winPtr, reparentEventPtr)2050TkWindow *winPtr; /* Top-level window. */2051XReparentEvent *reparentEventPtr; /* Event that just occurred for2052* winPtr. */2053{2054register WmInfo *wmPtr = winPtr->wmInfoPtr;2055Window vRoot, ancestor, *children, dummy2, *virtualRootPtr;2056Atom actualType;2057int actualFormat;2058unsigned long numItems, bytesAfter;2059unsigned int dummy;2060Tk_ErrorHandler handler;20612062/*2063* Identify the root window for winPtr. This is tricky because of2064* virtual root window managers like tvtwm. If the window has a2065* property named __SWM_ROOT or __WM_ROOT then this property gives2066* the id for a virtual root window that should be used instead of2067* the root window of the screen.2068*/20692070vRoot = RootWindow(winPtr->display, winPtr->screenNum);2071wmPtr->vRoot = None;2072handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,2073(Tk_ErrorProc *) NULL, (ClientData) NULL);2074if (((XGetWindowProperty(winPtr->display, winPtr->window,2075Tk_InternAtom((Tk_Window) winPtr, "__WM_ROOT"), 0, (long) 1,2076False, XA_WINDOW, &actualType, &actualFormat, &numItems,2077&bytesAfter, (unsigned char **) &virtualRootPtr) == Success)2078&& (actualType == XA_WINDOW))2079|| ((XGetWindowProperty(winPtr->display, winPtr->window,2080Tk_InternAtom((Tk_Window) winPtr, "__SWM_ROOT"), 0, (long) 1,2081False, XA_WINDOW, &actualType, &actualFormat, &numItems,2082&bytesAfter, (unsigned char **) &virtualRootPtr) == Success)2083&& (actualType == XA_WINDOW))) {2084if ((actualFormat == 32) && (numItems == 1)) {2085vRoot = wmPtr->vRoot = *virtualRootPtr;2086} else if (wmTracing) {2087printf("%s format %d numItems %ld\n",2088"ReparentEvent got bogus VROOT property:", actualFormat,2089numItems);2090}2091XFree((char *) virtualRootPtr);2092}2093Tk_DeleteErrorHandler(handler);20942095if (wmTracing) {2096printf("ReparentEvent: %s reparented to 0x%x, vRoot = 0x%x\n",2097winPtr->pathName, (unsigned int) reparentEventPtr->parent,2098(unsigned int) vRoot);2099}21002101/*2102* Fetch correct geometry information for the new virtual root.2103*/21042105UpdateVRootGeometry(wmPtr);21062107/*2108* If the window's new parent is the root window, then mark it as2109* no longer reparented.2110*/21112112if (reparentEventPtr->parent == vRoot) {2113noReparent:2114wmPtr->reparent = None;2115wmPtr->parentWidth = winPtr->changes.width2116+ 2*winPtr->changes.border_width;2117wmPtr->parentHeight = winPtr->changes.height2118+ 2*winPtr->changes.border_width;2119wmPtr->xInParent = wmPtr->yInParent = 0;2120winPtr->changes.x = reparentEventPtr->x;2121winPtr->changes.y = reparentEventPtr->y;2122return;2123}21242125/*2126* Search up the window hierarchy to find the ancestor of this2127* window that is just below the (virtual) root. This is tricky2128* because it's possible that things have changed since the event2129* was generated so that the ancestry indicated by the event no2130* longer exists. If this happens then an error will occur and2131* we just discard the event (there will be a more up-to-date2132* ReparentNotify event coming later).2133*/21342135handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,2136(Tk_ErrorProc *) NULL, (ClientData) NULL);2137wmPtr->reparent = reparentEventPtr->parent;2138while (1) {2139if (XQueryTree(winPtr->display, wmPtr->reparent, &dummy2, &ancestor,2140&children, &dummy) == 0) {2141Tk_DeleteErrorHandler(handler);2142goto noReparent;2143}2144XFree((char *) children);2145if ((ancestor == vRoot) ||2146(ancestor == RootWindow(winPtr->display, winPtr->screenNum))) {2147break;2148}2149wmPtr->reparent = ancestor;2150}2151Tk_DeleteErrorHandler(handler);21522153if (!ComputeReparentGeometry(winPtr)) {2154goto noReparent;2155}2156}21572158/*2159*----------------------------------------------------------------------2160*2161* ComputeReparentGeometry --2162*2163* This procedure is invoked to recompute geometry information2164* related to a reparented top-level window, such as the position2165* and total size of the parent and the position within it of2166* the top-level window.2167*2168* Results:2169* The return value is 1 if everything completed successfully2170* and 0 if an error occurred while querying information about2171* winPtr's parents. In this case winPtr is marked as no longer2172* being reparented.2173*2174* Side effects:2175* Geometry information in winPtr and winPtr->wmPtr gets updated.2176*2177*----------------------------------------------------------------------2178*/21792180static int2181ComputeReparentGeometry(winPtr)2182TkWindow *winPtr; /* Top-level window whose reparent info2183* is to be recomputed. */2184{2185register WmInfo *wmPtr = winPtr->wmInfoPtr;2186int width, height, bd;2187unsigned int dummy;2188int xOffset, yOffset, x, y;2189Window dummy2;2190Status status;2191Tk_ErrorHandler handler;21922193handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,2194(Tk_ErrorProc *) NULL, (ClientData) NULL);2195(void) XTranslateCoordinates(winPtr->display, winPtr->window,2196wmPtr->reparent, 0, 0, &xOffset, &yOffset, &dummy2);2197status = XGetGeometry(winPtr->display, wmPtr->reparent,2198&dummy2, &x, &y, (unsigned int *) &width,2199(unsigned int *) &height, (unsigned int *) &bd, &dummy);2200Tk_DeleteErrorHandler(handler);2201if (status == 0) {2202/*2203* It appears that the reparented parent went away and2204* no-one told us. Reset the window to indicate that2205* it's not reparented.2206*/2207wmPtr->reparent = None;2208wmPtr->xInParent = wmPtr->yInParent = 0;2209return 0;2210}2211wmPtr->xInParent = xOffset + bd - winPtr->changes.border_width;2212wmPtr->yInParent = yOffset + bd - winPtr->changes.border_width;2213wmPtr->parentWidth = width + 2*bd;2214wmPtr->parentHeight = height + 2*bd;22152216/*2217* Some tricky issues in updating wmPtr->x and wmPtr->y:2218*2219* 1. Don't update them if the event occurred because of something2220* we did (i.e. WM_SYNC_PENDING and WM_MOVE_PENDING are both set).2221* This is because window managers treat coords differently than Tk,2222* and no two window managers are alike. If the window manager moved2223* the window because we told it to, remember the coordinates we told2224* it, not the ones it actually moved it to. This allows us to move2225* the window back to the same coordinates later and get the same2226* result. Without this check, windows can "walk" across the screen2227* under some conditions.2228*2229* 2. Don't update wmPtr->x and wmPtr->y unless winPtr->changes.x2230* or winPtr->changes.y has changed (otherwise a size change can2231* spoof us into thinking that the position changed too and defeat2232* the intent of (1) above.2233*2234* 3. Ignore size changes coming from the window system if we're2235* about to change the size ourselves but haven't seen the event for2236* it yet: our size change is supposed to take priority.2237*/22382239if (!(wmPtr->flags & WM_MOVE_PENDING)2240&& ((winPtr->changes.x != (x + wmPtr->xInParent))2241|| (winPtr->changes.y != (y + wmPtr->yInParent)))) {2242wmPtr->x = x;2243if (wmPtr->flags & WM_NEGATIVE_X) {2244wmPtr->x = wmPtr->vRootWidth - (wmPtr->x + wmPtr->parentWidth);2245}2246wmPtr->y = y;2247if (wmPtr->flags & WM_NEGATIVE_Y) {2248wmPtr->y = wmPtr->vRootHeight - (wmPtr->y + wmPtr->parentHeight);2249}2250}22512252winPtr->changes.x = x + wmPtr->xInParent;2253winPtr->changes.y = y + wmPtr->yInParent;2254if (wmTracing) {2255printf("winPtr coords %d,%d, wmPtr coords %d,%d, offsets %d %d\n",2256winPtr->changes.x, winPtr->changes.y, wmPtr->x, wmPtr->y,2257wmPtr->xInParent, wmPtr->yInParent);2258}2259return 1;2260}22612262/*2263*----------------------------------------------------------------------2264*2265* TopLevelEventProc --2266*2267* This procedure is invoked when a top-level (or other externally-2268* managed window) is restructured in any way.2269*2270* Results:2271* None.2272*2273* Side effects:2274* Tk's internal data structures for the window get modified to2275* reflect the structural change.2276*2277*----------------------------------------------------------------------2278*/22792280static void2281TopLevelEventProc(clientData, eventPtr)2282ClientData clientData; /* Window for which event occurred. */2283XEvent *eventPtr; /* Event that just happened. */2284{2285register TkWindow *winPtr = (TkWindow *) clientData;22862287winPtr->wmInfoPtr->flags |= WM_VROOT_OFFSET_STALE;2288if (eventPtr->type == DestroyNotify) {2289Tk_ErrorHandler handler;22902291if (!(winPtr->flags & TK_ALREADY_DEAD)) {2292/*2293* A top-level window was deleted externally (e.g., by the window2294* manager). This is probably not a good thing, but cleanup as2295* best we can. The error handler is needed because2296* Tk_DestroyWindow will try to destroy the window, but of course2297* it's already gone.2298*/22992300handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,2301(Tk_ErrorProc *) NULL, (ClientData) NULL);2302Tk_DestroyWindow((Tk_Window) winPtr);2303Tk_DeleteErrorHandler(handler);2304}2305if (wmTracing) {2306printf("TopLevelEventProc: %s deleted\n", winPtr->pathName);2307}2308} else if (eventPtr->type == ConfigureNotify) {2309/*2310* Ignore the event if the window has never been mapped yet.2311* Such an event occurs only in weird cases like changing the2312* internal border width of a top-level window, which results2313* in a synthetic Configure event. These events are not relevant2314* to us, and if we process them confusion may result (e.g. we2315* may conclude erroneously that the user repositioned or resized2316* the window).2317*/23182319if (!(winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED)) {2320ConfigureEvent(winPtr, &eventPtr->xconfigure);2321}2322} else if (eventPtr->type == MapNotify) {2323winPtr->flags |= TK_MAPPED;2324if (wmTracing) {2325printf("TopLevelEventProc: %s mapped\n", winPtr->pathName);2326}2327} else if (eventPtr->type == UnmapNotify) {2328winPtr->flags &= ~TK_MAPPED;2329if (wmTracing) {2330printf("TopLevelEventProc: %s unmapped\n", winPtr->pathName);2331}2332} else if (eventPtr->type == ReparentNotify) {2333ReparentEvent(winPtr, &eventPtr->xreparent);2334}2335}23362337/*2338*----------------------------------------------------------------------2339*2340* TopLevelReqProc --2341*2342* This procedure is invoked by the geometry manager whenever2343* the requested size for a top-level window is changed.2344*2345* Results:2346* None.2347*2348* Side effects:2349* Arrange for the window to be resized to satisfy the request2350* (this happens as a when-idle action).2351*2352*----------------------------------------------------------------------2353*/23542355/* ARGSUSED */2356static void2357TopLevelReqProc(dummy, tkwin)2358ClientData dummy; /* Not used. */2359Tk_Window tkwin; /* Information about window. */2360{2361TkWindow *winPtr = (TkWindow *) tkwin;2362WmInfo *wmPtr;23632364wmPtr = winPtr->wmInfoPtr;2365wmPtr->flags |= WM_UPDATE_SIZE_HINTS;2366if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {2367Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);2368wmPtr->flags |= WM_UPDATE_PENDING;2369}23702371/*2372* If the window isn't being positioned by its upper left corner2373* then we have to move it as well.2374*/23752376if (wmPtr->flags & (WM_NEGATIVE_X | WM_NEGATIVE_Y)) {2377wmPtr->flags |= WM_MOVE_PENDING;2378}2379}23802381/*2382*----------------------------------------------------------------------2383*2384* UpdateGeometryInfo --2385*2386* This procedure is invoked when a top-level window is first2387* mapped, and also as a when-idle procedure, to bring the2388* geometry and/or position of a top-level window back into2389* line with what has been requested by the user and/or widgets.2390* This procedure doesn't return until the window manager has2391* responded to the geometry change.2392*2393* Results:2394* None.2395*2396* Side effects:2397* The window's size and location may change, unless the WM prevents2398* that from happening.2399*2400*----------------------------------------------------------------------2401*/24022403static void2404UpdateGeometryInfo(clientData)2405ClientData clientData; /* Pointer to the window's record. */2406{2407register TkWindow *winPtr = (TkWindow *) clientData;2408register WmInfo *wmPtr = winPtr->wmInfoPtr;2409int x, y, width, height;2410unsigned long serial;24112412wmPtr->flags &= ~WM_UPDATE_PENDING;24132414/*2415* Compute the new size for the top-level window. See the2416* user documentation for details on this, but the size2417* requested depends on (a) the size requested internally2418* by the window's widgets, (b) the size requested by the2419* user in a "wm geometry" command or via wm-based interactive2420* resizing (if any), and (c) whether or not the window is2421* gridded. Don't permit sizes <= 0 because this upsets2422* the X server.2423*/24242425if (wmPtr->width == -1) {2426width = winPtr->reqWidth;2427} else if (wmPtr->gridWin != NULL) {2428width = winPtr->reqWidth2429+ (wmPtr->width - wmPtr->reqGridWidth)*wmPtr->widthInc;2430} else {2431width = wmPtr->width;2432}2433if (width <= 0) {2434width = 1;2435}2436if (wmPtr->height == -1) {2437height = winPtr->reqHeight;2438} else if (wmPtr->gridWin != NULL) {2439height = winPtr->reqHeight2440+ (wmPtr->height - wmPtr->reqGridHeight)*wmPtr->heightInc;2441} else {2442height = wmPtr->height;2443}2444if (height <= 0) {2445height = 1;2446}24472448/*2449* Compute the new position for the upper-left pixel of the window's2450* decorative frame. This is tricky, because we need to include the2451* border widths supplied by a reparented parent in this calculation,2452* but can't use the parent's current overall size since that may2453* change as a result of this code.2454*/24552456if (wmPtr->flags & WM_NEGATIVE_X) {2457x = wmPtr->vRootWidth - wmPtr->x2458- (width + (wmPtr->parentWidth - winPtr->changes.width));2459} else {2460x = wmPtr->x;2461}2462if (wmPtr->flags & WM_NEGATIVE_Y) {2463y = wmPtr->vRootHeight - wmPtr->y2464- (height + (wmPtr->parentHeight - winPtr->changes.height));2465} else {2466y = wmPtr->y;2467}24682469/*2470* If the window's size is going to change and the window is2471* supposed to not be resizable by the user, then we have to2472* update the size hints. There may also be a size-hint-update2473* request pending from somewhere else, too.2474*/24752476if (((width != winPtr->changes.width)2477|| (height != winPtr->changes.height))2478&& (wmPtr->gridWin == NULL)2479&& ((wmPtr->sizeHintsFlags & (PMinSize|PMaxSize)) == 0)) {2480wmPtr->flags |= WM_UPDATE_SIZE_HINTS;2481}2482if (wmPtr->flags & WM_UPDATE_SIZE_HINTS) {2483UpdateSizeHints(winPtr);2484}24852486/*2487* Reconfigure the window if it isn't already configured correctly.2488* A few tricky points:2489*2490* 1. Sometimes the window manager will give us a different size2491* than we asked for (e.g. mwm has a minimum size for windows), so2492* base the size check on what we *asked for* last time, not what we2493* got.2494* 2. Can't just reconfigure always, because we may not get a2495* ConfigureNotify event back if nothing changed, so2496* WaitForConfigureNotify will hang a long time.2497* 3. Don't move window unless a new position has been requested for2498* it. This is because of "features" in some window managers (e.g.2499* twm, as of 4/24/91) where they don't interpret coordinates2500* according to ICCCM. Moving a window to its current location may2501* cause it to shift position on the screen.2502*/25032504serial = NextRequest(winPtr->display);2505if (wmPtr->flags & WM_MOVE_PENDING) {2506wmPtr->configWidth = width;2507wmPtr->configHeight = height;2508if (wmTracing) {2509printf("UpdateGeometryInfo moving to %d %d, resizing to %d x %d,\n",2510x, y, width, height);2511}2512Tk_MoveResizeWindow((Tk_Window) winPtr, x, y, width, height);2513} else if ((width != wmPtr->configWidth)2514|| (height != wmPtr->configHeight)) {2515wmPtr->configWidth = width;2516wmPtr->configHeight = height;2517if (wmTracing) {2518printf("UpdateGeometryInfo resizing to %d x %d\n", width, height);2519}2520Tk_ResizeWindow((Tk_Window) winPtr, width, height);2521} else {2522return;2523}25242525/*2526* Wait for the configure operation to complete. Don't need to do2527* this, however, if the window is about to be mapped: it will be2528* taken care of elsewhere.2529*/25302531if (!(wmPtr->flags & WM_ABOUT_TO_MAP)) {2532WaitForConfigureNotify(winPtr, serial);2533}2534}25352536/*2537*--------------------------------------------------------------2538*2539* UpdateSizeHints --2540*2541* This procedure is called to update the window manager's2542* size hints information from the information in a WmInfo2543* structure.2544*2545* Results:2546* None.2547*2548* Side effects:2549* Properties get changed for winPtr.2550*2551*--------------------------------------------------------------2552*/25532554static void2555UpdateSizeHints(winPtr)2556TkWindow *winPtr;2557{2558register WmInfo *wmPtr = winPtr->wmInfoPtr;2559XSizeHints *hintsPtr;2560int maxWidth, maxHeight;25612562wmPtr->flags &= ~WM_UPDATE_SIZE_HINTS;25632564hintsPtr = XAllocSizeHints();2565if (hintsPtr == NULL) {2566return;2567}25682569/*2570* Compute the pixel-based sizes for the various fields in the2571* size hints structure, based on the grid-based sizes in2572* our structure.2573*/25742575GetMaxSize(wmPtr, &maxWidth, &maxHeight);2576if (wmPtr->gridWin != NULL) {2577#if PBaseSize2578hintsPtr->base_width = winPtr->reqWidth2579- (wmPtr->reqGridWidth * wmPtr->widthInc);2580if (hintsPtr->base_width < 0) {2581hintsPtr->base_width = 0;2582}2583hintsPtr->base_height = winPtr->reqHeight2584- (wmPtr->reqGridHeight * wmPtr->heightInc);2585if (hintsPtr->base_height < 0) {2586hintsPtr->base_height = 0;2587}2588#endif2589hintsPtr->min_width =2590#if PBaseSize2591hintsPtr->base_width +2592#endif2593(wmPtr->minWidth * wmPtr->widthInc);2594hintsPtr->min_height =2595#if PBaseSize2596hintsPtr->base_height +2597#endif2598(wmPtr->minHeight * wmPtr->heightInc);2599hintsPtr->max_width =2600#if PBaseSize2601hintsPtr->base_width +2602#endif2603(maxWidth * wmPtr->widthInc);2604hintsPtr->max_height =2605#if PBaseSize2606hintsPtr->base_height +2607#endif2608(maxHeight * wmPtr->heightInc);2609} else {2610hintsPtr->min_width = wmPtr->minWidth;2611hintsPtr->min_height = wmPtr->minHeight;2612hintsPtr->max_width = maxWidth;2613hintsPtr->max_height = maxHeight;2614#if PBaseSize2615hintsPtr->base_width = 0;2616hintsPtr->base_height = 0;2617#endif2618}2619hintsPtr->width_inc = wmPtr->widthInc;2620hintsPtr->height_inc = wmPtr->heightInc;2621hintsPtr->min_aspect.x = wmPtr->minAspect.x;2622hintsPtr->min_aspect.y = wmPtr->minAspect.y;2623hintsPtr->max_aspect.x = wmPtr->maxAspect.x;2624hintsPtr->max_aspect.y = wmPtr->maxAspect.y;2625#if PWinGravity2626hintsPtr->win_gravity = wmPtr->gravity;2627#endif2628hintsPtr->flags = wmPtr->sizeHintsFlags | PMinSize | PMaxSize;26292630/*2631* If the window isn't supposed to be resizable, then set the2632* minimum and maximum dimensions to be the same.2633*/26342635if (wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) {2636if (wmPtr->width >= 0) {2637hintsPtr->min_width = wmPtr->width;2638} else {2639hintsPtr->min_width = winPtr->reqWidth;2640}2641hintsPtr->max_width = hintsPtr->min_width;2642}2643if (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE) {2644if (wmPtr->height >= 0) {2645hintsPtr->min_height = wmPtr->height;2646} else {2647hintsPtr->min_height = winPtr->reqHeight;2648}2649hintsPtr->max_height = hintsPtr->min_height;2650}26512652XSetWMNormalHints(winPtr->display, winPtr->window, hintsPtr);26532654XFree((char *) hintsPtr);2655}26562657/*2658*----------------------------------------------------------------------2659*2660* WaitForConfigureNotify --2661*2662* This procedure is invoked in order to synchronize with the2663* window manager. It waits for a ConfigureNotify event to2664* arrive, signalling that the window manager has seen an attempt2665* on our part to move or resize a top-level window.2666*2667* Results:2668* None.2669*2670* Side effects:2671* Delays the execution of the process until a ConfigureNotify event2672* arrives with serial number at least as great as serial. This2673* is useful for two reasons:2674*2675* 1. It's important to distinguish ConfigureNotify events that are2676* coming in response to a request we've made from those generated2677* spontaneously by the user. The reason for this is that if the2678* user resizes the window we take that as an order to ignore2679* geometry requests coming from inside the window hierarchy. If2680* we accidentally interpret a response to our request as a2681* user-initiated action, the window will stop responding to2682* new geometry requests. To make this distinction, (a) this2683* procedure sets a flag for TopLevelEventProc to indicate that2684* we're waiting to sync with the wm, and (b) all changes to2685* the size of a top-level window are followed by calls to this2686* procedure.2687* 2. Races and confusion can come about if there are multiple2688* operations outstanding at a time (e.g. two different resizes2689* of the top-level window: it's hard to tell which of the2690* ConfigureNotify events coming back is for which request).2691* While waiting, all events covered by StructureNotifyMask are2692* processed and all others are deferred.2693*2694*----------------------------------------------------------------------2695*/26962697static void2698WaitForConfigureNotify(winPtr, serial)2699TkWindow *winPtr; /* Top-level window for which we want2700* to see a ConfigureNotify. */2701unsigned long serial; /* Serial number of resize request. Want to2702* be sure wm has seen this. */2703{2704WmInfo *wmPtr = winPtr->wmInfoPtr;2705XEvent event;2706int diff, code;2707int gotConfig = 0;27082709/*2710* One more tricky detail about this procedure. In some cases the2711* window manager will decide to ignore a configure request (e.g.2712* because it thinks the window is already in the right place).2713* To avoid hanging in this situation, only wait for a few seconds,2714* then give up.2715*/27162717while (!gotConfig) {2718wmPtr->flags |= WM_SYNC_PENDING;2719code = WaitForEvent(winPtr->display, winPtr->window, ConfigureNotify,2720&event);2721wmPtr->flags &= ~WM_SYNC_PENDING;2722if (code != TCL_OK) {2723if (wmTracing) {2724printf("WaitForConfigureNotify giving up on %s\n",2725winPtr->pathName);2726}2727break;2728}2729diff = event.xconfigure.serial - serial;2730if (diff >= 0) {2731gotConfig = 1;2732}2733}2734wmPtr->flags &= ~WM_MOVE_PENDING;2735if (wmTracing) {2736printf("WaitForConfigureNotify finished with %s, serial %ld\n",2737winPtr->pathName, serial);2738}2739}27402741/*2742*----------------------------------------------------------------------2743*2744* WaitForEvent --2745*2746* This procedure is used by WaitForConfigureNotify and2747* WaitForMapNotify to wait for an event of a certain type2748* to arrive.2749*2750* Results:2751* Under normal conditions, TCL_OK is returned and an event for2752* display and window that matches "mask" is stored in *eventPtr.2753* This event has already been processed by Tk before this procedure2754* returns. If a long time goes by with no event of the right type2755* arriving, or if an error occurs while waiting for the event to2756* arrive, then TCL_ERROR is returned.2757*2758* Side effects:2759* While waiting for the desired event to occur, Configurenotify2760* events for window are processed, as are all ReparentNotify events,2761*2762*----------------------------------------------------------------------2763*/27642765static int2766WaitForEvent(display, window, type, eventPtr)2767Display *display; /* Display event is coming from. */2768Window window; /* Window for which event is desired. */2769int type; /* Type of event that is wanted. */2770XEvent *eventPtr; /* Place to store event. */2771{2772#define TIMEOUT_MS 20002773WaitRestrictInfo info;2774Tk_RestrictProc *oldRestrictProc;2775ClientData oldRestrictData;27762777/*2778* Set up an event filter to select just the events we want, and2779* a timer handler, then wait for events until we get the event2780* we want or a timeout happens.2781*/27822783info.display = display;2784info.window = window;2785info.type = type;2786info.eventPtr = eventPtr;2787info.foundEvent = 0;2788info.timeout = 0;2789oldRestrictProc = Tk_RestrictEvents(WaitRestrictProc, (ClientData) &info,2790&oldRestrictData);2791Tcl_CreateModalTimeout(TIMEOUT_MS, WaitTimeoutProc,2792(ClientData) &info);2793while (1) {2794Tcl_DoOneEvent(TCL_WINDOW_EVENTS);2795if (info.foundEvent) {2796break;2797}2798if (info.timeout) {2799break;2800}2801}2802Tcl_DeleteModalTimeout(WaitTimeoutProc, (ClientData) &info);2803(void) Tk_RestrictEvents(oldRestrictProc, oldRestrictData,2804&oldRestrictData);2805if (info.foundEvent) {2806return TCL_OK;2807}2808return TCL_ERROR;2809}28102811/*2812*----------------------------------------------------------------------2813*2814* WaitRestrictProc --2815*2816* This procedure is a Tk_RestrictProc that is used to filter2817* events while WaitForEvent is active.2818*2819* Results:2820* Returns TK_PROCESS_EVENT if the right event is found. Also2821* returns TK_PROCESS_EVENT if any ReparentNotify event is found2822* for window or if the event is a ConfigureNotify for window.2823* Otherwise returns TK_DEFER_EVENT.2824*2825* Side effects:2826* An event may get stored in the area indicated by the caller2827* of WaitForEvent.2828*2829*----------------------------------------------------------------------2830*/28312832static Tk_RestrictAction2833WaitRestrictProc(clientData, eventPtr)2834ClientData clientData; /* Pointer to WaitRestrictInfo structure. */2835XEvent *eventPtr; /* Event that is about to be handled. */2836{2837WaitRestrictInfo *infoPtr = (WaitRestrictInfo *) clientData;28382839if (eventPtr->type == ReparentNotify) {2840return TK_PROCESS_EVENT;2841}2842if ((eventPtr->xany.window != infoPtr->window)2843|| (eventPtr->xany.display != infoPtr->display)) {2844return TK_DEFER_EVENT;2845}2846if (eventPtr->type == infoPtr->type) {2847*infoPtr->eventPtr = *eventPtr;2848infoPtr->foundEvent = 1;2849return TK_PROCESS_EVENT;2850}2851if (eventPtr->type == ConfigureNotify) {2852return TK_PROCESS_EVENT;2853}2854return TK_DEFER_EVENT;2855}28562857/*2858*----------------------------------------------------------------------2859*2860* WaitTimeoutProc --2861*2862* This procedure is invoked as a timer handler when too much2863* time elapses during a call to WaitForEvent. It sets a flag2864* in a structure shared with WaitForEvent so that WaitForEvent2865* knows that it should return.2866*2867* Results:2868* None.2869*2870* Side effects:2871* The timeout field gest set in the WaitRestrictInfo structure.2872*2873*----------------------------------------------------------------------2874*/28752876static void2877WaitTimeoutProc(clientData)2878ClientData clientData; /* Pointer to WaitRestrictInfo structure. */2879{2880WaitRestrictInfo *infoPtr = (WaitRestrictInfo *) clientData;28812882infoPtr->timeout = 1;2883}28842885/*2886*----------------------------------------------------------------------2887*2888* WaitForMapNotify --2889*2890* This procedure is invoked in order to synchronize with the2891* window manager. It waits for the window's mapped state to2892* reach the value given by mapped.2893*2894* Results:2895* None.2896*2897* Side effects:2898* Delays the execution of the process until winPtr becomes mapped2899* or unmapped, depending on the "mapped" argument. This allows us2900* to synchronize with the window manager, and allows us to2901* identify changes in window size that come about when the window2902* manager first starts managing the window (as opposed to those2903* requested interactively by the user later). See the comments2904* for WaitForConfigureNotify and WM_SYNC_PENDING. While waiting,2905* all events covered by StructureNotifyMask are processed and all2906* others are deferred.2907*2908*----------------------------------------------------------------------2909*/29102911static void2912WaitForMapNotify(winPtr, mapped)2913TkWindow *winPtr; /* Top-level window for which we want2914* to see a particular mapping state. */2915int mapped; /* If non-zero, wait for window to become2916* mapped, otherwise wait for it to become2917* unmapped. */2918{2919WmInfo *wmPtr = winPtr->wmInfoPtr;2920XEvent event;2921int code;29222923while (1) {2924if (mapped) {2925if (winPtr->flags & TK_MAPPED) {2926break;2927}2928} else if (!(winPtr->flags & TK_MAPPED)) {2929break;2930}2931wmPtr->flags |= WM_SYNC_PENDING;2932code = WaitForEvent(winPtr->display, winPtr->window,2933mapped ? MapNotify : UnmapNotify, &event);2934wmPtr->flags &= ~WM_SYNC_PENDING;2935if (code != TCL_OK) {2936/*2937* There are some bizarre situations in which the window2938* manager can't respond or chooses not to (e.g. if we've2939* got a grab set it can't respond). If this happens then2940* just quit.2941*/29422943if (wmTracing) {2944printf("WaitForMapNotify giving up on %s\n", winPtr->pathName);2945}2946break;2947}2948}2949wmPtr->flags &= ~WM_MOVE_PENDING;2950if (wmTracing) {2951printf("WaitForMapNotify finished with %s\n", winPtr->pathName);2952}2953}29542955/*2956*--------------------------------------------------------------2957*2958* UpdateHints --2959*2960* This procedure is called to update the window manager's2961* hints information from the information in a WmInfo2962* structure.2963*2964* Results:2965* None.2966*2967* Side effects:2968* Properties get changed for winPtr.2969*2970*--------------------------------------------------------------2971*/29722973static void2974UpdateHints(winPtr)2975TkWindow *winPtr;2976{2977WmInfo *wmPtr = winPtr->wmInfoPtr;29782979if (wmPtr->flags & WM_NEVER_MAPPED) {2980return;2981}2982XSetWMHints(winPtr->display, winPtr->window, &wmPtr->hints);2983}29842985/*2986*--------------------------------------------------------------2987*2988* ParseGeometry --2989*2990* This procedure parses a geometry string and updates2991* information used to control the geometry of a top-level2992* window.2993*2994* Results:2995* A standard Tcl return value, plus an error message in2996* interp->result if an error occurs.2997*2998* Side effects:2999* The size and/or location of winPtr may change.3000*3001*--------------------------------------------------------------3002*/30033004static int3005ParseGeometry(interp, string, winPtr)3006Tcl_Interp *interp; /* Used for error reporting. */3007char *string; /* String containing new geometry. Has the3008* standard form "=wxh+x+y". */3009TkWindow *winPtr; /* Pointer to top-level window whose3010* geometry is to be changed. */3011{3012register WmInfo *wmPtr = winPtr->wmInfoPtr;3013int x, y, width, height, flags;3014char *end;3015register char *p = string;30163017/*3018* The leading "=" is optional.3019*/30203021if (*p == '=') {3022p++;3023}30243025/*3026* Parse the width and height, if they are present. Don't3027* actually update any of the fields of wmPtr until we've3028* successfully parsed the entire geometry string.3029*/30303031width = wmPtr->width;3032height = wmPtr->height;3033x = wmPtr->x;3034y = wmPtr->y;3035flags = wmPtr->flags;3036if (isdigit(UCHAR(*p))) {3037width = strtoul(p, &end, 10);3038p = end;3039if (*p != 'x') {3040goto error;3041}3042p++;3043if (!isdigit(UCHAR(*p))) {3044goto error;3045}3046height = strtoul(p, &end, 10);3047p = end;3048}30493050/*3051* Parse the X and Y coordinates, if they are present.3052*/30533054if (*p != '\0') {3055flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y);3056if (*p == '-') {3057flags |= WM_NEGATIVE_X;3058} else if (*p != '+') {3059goto error;3060}3061x = strtol(p+1, &end, 10);3062p = end;3063if (*p == '-') {3064flags |= WM_NEGATIVE_Y;3065} else if (*p != '+') {3066goto error;3067}3068y = strtol(p+1, &end, 10);3069if (*end != '\0') {3070goto error;3071}30723073/*3074* Assume that the geometry information came from the user,3075* unless an explicit source has been specified. Otherwise3076* most window managers assume that the size hints were3077* program-specified and they ignore them.3078*/30793080if ((wmPtr->sizeHintsFlags & (USPosition|PPosition)) == 0) {3081wmPtr->sizeHintsFlags |= USPosition;3082flags |= WM_UPDATE_SIZE_HINTS;3083}3084}30853086/*3087* Everything was parsed OK. Update the fields of *wmPtr and3088* arrange for the appropriate information to be percolated out3089* to the window manager at the next idle moment.3090*/30913092wmPtr->width = width;3093wmPtr->height = height;3094wmPtr->x = x;3095wmPtr->y = y;3096flags |= WM_MOVE_PENDING;3097wmPtr->flags = flags;30983099if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {3100Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);3101wmPtr->flags |= WM_UPDATE_PENDING;3102}3103return TCL_OK;31043105error:3106Tcl_AppendResult(interp, "bad geometry specifier \"",3107string, "\"", (char *) NULL);3108return TCL_ERROR;3109}31103111/*3112*----------------------------------------------------------------------3113*3114* Tk_GetRootCoords --3115*3116* Given a token for a window, this procedure traces through the3117* window's lineage to find the (virtual) root-window coordinates3118* corresponding to point (0,0) in the window.3119*3120* Results:3121* The locations pointed to by xPtr and yPtr are filled in with3122* the root coordinates of the (0,0) point in tkwin. If a virtual3123* root window is in effect for the window, then the coordinates3124* in the virtual root are returned.3125*3126* Side effects:3127* None.3128*3129*----------------------------------------------------------------------3130*/31313132void3133Tk_GetRootCoords(tkwin, xPtr, yPtr)3134Tk_Window tkwin; /* Token for window. */3135int *xPtr; /* Where to store x-displacement of (0,0). */3136int *yPtr; /* Where to store y-displacement of (0,0). */3137{3138int x, y;3139register TkWindow *winPtr = (TkWindow *) tkwin;31403141/*3142* Search back through this window's parents all the way to a3143* top-level window, combining the offsets of each window within3144* its parent.3145*/31463147x = y = 0;3148while (1) {3149x += winPtr->changes.x + winPtr->changes.border_width;3150y += winPtr->changes.y + winPtr->changes.border_width;3151if ((winPtr->flags & TK_TOP_LEVEL) || (winPtr->parentPtr == NULL)) {3152break;3153}3154winPtr = winPtr->parentPtr;3155}3156*xPtr = x;3157*yPtr = y;3158}31593160/*3161*----------------------------------------------------------------------3162*3163* Tk_CoordsToWindow --3164*3165* Given the (virtual) root coordinates of a point, this procedure3166* returns the token for the top-most window covering that point,3167* if there exists such a window in this application.3168*3169* Results:3170* The return result is either a token for the window corresponding3171* to rootX and rootY, or else NULL to indicate that there is no such3172* window.3173*3174* Side effects:3175* None.3176*3177*----------------------------------------------------------------------3178*/31793180Tk_Window3181Tk_CoordsToWindow(rootX, rootY, tkwin)3182int rootX, rootY; /* Coordinates of point in root window. If3183* a virtual-root window manager is in use,3184* these coordinates refer to the virtual3185* root, not the real root. */3186Tk_Window tkwin; /* Token for any window in application;3187* used to identify the display. */3188{3189Window rootChild, root, vRoot;3190int dummy1, dummy2;3191register WmInfo *wmPtr;3192register TkWindow *winPtr, *childPtr;3193TkWindow *nextPtr; /* Coordinates of highest child found so3194* far that contains point. */3195int x, y; /* Coordinates in winPtr. */3196int tmpx, tmpy, bd;31973198/*3199* Step 1: find any top-level window for the right screen.3200*/32013202while (!Tk_IsTopLevel(tkwin)) {3203tkwin = Tk_Parent(tkwin);3204}3205wmPtr = ((TkWindow *) tkwin)->wmInfoPtr;32063207/*3208* Step 2: find the window in the actual root that contains the3209* desired point. Special trick: if a virtual root window manager3210* is in use, there may be windows in both the true root (e.g.3211* pop-up menus) and in the virtual root; have to look in *both*3212* places.3213*/32143215UpdateVRootGeometry(wmPtr);3216root = RootWindowOfScreen(Tk_Screen(tkwin));3217if (XTranslateCoordinates(Tk_Display(tkwin), root, root,3218rootX + wmPtr->vRootX, rootY + wmPtr->vRootY,3219&dummy1, &dummy2, &rootChild) == False) {3220panic("Tk_CoordsToWindow get False return from XTranslateCoordinates");3221}32223223/*3224* Step 3: if the window we've found so far (a child of the root)3225* is the virtual root window, then look again to find the child of3226* the virtual root.3227*/32283229vRoot = ((TkWindow *) tkwin)->wmInfoPtr->vRoot;3230if ((vRoot != None) && (rootChild == vRoot)) {3231if (XTranslateCoordinates(Tk_Display(tkwin), vRoot, vRoot, rootX,3232rootY, &dummy1, &dummy2, &rootChild) == False) {3233panic("Tk_CoordsToWindow get False return from XTranslateCoordinates");3234}3235}3236for (wmPtr = firstWmPtr; ; wmPtr = wmPtr->nextPtr) {3237if (wmPtr == NULL) {3238return NULL;3239}3240if ((wmPtr->reparent == rootChild) || ((wmPtr->reparent == None)3241&& (wmPtr->winPtr->window == rootChild))) {3242break;3243}3244}3245winPtr = wmPtr->winPtr;3246if (winPtr->mainPtr != ((TkWindow *) tkwin)->mainPtr) {3247return NULL;3248}32493250/*3251* Step 4: work down through the hierarchy underneath this window.3252* At each level, scan through all the children to find the highest3253* one in the stacking order that contains the point. Then repeat3254* the whole process on that child.3255*/32563257x = rootX;3258y = rootY;3259while (1) {3260x -= winPtr->changes.x;3261y -= winPtr->changes.y;3262nextPtr = NULL;3263for (childPtr = winPtr->childList; childPtr != NULL;3264childPtr = childPtr->nextPtr) {3265if (!Tk_IsMapped(childPtr) || (childPtr->flags & TK_TOP_LEVEL)) {3266continue;3267}3268tmpx = x - childPtr->changes.x;3269tmpy = y - childPtr->changes.y;3270bd = childPtr->changes.border_width;3271if ((tmpx >= -bd) && (tmpy >= -bd)3272&& (tmpx < (childPtr->changes.width + bd))3273&& (tmpy < (childPtr->changes.height + bd))) {3274nextPtr = childPtr;3275}3276}3277if (nextPtr == NULL) {3278break;3279}3280winPtr = nextPtr;3281}3282return (Tk_Window) winPtr;3283}32843285/*3286*----------------------------------------------------------------------3287*3288* UpdateVRootGeometry --3289*3290* This procedure is called to update all the virtual root3291* geometry information in wmPtr.3292*3293* Results:3294* None.3295*3296* Side effects:3297* The vRootX, vRootY, vRootWidth, and vRootHeight fields in3298* wmPtr are filled with the most up-to-date information.3299*3300*----------------------------------------------------------------------3301*/33023303static void3304UpdateVRootGeometry(wmPtr)3305WmInfo *wmPtr; /* Window manager information to be3306* updated. The wmPtr->vRoot field must3307* be valid. */3308{3309TkWindow *winPtr = wmPtr->winPtr;3310int bd;3311unsigned int dummy;3312Window dummy2;3313Status status;3314Tk_ErrorHandler handler;33153316/*3317* If this isn't a virtual-root window manager, just return information3318* about the screen.3319*/33203321wmPtr->flags &= ~WM_VROOT_OFFSET_STALE;3322if (wmPtr->vRoot == None) {3323noVRoot:3324wmPtr->vRootX = wmPtr->vRootY = 0;3325wmPtr->vRootWidth = DisplayWidth(winPtr->display, winPtr->screenNum);3326wmPtr->vRootHeight = DisplayHeight(winPtr->display, winPtr->screenNum);3327return;3328}33293330/*3331* Refresh the virtual root information if it's out of date.3332*/33333334handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,3335(Tk_ErrorProc *) NULL, (ClientData) NULL);3336status = XGetGeometry(winPtr->display, wmPtr->vRoot,3337&dummy2, &wmPtr->vRootX, &wmPtr->vRootY,3338(unsigned int *) &wmPtr->vRootWidth,3339(unsigned int *) &wmPtr->vRootHeight, (unsigned int *) &bd,3340&dummy);3341if (wmTracing) {3342printf("UpdateVRootGeometry: x = %d, y = %d, width = %d, ",3343wmPtr->vRootX, wmPtr->vRootY, wmPtr->vRootWidth);3344printf("height = %d, status = %d\n", wmPtr->vRootHeight, status);3345}3346Tk_DeleteErrorHandler(handler);3347if (status == 0) {3348/*3349* The virtual root is gone! Pretend that it never existed.3350*/33513352wmPtr->vRoot = None;3353goto noVRoot;3354}3355}33563357/*3358*----------------------------------------------------------------------3359*3360* Tk_GetVRootGeometry --3361*3362* This procedure returns information about the virtual root3363* window corresponding to a particular Tk window.3364*3365* Results:3366* The values at xPtr, yPtr, widthPtr, and heightPtr are set3367* with the offset and dimensions of the root window corresponding3368* to tkwin. If tkwin is being managed by a virtual root window3369* manager these values correspond to the virtual root window being3370* used for tkwin; otherwise the offsets will be 0 and the3371* dimensions will be those of the screen.3372*3373* Side effects:3374* Vroot window information is refreshed if it is out of date.3375*3376*----------------------------------------------------------------------3377*/33783379void3380Tk_GetVRootGeometry(tkwin, xPtr, yPtr, widthPtr, heightPtr)3381Tk_Window tkwin; /* Window whose virtual root is to be3382* queried. */3383int *xPtr, *yPtr; /* Store x and y offsets of virtual root3384* here. */3385int *widthPtr, *heightPtr; /* Store dimensions of virtual root here. */3386{3387WmInfo *wmPtr;3388TkWindow *winPtr = (TkWindow *) tkwin;33893390/*3391* Find the top-level window for tkwin, and locate the window manager3392* information for that window.3393*/33943395while (!(winPtr->flags & TK_TOP_LEVEL) && (winPtr->parentPtr != NULL)) {3396winPtr = winPtr->parentPtr;3397}3398wmPtr = winPtr->wmInfoPtr;33993400/*3401* Make sure that the geometry information is up-to-date, then copy3402* it out to the caller.3403*/34043405if (wmPtr->flags & WM_VROOT_OFFSET_STALE) {3406UpdateVRootGeometry(wmPtr);3407}3408*xPtr = wmPtr->vRootX;3409*yPtr = wmPtr->vRootY;3410*widthPtr = wmPtr->vRootWidth;3411*heightPtr = wmPtr->vRootHeight;3412}34133414/*3415*----------------------------------------------------------------------3416*3417* Tk_MoveToplevelWindow --3418*3419* This procedure is called instead of Tk_MoveWindow to adjust3420* the x-y location of a top-level window. It delays the actual3421* move to a later time and keeps window-manager information3422* up-to-date with the move3423*3424* Results:3425* None.3426*3427* Side effects:3428* The window is eventually moved so that its upper-left corner3429* (actually, the upper-left corner of the window's decorative3430* frame, if there is one) is at (x,y).3431*3432*----------------------------------------------------------------------3433*/34343435void3436Tk_MoveToplevelWindow(tkwin, x, y)3437Tk_Window tkwin; /* Window to move. */3438int x, y; /* New location for window (within3439* parent). */3440{3441TkWindow *winPtr = (TkWindow *) tkwin;3442register WmInfo *wmPtr = winPtr->wmInfoPtr;34433444if (!(winPtr->flags & TK_TOP_LEVEL)) {3445panic("Tk_MoveToplevelWindow called with non-toplevel window");3446}3447wmPtr->x = x;3448wmPtr->y = y;3449wmPtr->flags |= WM_MOVE_PENDING;3450wmPtr->flags &= ~(WM_NEGATIVE_X|WM_NEGATIVE_Y);3451if ((wmPtr->sizeHintsFlags & (USPosition|PPosition)) == 0) {3452wmPtr->sizeHintsFlags |= USPosition;3453wmPtr->flags |= WM_UPDATE_SIZE_HINTS;3454}34553456/*3457* If the window has already been mapped, must bring its geometry3458* up-to-date immediately, otherwise an event might arrive from the3459* server that would overwrite wmPtr->x and wmPtr->y and lose the3460* new position.3461*/34623463if (!(wmPtr->flags & WM_NEVER_MAPPED)) {3464if (wmPtr->flags & WM_UPDATE_PENDING) {3465Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);3466}3467UpdateGeometryInfo((ClientData) winPtr);3468}3469}34703471/*3472*----------------------------------------------------------------------3473*3474* UpdateWmProtocols --3475*3476* This procedure transfers the most up-to-date information about3477* window manager protocols from the WmInfo structure to the actual3478* property on the top-level window.3479*3480* Results:3481* None.3482*3483* Side effects:3484* The WM_PROTOCOLS property gets changed for wmPtr's window.3485*3486*----------------------------------------------------------------------3487*/34883489static void3490UpdateWmProtocols(wmPtr)3491register WmInfo *wmPtr; /* Information about top-level window. */3492{3493register ProtocolHandler *protPtr;3494Atom deleteWindowAtom;3495int count;3496Atom *arrayPtr, *atomPtr;34973498/*3499* There are only two tricky parts here. First, there could be any3500* number of atoms for the window, so count them and malloc an array3501* to hold all of their atoms. Second, we *always* want to respond3502* to the WM_DELETE_WINDOW protocol, even if no-one's officially asked.3503*/35043505for (protPtr = wmPtr->protPtr, count = 1; protPtr != NULL;3506protPtr = protPtr->nextPtr, count++) {3507/* Empty loop body; we're just counting the handlers. */3508}3509arrayPtr = (Atom *) ckalloc((unsigned) (count * sizeof(Atom)));3510deleteWindowAtom = Tk_InternAtom((Tk_Window) wmPtr->winPtr,3511"WM_DELETE_WINDOW");3512arrayPtr[0] = deleteWindowAtom;3513for (protPtr = wmPtr->protPtr, atomPtr = &arrayPtr[1];3514protPtr != NULL; protPtr = protPtr->nextPtr) {3515if (protPtr->protocol != deleteWindowAtom) {3516*atomPtr = protPtr->protocol;3517atomPtr++;3518}3519}3520XChangeProperty(wmPtr->winPtr->display, wmPtr->winPtr->window,3521Tk_InternAtom((Tk_Window) wmPtr->winPtr, "WM_PROTOCOLS"),3522XA_ATOM, 32, PropModeReplace, (unsigned char *) arrayPtr,3523atomPtr-arrayPtr);3524ckfree((char *) arrayPtr);3525}35263527/*3528*----------------------------------------------------------------------3529*3530* TkWmProtocolEventProc --3531*3532* This procedure is called by the Tk_HandleEvent whenever a3533* ClientMessage event arrives whose type is "WM_PROTOCOLS".3534* This procedure handles the message from the window manager3535* in an appropriate fashion.3536*3537* Results:3538* None.3539*3540* Side effects:3541* Depends on what sort of handler, if any, was set up for the3542* protocol.3543*3544*----------------------------------------------------------------------3545*/35463547void3548TkWmProtocolEventProc(winPtr, eventPtr)3549TkWindow *winPtr; /* Window to which the event was sent. */3550XEvent *eventPtr; /* X event. */3551{3552WmInfo *wmPtr;3553register ProtocolHandler *protPtr;3554Atom protocol;3555int result;3556char *protocolName;3557Tcl_Interp *interp;35583559wmPtr = winPtr->wmInfoPtr;3560if (wmPtr == NULL) {3561return;3562}3563protocol = (Atom) eventPtr->xclient.data.l[0];35643565/*3566* Note: it's very important to retrieve the protocol name now,3567* before invoking the command, even though the name won't be used3568* until after the command returns. This is because the command3569* could delete winPtr, making it impossible for us to use it3570* later in the call to Tk_GetAtomName.3571*/35723573protocolName = Tk_GetAtomName((Tk_Window) winPtr, protocol);3574for (protPtr = wmPtr->protPtr; protPtr != NULL;3575protPtr = protPtr->nextPtr) {3576if (protocol == protPtr->protocol) {3577Tcl_Preserve((ClientData) protPtr);3578interp = protPtr->interp;3579Tcl_Preserve((ClientData) interp);3580result = Tcl_GlobalEval(interp, protPtr->command);3581if (result != TCL_OK) {3582Tcl_AddErrorInfo(interp, "\n (command for \"");3583Tcl_AddErrorInfo(interp, protocolName);3584Tcl_AddErrorInfo(interp,3585"\" window manager protocol)");3586Tcl_BackgroundError(interp);3587}3588Tcl_Release((ClientData) interp);3589Tcl_Release((ClientData) protPtr);3590return;3591}3592}35933594/*3595* No handler was present for this protocol. If this is a3596* WM_DELETE_WINDOW message then just destroy the window.3597*/35983599if (protocol == Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW")) {3600Tk_DestroyWindow((Tk_Window) winPtr);3601}3602}36033604/*3605*----------------------------------------------------------------------3606*3607* TkWmRestackToplevel --3608*3609* This procedure restacks a top-level window.3610*3611* Results:3612* None.3613*3614* Side effects:3615* WinPtr gets restacked as specified by aboveBelow and otherPtr.3616* This procedure doesn't return until the restack has taken3617* effect and the ConfigureNotify event for it has been received.3618*3619*----------------------------------------------------------------------3620*/36213622void3623TkWmRestackToplevel(winPtr, aboveBelow, otherPtr)3624TkWindow *winPtr; /* Window to restack. */3625int aboveBelow; /* Gives relative position for restacking;3626* must be Above or Below. */3627TkWindow *otherPtr; /* Window relative to which to restack;3628* if NULL, then winPtr gets restacked3629* above or below *all* siblings. */3630{3631XWindowChanges changes;3632XWindowAttributes atts;3633unsigned int mask;3634Window window, dummy1, dummy2, vRoot;3635Window *children;3636unsigned int numChildren;3637int i;3638int desiredIndex = 0; /* Initialized to stop gcc warnings. */3639int ourIndex = 0; /* Initialized to stop gcc warnings. */3640unsigned long serial;3641XEvent event;3642int diff;3643Tk_ErrorHandler handler;36443645changes.stack_mode = aboveBelow;3646changes.sibling = None;3647mask = CWStackMode;3648if (winPtr->window == None) {3649Tk_MakeWindowExist((Tk_Window) winPtr);3650}3651if (winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {3652/*3653* Can't set stacking order properly until the window is on the3654* screen (mapping it may give it a reparent window), so make sure3655* it's on the screen.3656*/36573658TkWmMapWindow(winPtr);3659}3660window = (winPtr->wmInfoPtr->reparent != None)3661? winPtr->wmInfoPtr->reparent : winPtr->window;3662if (otherPtr != NULL) {3663if (otherPtr->window == None) {3664Tk_MakeWindowExist((Tk_Window) otherPtr);3665}3666if (otherPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {3667TkWmMapWindow(otherPtr);3668}3669changes.sibling = (otherPtr->wmInfoPtr->reparent != None)3670? otherPtr->wmInfoPtr->reparent : otherPtr->window;3671mask = CWStackMode|CWSibling;3672}36733674/*3675* Before actually reconfiguring the window, see if it's already3676* in the right place. If so then don't reconfigure it. The3677* reason for this extra work is that some window managers will3678* ignore the reconfigure request if the window is already in3679* the right place, causing a long delay in WaitForConfigureNotify3680* while it times out. Special note: if the window is almost in3681* the right place, and the only windows between it and the right3682* place aren't mapped, then we don't reconfigure it either, for3683* the same reason.3684*/36853686vRoot = winPtr->wmInfoPtr->vRoot;3687if (vRoot == None) {3688vRoot = RootWindowOfScreen(Tk_Screen((Tk_Window) winPtr));3689}3690if (XQueryTree(winPtr->display, vRoot, &dummy1, &dummy2,3691&children, &numChildren) != 0) {3692/*3693* Find where our window is in the stacking order, and3694* compute the desired location in the stacking order.3695*/36963697for (i = 0; i < numChildren; i++) {3698if (children[i] == window) {3699ourIndex = i;3700}3701if (children[i] == changes.sibling) {3702desiredIndex = i;3703}3704}3705if (mask & CWSibling) {3706if (aboveBelow == Above) {3707if (desiredIndex < ourIndex) {3708desiredIndex += 1;3709}3710} else {3711if (desiredIndex > ourIndex) {3712desiredIndex -= 1;3713}3714}3715} else {3716if (aboveBelow == Above) {3717desiredIndex = numChildren-1;3718} else {3719desiredIndex = 0;3720}3721}37223723/*3724* See if there are any mapped windows between where we are3725* and where we want to be.3726*/37273728handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,3729(Tk_ErrorProc *) NULL, (ClientData) NULL);3730while (desiredIndex != ourIndex) {3731if ((XGetWindowAttributes(winPtr->display, children[desiredIndex],3732&atts) != 0) && (atts.map_state != IsUnmapped)) {3733break;3734}3735if (desiredIndex < ourIndex) {3736desiredIndex++;3737} else {3738desiredIndex--;3739}3740}3741Tk_DeleteErrorHandler(handler);3742XFree((char *) children);3743if (ourIndex == desiredIndex) {3744return;3745}3746}37473748/*3749* Reconfigure the window. This tricky because of two things:3750* (a) Some window managers, like olvwm, insist that we raise3751* or lower the toplevel window itself, as opposed to its3752* decorative frame. Attempts to raise or lower the frame3753* are ignored.3754* (b) If the raise or lower is relative to a sibling, X will3755* generate an error unless we work with the frames (the3756* toplevels themselves aren't siblings).3757* Fortunately, the procedure XReconfigureWMWindow is supposed3758* to handle all of this stuff, so be careful to use it instead3759* of XConfigureWindow.3760*/37613762serial = NextRequest(winPtr->display);3763if (window != winPtr->window) {3764XSelectInput(winPtr->display, window, StructureNotifyMask);3765}3766XReconfigureWMWindow(winPtr->display, winPtr->window,3767Tk_ScreenNumber((Tk_Window) winPtr), mask, &changes);37683769/*3770* Wait for the reconfiguration to complete. If we don't wait, then3771* the window may not restack for a while and the application might3772* observe it before it has restacked. Waiting for the reconfiguration3773* is tricky if winPtr has been reparented, since the window getting3774* the event isn't one that Tk owns.3775*/37763777if (window == winPtr->window) {3778WaitForConfigureNotify(winPtr, serial);3779} else {3780while (1) {3781if (WaitForEvent(winPtr->display, window, ConfigureNotify,3782&event) != TCL_OK) {3783break;3784}3785diff = event.xconfigure.serial - serial;3786if (diff >= 0) {3787break;3788}3789}3790XSelectInput(winPtr->display, window, (long) 0);3791}3792}37933794/*3795*----------------------------------------------------------------------3796*3797* TkWmAddToColormapWindows --3798*3799* This procedure is called to add a given window to the3800* WM_COLORMAP_WINDOWS property for its top-level, if it3801* isn't already there. It is invoked by the Tk code that3802* creates a new colormap, in order to make sure that colormap3803* information is propagated to the window manager by default.3804*3805* Results:3806* None.3807*3808* Side effects:3809* WinPtr's window gets added to the WM_COLORMAP_WINDOWS3810* property of its nearest top-level ancestor, unless the3811* colormaps have been set explicitly with the3812* "wm colormapwindows" command.3813*3814*----------------------------------------------------------------------3815*/38163817void3818TkWmAddToColormapWindows(winPtr)3819TkWindow *winPtr; /* Window with a non-default colormap.3820* Should not be a top-level window. */3821{3822TkWindow *topPtr;3823Window *oldPtr, *newPtr;3824int count, i;38253826if (winPtr->window == None) {3827return;3828}38293830for (topPtr = winPtr->parentPtr; ; topPtr = topPtr->parentPtr) {3831if (topPtr == NULL) {3832/*3833* Window is being deleted. Skip the whole operation.3834*/38353836return;3837}3838if (topPtr->flags & TK_TOP_LEVEL) {3839break;3840}3841}3842if (topPtr->wmInfoPtr->flags & WM_COLORMAPS_EXPLICIT) {3843return;3844}38453846/*3847* Fetch the old value of the property.3848*/38493850if (XGetWMColormapWindows(topPtr->display, topPtr->window,3851&oldPtr, &count) == 0) {3852oldPtr = NULL;3853count = 0;3854}38553856/*3857* Make sure that the window isn't already in the list.3858*/38593860for (i = 0; i < count; i++) {3861if (oldPtr[i] == winPtr->window) {3862return;3863}3864}38653866/*3867* Make a new bigger array and use it to reset the property.3868* Automatically add the toplevel itself as the last element3869* of the list.3870*/38713872newPtr = (Window *) ckalloc((unsigned) ((count+2)*sizeof(Window)));3873for (i = 0; i < count; i++) {3874newPtr[i] = oldPtr[i];3875}3876if (count == 0) {3877count++;3878}3879newPtr[count-1] = winPtr->window;3880newPtr[count] = topPtr->window;3881XSetWMColormapWindows(topPtr->display, topPtr->window, newPtr, count+1);3882ckfree((char *) newPtr);3883if (oldPtr != NULL) {3884XFree((char *) oldPtr);3885}3886}38873888/*3889*----------------------------------------------------------------------3890*3891* TkWmRemoveFromColormapWindows --3892*3893* This procedure is called to remove a given window from the3894* WM_COLORMAP_WINDOWS property for its top-level. It is invoked3895* when windows are deleted.3896*3897* Results:3898* None.3899*3900* Side effects:3901* WinPtr's window gets removed from the WM_COLORMAP_WINDOWS3902* property of its nearest top-level ancestor, unless the3903* top-level itself is being deleted too.3904*3905*----------------------------------------------------------------------3906*/39073908void3909TkWmRemoveFromColormapWindows(winPtr)3910TkWindow *winPtr; /* Window that may be present in3911* WM_COLORMAP_WINDOWS property for its3912* top-level. Should not be a top-level3913* window. */3914{3915TkWindow *topPtr;3916Window *oldPtr;3917int count, i, j;39183919for (topPtr = winPtr->parentPtr; ; topPtr = topPtr->parentPtr) {3920if (topPtr == NULL) {3921/*3922* Ancestors have been deleted, so skip the whole operation.3923* Seems like this can't ever happen?3924*/39253926return;3927}3928if (topPtr->flags & TK_TOP_LEVEL) {3929break;3930}3931}3932if (topPtr->flags & TK_ALREADY_DEAD) {3933/*3934* Top-level is being deleted, so there's no need to cleanup3935* the WM_COLORMAP_WINDOWS property.3936*/39373938return;3939}39403941/*3942* Fetch the old value of the property.3943*/39443945if (XGetWMColormapWindows(topPtr->display, topPtr->window,3946&oldPtr, &count) == 0) {3947return;3948}39493950/*3951* Find the window and slide the following ones down to cover3952* it up.3953*/39543955for (i = 0; i < count; i++) {3956if (oldPtr[i] == winPtr->window) {3957for (j = i ; j < count-1; j++) {3958oldPtr[j] = oldPtr[j+1];3959}3960XSetWMColormapWindows(topPtr->display, topPtr->window,3961oldPtr, count-1);3962break;3963}3964}3965XFree((char *) oldPtr);3966}39673968/*3969*----------------------------------------------------------------------3970*3971* TkGetPointerCoords --3972*3973* Fetch the position of the mouse pointer.3974*3975* Results:3976* *xPtr and *yPtr are filled in with the (virtual) root coordinates3977* of the mouse pointer for tkwin's display. If the pointer isn't3978* on tkwin's screen, then -1 values are returned for both3979* coordinates. The argument tkwin must be a toplevel window.3980*3981* Side effects:3982* None.3983*3984*----------------------------------------------------------------------3985*/39863987void3988TkGetPointerCoords(tkwin, xPtr, yPtr)3989Tk_Window tkwin; /* Toplevel window that identifies screen3990* on which lookup is to be done. */3991int *xPtr, *yPtr; /* Store pointer coordinates here. */3992{3993TkWindow *winPtr = (TkWindow *) tkwin;3994WmInfo *wmPtr;3995Window w, root, child;3996int rootX, rootY;3997unsigned int mask;39983999wmPtr = winPtr->wmInfoPtr;40004001w = wmPtr->vRoot;4002if (w == None) {4003w = RootWindow(winPtr->display, winPtr->screenNum);4004}4005if (XQueryPointer(winPtr->display, w, &root, &child, &rootX, &rootY,4006xPtr, yPtr, &mask) != True) {4007*xPtr = -1;4008*yPtr = -1;4009}4010}40114012/*4013*----------------------------------------------------------------------4014*4015* TkMakeWindow --4016*4017* Create an actual window system window object based on the4018* current attributes of the specified TkWindow.4019*4020* Results:4021* Returns the handle to the new window, or None on failure.4022*4023* Side effects:4024* Creates a new X window.4025*4026*----------------------------------------------------------------------4027*/40284029Window4030TkMakeWindow(winPtr, parent)4031TkWindow *winPtr;4032Window parent;4033{4034return XCreateWindow(winPtr->display, parent, winPtr->changes.x,4035winPtr->changes.y, (unsigned) winPtr->changes.width,4036(unsigned) winPtr->changes.height,4037(unsigned) winPtr->changes.border_width, winPtr->depth,4038InputOutput, winPtr->visual, winPtr->dirtyAtts,4039&winPtr->atts);4040}40414042/*4043*----------------------------------------------------------------------4044*4045* GetMaxSize --4046*4047* This procedure computes the current maxWidth and maxHeight4048* values for a window, taking into account the possibility4049* that they may be defaulted.4050*4051* Results:4052* The values at *maxWidthPtr and *maxHeightPtr are filled4053* in with the maximum allowable dimensions of wmPtr's window,4054* in grid units. If no maximum has been specified for the4055* window, then this procedure computes the largest sizes that4056* will fit on the screen.4057*4058* Side effects:4059* None.4060*4061*----------------------------------------------------------------------4062*/40634064static void4065GetMaxSize(wmPtr, maxWidthPtr, maxHeightPtr)4066WmInfo *wmPtr; /* Window manager information for the4067* window. */4068int *maxWidthPtr; /* Where to store the current maximum4069* width of the window. */4070int *maxHeightPtr; /* Where to store the current maximum4071* height of the window. */4072{4073int tmp;40744075if (wmPtr->maxWidth > 0) {4076*maxWidthPtr = wmPtr->maxWidth;4077} else {4078/*4079* Must compute a default width. Fill up the display, leaving a4080* bit of extra space for the window manager's borders.4081*/40824083tmp = DisplayWidth(wmPtr->winPtr->display, wmPtr->winPtr->screenNum)4084- 15;4085if (wmPtr->gridWin != NULL) {4086/*4087* Gridding is turned on; convert from pixels to grid units.4088*/40894090tmp = wmPtr->reqGridWidth4091+ (tmp - wmPtr->winPtr->reqWidth)/wmPtr->widthInc;4092}4093*maxWidthPtr = tmp;4094}4095if (wmPtr->maxHeight > 0) {4096*maxHeightPtr = wmPtr->maxHeight;4097} else {4098tmp = DisplayHeight(wmPtr->winPtr->display, wmPtr->winPtr->screenNum)4099- 30;4100if (wmPtr->gridWin != NULL) {4101tmp = wmPtr->reqGridHeight4102+ (tmp - wmPtr->winPtr->reqHeight)/wmPtr->heightInc;4103}4104*maxHeightPtr = tmp;4105}4106}410741084109