Parent Directory | Revision Log | Patch
revision 44 by dashley, Fri Oct 14 02:09:58 2016 UTC | revision 71 by dashley, Sat Nov 5 11:07:06 2016 UTC | |
---|---|---|
# | Line 1 | Line 1 |
/* $Header: /cvsroot/esrg/sfesrg/esrgpcpj/shared/tk_base/tkwindow.c,v 1.1.1.1 2001/06/13 05:12:54 dtashley Exp $ */ | ||
/* | ||
* tkWindow.c -- | ||
* | ||
* This file provides basic window-manipulation procedures, | ||
* which are equivalent to procedures in Xlib (and even | ||
* invoke them) but also maintain the local Tk_Window | ||
* structure. | ||
* | ||
* Copyright (c) 1989-1994 The Regents of the University of California. | ||
* Copyright (c) 1994-1997 Sun Microsystems, Inc. | ||
* | ||
* See the file "license.terms" for information on usage and redistribution | ||
* of this file, and for a DISCLAIMER OF ALL WARRANTIES. | ||
* | ||
* RCS: @(#) $Id: tkwindow.c,v 1.1.1.1 2001/06/13 05:12:54 dtashley Exp $ | ||
*/ | ||
#include "tkPort.h" | ||
#include "tkInt.h" | ||
#if !defined(__WIN32__) && !defined(MAC_TCL) | ||
#include "tkUnixInt.h" | ||
#endif | ||
typedef struct ThreadSpecificData { | ||
int numMainWindows; /* Count of numver of main windows currently | ||
* open in this thread. */ | ||
TkMainInfo *mainWindowList; | ||
/* First in list of all main windows managed | ||
* by this thread. */ | ||
TkDisplay *displayList; | ||
/* List of all displays currently in use by | ||
* the current thread. */ | ||
int initialized; /* 0 means the structures above need | ||
* initializing. */ | ||
} ThreadSpecificData; | ||
static Tcl_ThreadDataKey dataKey; | ||
/* | ||
* The Mutex below is used to lock access to the Tk_Uid structs above. | ||
*/ | ||
TCL_DECLARE_MUTEX(windowMutex) | ||
/* | ||
* Default values for "changes" and "atts" fields of TkWindows. Note | ||
* that Tk always requests all events for all windows, except StructureNotify | ||
* events on internal windows: these events are generated internally. | ||
*/ | ||
static XWindowChanges defChanges = { | ||
0, 0, 1, 1, 0, 0, Above | ||
}; | ||
#define ALL_EVENTS_MASK \ | ||
KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask| \ | ||
EnterWindowMask|LeaveWindowMask|PointerMotionMask|ExposureMask| \ | ||
VisibilityChangeMask|PropertyChangeMask|ColormapChangeMask | ||
static XSetWindowAttributes defAtts= { | ||
None, /* background_pixmap */ | ||
0, /* background_pixel */ | ||
CopyFromParent, /* border_pixmap */ | ||
0, /* border_pixel */ | ||
NorthWestGravity, /* bit_gravity */ | ||
NorthWestGravity, /* win_gravity */ | ||
NotUseful, /* backing_store */ | ||
(unsigned) ~0, /* backing_planes */ | ||
0, /* backing_pixel */ | ||
False, /* save_under */ | ||
ALL_EVENTS_MASK, /* event_mask */ | ||
0, /* do_not_propagate_mask */ | ||
False, /* override_redirect */ | ||
CopyFromParent, /* colormap */ | ||
None /* cursor */ | ||
}; | ||
/* | ||
* The following structure defines all of the commands supported by | ||
* Tk, and the C procedures that execute them. | ||
*/ | ||
typedef struct { | ||
char *name; /* Name of command. */ | ||
Tcl_CmdProc *cmdProc; /* Command's string-based procedure. */ | ||
Tcl_ObjCmdProc *objProc; /* Command's object-based procedure. */ | ||
int isSafe; /* If !0, this command will be exposed in | ||
* a safe interpreter. Otherwise it will be | ||
* hidden in a safe interpreter. */ | ||
int passMainWindow; /* 0 means provide NULL clientData to | ||
* command procedure; 1 means pass main | ||
* window as clientData to command | ||
* procedure. */ | ||
} TkCmd; | ||
static TkCmd commands[] = { | ||
/* | ||
* Commands that are part of the intrinsics: | ||
*/ | ||
{"bell", NULL, Tk_BellObjCmd, 0, 1}, | ||
{"bind", Tk_BindCmd, NULL, 1, 1}, | ||
{"bindtags", Tk_BindtagsCmd, NULL, 1, 1}, | ||
{"clipboard", Tk_ClipboardCmd, NULL, 0, 1}, | ||
{"destroy", NULL, Tk_DestroyObjCmd, 1, 1}, | ||
{"event", NULL, Tk_EventObjCmd, 1, 1}, | ||
{"focus", NULL, Tk_FocusObjCmd, 1, 1}, | ||
{"font", NULL, Tk_FontObjCmd, 1, 1}, | ||
{"grab", Tk_GrabCmd, NULL, 0, 1}, | ||
{"grid", Tk_GridCmd, NULL, 1, 1}, | ||
{"image", NULL, Tk_ImageObjCmd, 1, 1}, | ||
{"lower", NULL, Tk_LowerObjCmd, 1, 1}, | ||
{"option", NULL, Tk_OptionObjCmd, 1, 1}, | ||
{"pack", Tk_PackCmd, NULL, 1, 1}, | ||
{"place", Tk_PlaceCmd, NULL, 1, 1}, | ||
{"raise", NULL, Tk_RaiseObjCmd, 1, 1}, | ||
{"selection", Tk_SelectionCmd, NULL, 0, 1}, | ||
{"tk", NULL, Tk_TkObjCmd, 0, 1}, | ||
{"tkwait", Tk_TkwaitCmd, NULL, 1, 1}, | ||
#if defined(__WIN32__) || defined(MAC_TCL) | ||
{"tk_chooseColor", NULL, Tk_ChooseColorObjCmd, 0, 1}, | ||
{"tk_chooseDirectory", NULL, Tk_ChooseDirectoryObjCmd, 0, 1}, | ||
{"tk_getOpenFile", NULL, Tk_GetOpenFileObjCmd, 0, 1}, | ||
{"tk_getSaveFile", NULL, Tk_GetSaveFileObjCmd, 0, 1}, | ||
#endif | ||
#ifdef __WIN32__ | ||
{"tk_messageBox", NULL, Tk_MessageBoxObjCmd, 0, 1}, | ||
#endif | ||
{"update", NULL, Tk_UpdateObjCmd, 1, 1}, | ||
{"winfo", NULL, Tk_WinfoObjCmd, 1, 1}, | ||
{"wm", Tk_WmCmd, NULL, 0, 1}, | ||
/* | ||
* Widget class commands. | ||
*/ | ||
{"button", NULL, Tk_ButtonObjCmd, 1, 0}, | ||
{"canvas", NULL, Tk_CanvasObjCmd, 1, 1}, | ||
{"checkbutton", NULL, Tk_CheckbuttonObjCmd, 1, 0}, | ||
{"entry", NULL, Tk_EntryObjCmd, 1, 0}, | ||
{"frame", NULL, Tk_FrameObjCmd, 1, 1}, | ||
{"label", NULL, Tk_LabelObjCmd, 1, 0}, | ||
{"listbox", NULL, Tk_ListboxObjCmd, 1, 0}, | ||
{"menubutton", NULL, Tk_MenubuttonObjCmd, 1, 0}, | ||
{"message", Tk_MessageCmd, NULL, 1, 1}, | ||
{"radiobutton", NULL, Tk_RadiobuttonObjCmd, 1, 0}, | ||
{"scale", NULL, Tk_ScaleObjCmd, 1, 0}, | ||
{"scrollbar", Tk_ScrollbarCmd, NULL, 1, 1}, | ||
{"text", Tk_TextCmd, NULL, 1, 1}, | ||
{"toplevel", NULL, Tk_ToplevelObjCmd, 0, 1}, | ||
/* | ||
* Misc. | ||
*/ | ||
#ifdef MAC_TCL | ||
{"unsupported1", TkUnsupported1Cmd, NULL, 1, 1}, | ||
#endif | ||
{(char *) NULL, (int (*) _ANSI_ARGS_((ClientData, Tcl_Interp *, int, char **))) NULL, NULL, 0} | ||
}; | ||
/* | ||
* The variables and table below are used to parse arguments from | ||
* the "argv" variable in Tk_Init. | ||
*/ | ||
static int synchronize = 0; | ||
static char *name = NULL; | ||
static char *display = NULL; | ||
static char *geometry = NULL; | ||
static char *colormap = NULL; | ||
static char *use = NULL; | ||
static char *visual = NULL; | ||
static int rest = 0; | ||
static Tk_ArgvInfo argTable[] = { | ||
{"-colormap", TK_ARGV_STRING, (char *) NULL, (char *) &colormap, | ||
"Colormap for main window"}, | ||
{"-display", TK_ARGV_STRING, (char *) NULL, (char *) &display, | ||
"Display to use"}, | ||
{"-geometry", TK_ARGV_STRING, (char *) NULL, (char *) &geometry, | ||
"Initial geometry for window"}, | ||
{"-name", TK_ARGV_STRING, (char *) NULL, (char *) &name, | ||
"Name to use for application"}, | ||
{"-sync", TK_ARGV_CONSTANT, (char *) 1, (char *) &synchronize, | ||
"Use synchronous mode for display server"}, | ||
{"-visual", TK_ARGV_STRING, (char *) NULL, (char *) &visual, | ||
"Visual for main window"}, | ||
{"-use", TK_ARGV_STRING, (char *) NULL, (char *) &use, | ||
"Id of window in which to embed application"}, | ||
{"--", TK_ARGV_REST, (char *) 1, (char *) &rest, | ||
"Pass all remaining arguments through to script"}, | ||
{(char *) NULL, TK_ARGV_END, (char *) NULL, (char *) NULL, | ||
(char *) NULL} | ||
}; | ||
/* | ||
* Forward declarations to procedures defined later in this file: | ||
*/ | ||
static Tk_Window CreateTopLevelWindow _ANSI_ARGS_((Tcl_Interp *interp, | ||
Tk_Window parent, char *name, char *screenName)); | ||
static void DeleteWindowsExitProc _ANSI_ARGS_(( | ||
ClientData clientData)); | ||
static TkDisplay * GetScreen _ANSI_ARGS_((Tcl_Interp *interp, | ||
char *screenName, int *screenPtr)); | ||
static int Initialize _ANSI_ARGS_((Tcl_Interp *interp)); | ||
static int NameWindow _ANSI_ARGS_((Tcl_Interp *interp, | ||
TkWindow *winPtr, TkWindow *parentPtr, | ||
char *name)); | ||
static void OpenIM _ANSI_ARGS_((TkDisplay *dispPtr)); | ||
static void UnlinkWindow _ANSI_ARGS_((TkWindow *winPtr)); | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* CreateTopLevelWindow -- | ||
* | ||
* Make a new window that will be at top-level (its parent will | ||
* be the root window of a screen). | ||
* | ||
* Results: | ||
* The return value is a token for the new window, or NULL if | ||
* an error prevented the new window from being created. If | ||
* NULL is returned, an error message will be left in | ||
* the interp's result. | ||
* | ||
* Side effects: | ||
* A new window structure is allocated locally. An X | ||
* window is NOT initially created, but will be created | ||
* the first time the window is mapped. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static Tk_Window | ||
CreateTopLevelWindow(interp, parent, name, screenName) | ||
Tcl_Interp *interp; /* Interpreter to use for error reporting. */ | ||
Tk_Window parent; /* Token for logical parent of new window | ||
* (used for naming, options, etc.). May | ||
* be NULL. */ | ||
char *name; /* Name for new window; if parent is | ||
* non-NULL, must be unique among parent's | ||
* children. */ | ||
char *screenName; /* Name of screen on which to create | ||
* window. NULL means use DISPLAY environment | ||
* variable to determine. Empty string means | ||
* use parent's screen, or DISPLAY if no | ||
* parent. */ | ||
{ | ||
register TkWindow *winPtr; | ||
register TkDisplay *dispPtr; | ||
int screenId; | ||
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) | ||
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); | ||
if (!tsdPtr->initialized) { | ||
tsdPtr->initialized = 1; | ||
/* | ||
* Create built-in image types. | ||
*/ | ||
Tk_CreateImageType(&tkBitmapImageType); | ||
Tk_CreateImageType(&tkPhotoImageType); | ||
/* | ||
* Create built-in photo image formats. | ||
*/ | ||
Tk_CreatePhotoImageFormat(&tkImgFmtGIF); | ||
Tk_CreateOldPhotoImageFormat(&tkImgFmtPPM); | ||
/* | ||
* Create exit handler to delete all windows when the application | ||
* exits. | ||
*/ | ||
Tcl_CreateExitHandler(DeleteWindowsExitProc, (ClientData) NULL); | ||
} | ||
if ((parent != NULL) && (screenName != NULL) && (screenName[0] == '\0')) { | ||
dispPtr = ((TkWindow *) parent)->dispPtr; | ||
screenId = Tk_ScreenNumber(parent); | ||
} else { | ||
dispPtr = GetScreen(interp, screenName, &screenId); | ||
if (dispPtr == NULL) { | ||
return (Tk_Window) NULL; | ||
} | ||
} | ||
winPtr = TkAllocWindow(dispPtr, screenId, (TkWindow *) parent); | ||
/* | ||
* Force the window to use a border pixel instead of border pixmap. | ||
* This is needed for the case where the window doesn't use the | ||
* default visual. In this case, the default border is a pixmap | ||
* inherited from the root window, which won't work because it will | ||
* have the wrong visual. | ||
*/ | ||
winPtr->dirtyAtts |= CWBorderPixel; | ||
/* | ||
* (Need to set the TK_TOP_LEVEL flag immediately here; otherwise | ||
* Tk_DestroyWindow will core dump if it is called before the flag | ||
* has been set.) | ||
*/ | ||
winPtr->flags |= TK_TOP_LEVEL; | ||
if (parent != NULL) { | ||
if (NameWindow(interp, winPtr, (TkWindow *) parent, name) != TCL_OK) { | ||
Tk_DestroyWindow((Tk_Window) winPtr); | ||
return (Tk_Window) NULL; | ||
} | ||
} | ||
TkWmNewWindow(winPtr); | ||
return (Tk_Window) winPtr; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* GetScreen -- | ||
* | ||
* Given a string name for a display-plus-screen, find the | ||
* TkDisplay structure for the display and return the screen | ||
* number too. | ||
* | ||
* Results: | ||
* The return value is a pointer to information about the display, | ||
* or NULL if the display couldn't be opened. In this case, an | ||
* error message is left in the interp's result. The location at | ||
* *screenPtr is overwritten with the screen number parsed from | ||
* screenName. | ||
* | ||
* Side effects: | ||
* A new connection is opened to the display if there is no | ||
* connection already. A new TkDisplay data structure is also | ||
* setup, if necessary. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static TkDisplay * | ||
GetScreen(interp, screenName, screenPtr) | ||
Tcl_Interp *interp; /* Place to leave error message. */ | ||
char *screenName; /* Name for screen. NULL or empty means | ||
* use DISPLAY envariable. */ | ||
int *screenPtr; /* Where to store screen number. */ | ||
{ | ||
register TkDisplay *dispPtr; | ||
char *p; | ||
int screenId; | ||
size_t length; | ||
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) | ||
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); | ||
/* | ||
* Separate the screen number from the rest of the display | ||
* name. ScreenName is assumed to have the syntax | ||
* <display>.<screen> with the dot and the screen being | ||
* optional. | ||
*/ | ||
screenName = TkGetDefaultScreenName(interp, screenName); | ||
if (screenName == NULL) { | ||
Tcl_SetResult(interp, | ||
"no display name and no $DISPLAY environment variable", | ||
TCL_STATIC); | ||
return (TkDisplay *) NULL; | ||
} | ||
length = strlen(screenName); | ||
screenId = 0; | ||
p = screenName+length-1; | ||
while (isdigit(UCHAR(*p)) && (p != screenName)) { | ||
p--; | ||
} | ||
if ((*p == '.') && (p[1] != '\0')) { | ||
length = p - screenName; | ||
screenId = strtoul(p+1, (char **) NULL, 10); | ||
} | ||
/* | ||
* See if we already have a connection to this display. If not, | ||
* then open a new connection. | ||
*/ | ||
for (dispPtr = TkGetDisplayList(); ; dispPtr = dispPtr->nextPtr) { | ||
if (dispPtr == NULL) { | ||
dispPtr = TkpOpenDisplay(screenName); | ||
if (dispPtr == NULL) { | ||
Tcl_AppendResult(interp, "couldn't connect to display \"", | ||
screenName, "\"", (char *) NULL); | ||
return (TkDisplay *) NULL; | ||
} | ||
dispPtr->nextPtr = TkGetDisplayList(); | ||
dispPtr->name = (char *) ckalloc((unsigned) (length+1)); | ||
dispPtr->lastEventTime = CurrentTime; | ||
dispPtr->borderInit = 0; | ||
dispPtr->atomInit = 0; | ||
dispPtr->bindInfoStale = 1; | ||
dispPtr->modeModMask = 0; | ||
dispPtr->metaModMask = 0; | ||
dispPtr->altModMask = 0; | ||
dispPtr->numModKeyCodes = 0; | ||
dispPtr->modKeyCodes = NULL; | ||
dispPtr->bitmapInit = 0; | ||
dispPtr->bitmapAutoNumber = 0; | ||
dispPtr->numIdSearches = 0; | ||
dispPtr->numSlowSearches = 0; | ||
dispPtr->colorInit = 0; | ||
dispPtr->stressPtr = NULL; | ||
dispPtr->cursorInit = 0; | ||
dispPtr->cursorString[0] = '\0'; | ||
dispPtr->cursorFont = None; | ||
dispPtr->errorPtr = NULL; | ||
dispPtr->deleteCount = 0; | ||
dispPtr->delayedMotionPtr = NULL; | ||
dispPtr->focusDebug = 0; | ||
dispPtr->implicitWinPtr = NULL; | ||
dispPtr->focusPtr = NULL; | ||
dispPtr->gcInit = 0; | ||
dispPtr->geomInit = 0; | ||
dispPtr->uidInit = 0; | ||
dispPtr->grabWinPtr = NULL; | ||
dispPtr->eventualGrabWinPtr = NULL; | ||
dispPtr->buttonWinPtr = NULL; | ||
dispPtr->serverWinPtr = NULL; | ||
dispPtr->firstGrabEventPtr = NULL; | ||
dispPtr->lastGrabEventPtr = NULL; | ||
dispPtr->grabFlags = 0; | ||
dispPtr->mouseButtonState = 0; | ||
dispPtr->warpInProgress = 0; | ||
dispPtr->warpWindow = None; | ||
dispPtr->warpX = 0; | ||
dispPtr->warpY = 0; | ||
dispPtr->gridInit = 0; | ||
dispPtr->imageId = 0; | ||
dispPtr->packInit = 0; | ||
dispPtr->placeInit = 0; | ||
dispPtr->selectionInfoPtr = NULL; | ||
dispPtr->multipleAtom = None; | ||
dispPtr->clipWindow = NULL; | ||
dispPtr->clipboardActive = 0; | ||
dispPtr->clipboardAppPtr = NULL; | ||
dispPtr->clipTargetPtr = NULL; | ||
dispPtr->commTkwin = NULL; | ||
dispPtr->wmTracing = 0; | ||
dispPtr->firstWmPtr = NULL; | ||
dispPtr->foregroundWmPtr = NULL; | ||
dispPtr->destroyCount = 0; | ||
dispPtr->lastDestroyRequest = 0; | ||
dispPtr->cmapPtr = NULL; | ||
Tcl_InitHashTable(&dispPtr->winTable, TCL_ONE_WORD_KEYS); | ||
dispPtr->refCount = 0; | ||
strncpy(dispPtr->name, screenName, length); | ||
dispPtr->name[length] = '\0'; | ||
dispPtr->useInputMethods = 0; | ||
OpenIM(dispPtr); | ||
TkInitXId(dispPtr); | ||
tsdPtr->displayList = dispPtr; | ||
break; | ||
} | ||
if ((strncmp(dispPtr->name, screenName, length) == 0) | ||
&& (dispPtr->name[length] == '\0')) { | ||
break; | ||
} | ||
} | ||
if (screenId >= ScreenCount(dispPtr->display)) { | ||
char buf[32 + TCL_INTEGER_SPACE]; | ||
sprintf(buf, "bad screen number \"%d\"", screenId); | ||
Tcl_SetResult(interp, buf, TCL_VOLATILE); | ||
return (TkDisplay *) NULL; | ||
} | ||
*screenPtr = screenId; | ||
return dispPtr; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TkGetDisplay -- | ||
* | ||
* Given an X display, TkGetDisplay returns the TkDisplay | ||
* structure for the display. | ||
* | ||
* Results: | ||
* The return value is a pointer to information about the display, | ||
* or NULL if the display did not have a TkDisplay structure. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
TkDisplay * | ||
TkGetDisplay(display) | ||
Display *display; /* X's display pointer */ | ||
{ | ||
TkDisplay *dispPtr; | ||
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) | ||
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); | ||
for (dispPtr = tsdPtr->displayList; dispPtr != NULL; | ||
dispPtr = dispPtr->nextPtr) { | ||
if (dispPtr->display == display) { | ||
break; | ||
} | ||
} | ||
return dispPtr; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* TkGetDisplayList -- | ||
* | ||
* This procedure returns a pointer to the thread-local | ||
* list of TkDisplays corresponding to the open displays. | ||
* | ||
* Results: | ||
* The return value is a pointer to the first TkDisplay | ||
* structure in thread-local-storage. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
TkDisplay * | ||
TkGetDisplayList() | ||
{ | ||
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) | ||
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); | ||
return tsdPtr->displayList; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* TkGetMainInfoList -- | ||
* | ||
* This procedure returns a pointer to the list of structures | ||
* containing information about all main windows for the | ||
* current thread. | ||
* | ||
* Results: | ||
* The return value is a pointer to the first TkMainInfo | ||
* structure in thread local storage. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
TkMainInfo * | ||
TkGetMainInfoList() | ||
{ | ||
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) | ||
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); | ||
return tsdPtr->mainWindowList; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* TkAllocWindow -- | ||
* | ||
* This procedure creates and initializes a TkWindow structure. | ||
* | ||
* Results: | ||
* The return value is a pointer to the new window. | ||
* | ||
* Side effects: | ||
* A new window structure is allocated and all its fields are | ||
* initialized. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
TkWindow * | ||
TkAllocWindow(dispPtr, screenNum, parentPtr) | ||
TkDisplay *dispPtr; /* Display associated with new window. */ | ||
int screenNum; /* Index of screen for new window. */ | ||
TkWindow *parentPtr; /* Parent from which this window should | ||
* inherit visual information. NULL means | ||
* use screen defaults instead of | ||
* inheriting. */ | ||
{ | ||
register TkWindow *winPtr; | ||
winPtr = (TkWindow *) ckalloc(sizeof(TkWindow)); | ||
winPtr->display = dispPtr->display; | ||
winPtr->dispPtr = dispPtr; | ||
winPtr->screenNum = screenNum; | ||
if ((parentPtr != NULL) && (parentPtr->display == winPtr->display) | ||
&& (parentPtr->screenNum == winPtr->screenNum)) { | ||
winPtr->visual = parentPtr->visual; | ||
winPtr->depth = parentPtr->depth; | ||
} else { | ||
winPtr->visual = DefaultVisual(dispPtr->display, screenNum); | ||
winPtr->depth = DefaultDepth(dispPtr->display, screenNum); | ||
} | ||
winPtr->window = None; | ||
winPtr->childList = NULL; | ||
winPtr->lastChildPtr = NULL; | ||
winPtr->parentPtr = NULL; | ||
winPtr->nextPtr = NULL; | ||
winPtr->mainPtr = NULL; | ||
winPtr->pathName = NULL; | ||
winPtr->nameUid = NULL; | ||
winPtr->classUid = NULL; | ||
winPtr->changes = defChanges; | ||
winPtr->dirtyChanges = CWX|CWY|CWWidth|CWHeight|CWBorderWidth; | ||
winPtr->atts = defAtts; | ||
if ((parentPtr != NULL) && (parentPtr->display == winPtr->display) | ||
&& (parentPtr->screenNum == winPtr->screenNum)) { | ||
winPtr->atts.colormap = parentPtr->atts.colormap; | ||
} else { | ||
winPtr->atts.colormap = DefaultColormap(dispPtr->display, screenNum); | ||
} | ||
winPtr->dirtyAtts = CWEventMask|CWColormap|CWBitGravity; | ||
winPtr->flags = 0; | ||
winPtr->handlerList = NULL; | ||
#ifdef TK_USE_INPUT_METHODS | ||
winPtr->inputContext = NULL; | ||
#endif /* TK_USE_INPUT_METHODS */ | ||
winPtr->tagPtr = NULL; | ||
winPtr->numTags = 0; | ||
winPtr->optionLevel = -1; | ||
winPtr->selHandlerList = NULL; | ||
winPtr->geomMgrPtr = NULL; | ||
winPtr->geomData = NULL; | ||
winPtr->reqWidth = winPtr->reqHeight = 1; | ||
winPtr->internalBorderWidth = 0; | ||
winPtr->wmInfoPtr = NULL; | ||
winPtr->classProcsPtr = NULL; | ||
winPtr->instanceData = NULL; | ||
winPtr->privatePtr = NULL; | ||
return winPtr; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* NameWindow -- | ||
* | ||
* This procedure is invoked to give a window a name and insert | ||
* the window into the hierarchy associated with a particular | ||
* application. | ||
* | ||
* Results: | ||
* A standard Tcl return value. | ||
* | ||
* Side effects: | ||
* See above. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static int | ||
NameWindow(interp, winPtr, parentPtr, name) | ||
Tcl_Interp *interp; /* Interpreter to use for error reporting. */ | ||
register TkWindow *winPtr; /* Window that is to be named and inserted. */ | ||
TkWindow *parentPtr; /* Pointer to logical parent for winPtr | ||
* (used for naming, options, etc.). */ | ||
char *name; /* Name for winPtr; must be unique among | ||
* parentPtr's children. */ | ||
{ | ||
#define FIXED_SIZE 200 | ||
char staticSpace[FIXED_SIZE]; | ||
char *pathName; | ||
int new; | ||
Tcl_HashEntry *hPtr; | ||
int length1, length2; | ||
/* | ||
* Setup all the stuff except name right away, then do the name stuff | ||
* last. This is so that if the name stuff fails, everything else | ||
* will be properly initialized (needed to destroy the window cleanly | ||
* after the naming failure). | ||
*/ | ||
winPtr->parentPtr = parentPtr; | ||
winPtr->nextPtr = NULL; | ||
if (parentPtr->childList == NULL) { | ||
parentPtr->childList = winPtr; | ||
} else { | ||
parentPtr->lastChildPtr->nextPtr = winPtr; | ||
} | ||
parentPtr->lastChildPtr = winPtr; | ||
winPtr->mainPtr = parentPtr->mainPtr; | ||
winPtr->mainPtr->refCount++; | ||
winPtr->nameUid = Tk_GetUid(name); | ||
/* | ||
* Don't permit names that start with an upper-case letter: this | ||
* will just cause confusion with class names in the option database. | ||
*/ | ||
if (isupper(UCHAR(name[0]))) { | ||
Tcl_AppendResult(interp, | ||
"window name starts with an upper-case letter: \"", | ||
name, "\"", (char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* To permit names of arbitrary length, must be prepared to malloc | ||
* a buffer to hold the new path name. To run fast in the common | ||
* case where names are short, use a fixed-size buffer on the | ||
* stack. | ||
*/ | ||
length1 = strlen(parentPtr->pathName); | ||
length2 = strlen(name); | ||
if ((length1+length2+2) <= FIXED_SIZE) { | ||
pathName = staticSpace; | ||
} else { | ||
pathName = (char *) ckalloc((unsigned) (length1+length2+2)); | ||
} | ||
if (length1 == 1) { | ||
pathName[0] = '.'; | ||
strcpy(pathName+1, name); | ||
} else { | ||
strcpy(pathName, parentPtr->pathName); | ||
pathName[length1] = '.'; | ||
strcpy(pathName+length1+1, name); | ||
} | ||
hPtr = Tcl_CreateHashEntry(&parentPtr->mainPtr->nameTable, pathName, &new); | ||
if (pathName != staticSpace) { | ||
ckfree(pathName); | ||
} | ||
if (!new) { | ||
Tcl_AppendResult(interp, "window name \"", name, | ||
"\" already exists in parent", (char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
Tcl_SetHashValue(hPtr, winPtr); | ||
winPtr->pathName = Tcl_GetHashKey(&parentPtr->mainPtr->nameTable, hPtr); | ||
return TCL_OK; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TkCreateMainWindow -- | ||
* | ||
* Make a new main window. A main window is a special kind of | ||
* top-level window used as the outermost window in an | ||
* application. | ||
* | ||
* Results: | ||
* The return value is a token for the new window, or NULL if | ||
* an error prevented the new window from being created. If | ||
* NULL is returned, an error message will be left in | ||
* the interp's result. | ||
* | ||
* Side effects: | ||
* A new window structure is allocated locally; "interp" is | ||
* associated with the window and registered for "send" commands | ||
* under "baseName". BaseName may be extended with an instance | ||
* number in the form "#2" if necessary to make it globally | ||
* unique. Tk-related commands are bound into interp. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
Tk_Window | ||
TkCreateMainWindow(interp, screenName, baseName) | ||
Tcl_Interp *interp; /* Interpreter to use for error reporting. */ | ||
char *screenName; /* Name of screen on which to create | ||
* window. Empty or NULL string means | ||
* use DISPLAY environment variable. */ | ||
char *baseName; /* Base name for application; usually of the | ||
* form "prog instance". */ | ||
{ | ||
Tk_Window tkwin; | ||
int dummy; | ||
int isSafe; | ||
Tcl_HashEntry *hPtr; | ||
register TkMainInfo *mainPtr; | ||
register TkWindow *winPtr; | ||
register TkCmd *cmdPtr; | ||
ClientData clientData; | ||
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) | ||
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); | ||
/* | ||
* Panic if someone updated the TkWindow structure without | ||
* also updating the Tk_FakeWin structure (or vice versa). | ||
*/ | ||
if (sizeof(TkWindow) != sizeof(Tk_FakeWin)) { | ||
panic("TkWindow and Tk_FakeWin are not the same size"); | ||
} | ||
/* | ||
* Create the basic TkWindow structure. | ||
*/ | ||
tkwin = CreateTopLevelWindow(interp, (Tk_Window) NULL, baseName, | ||
screenName); | ||
if (tkwin == NULL) { | ||
return NULL; | ||
} | ||
/* | ||
* Create the TkMainInfo structure for this application, and set | ||
* up name-related information for the new window. | ||
*/ | ||
winPtr = (TkWindow *) tkwin; | ||
mainPtr = (TkMainInfo *) ckalloc(sizeof(TkMainInfo)); | ||
mainPtr->winPtr = winPtr; | ||
mainPtr->refCount = 1; | ||
mainPtr->interp = interp; | ||
Tcl_InitHashTable(&mainPtr->nameTable, TCL_STRING_KEYS); | ||
TkEventInit(); | ||
TkBindInit(mainPtr); | ||
TkFontPkgInit(mainPtr); | ||
mainPtr->tlFocusPtr = NULL; | ||
mainPtr->displayFocusPtr = NULL; | ||
mainPtr->optionRootPtr = NULL; | ||
Tcl_InitHashTable(&mainPtr->imageTable, TCL_STRING_KEYS); | ||
mainPtr->strictMotif = 0; | ||
if (Tcl_LinkVar(interp, "tk_strictMotif", (char *) &mainPtr->strictMotif, | ||
TCL_LINK_BOOLEAN) != TCL_OK) { | ||
Tcl_ResetResult(interp); | ||
} | ||
mainPtr->nextPtr = tsdPtr->mainWindowList; | ||
tsdPtr->mainWindowList = mainPtr; | ||
winPtr->mainPtr = mainPtr; | ||
hPtr = Tcl_CreateHashEntry(&mainPtr->nameTable, ".", &dummy); | ||
Tcl_SetHashValue(hPtr, winPtr); | ||
winPtr->pathName = Tcl_GetHashKey(&mainPtr->nameTable, hPtr); | ||
/* | ||
* We have just created another Tk application; increment the refcount | ||
* on the display pointer. | ||
*/ | ||
winPtr->dispPtr->refCount++; | ||
/* | ||
* Register the interpreter for "send" purposes. | ||
*/ | ||
winPtr->nameUid = Tk_GetUid(Tk_SetAppName(tkwin, baseName)); | ||
/* | ||
* Bind in Tk's commands. | ||
*/ | ||
isSafe = Tcl_IsSafe(interp); | ||
for (cmdPtr = commands; cmdPtr->name != NULL; cmdPtr++) { | ||
if ((cmdPtr->cmdProc == NULL) && (cmdPtr->objProc == NULL)) { | ||
panic("TkCreateMainWindow: builtin command with NULL string and object procs"); | ||
} | ||
if (cmdPtr->passMainWindow) { | ||
clientData = (ClientData) tkwin; | ||
} else { | ||
clientData = (ClientData) NULL; | ||
} | ||
if (cmdPtr->cmdProc != NULL) { | ||
Tcl_CreateCommand(interp, cmdPtr->name, cmdPtr->cmdProc, | ||
clientData, (void (*) _ANSI_ARGS_((ClientData))) NULL); | ||
} else { | ||
Tcl_CreateObjCommand(interp, cmdPtr->name, cmdPtr->objProc, | ||
clientData, NULL); | ||
} | ||
if (isSafe) { | ||
if (!(cmdPtr->isSafe)) { | ||
Tcl_HideCommand(interp, cmdPtr->name, cmdPtr->name); | ||
} | ||
} | ||
} | ||
TkCreateMenuCmd(interp); | ||
/* | ||
* Set variables for the intepreter. | ||
*/ | ||
Tcl_SetVar(interp, "tk_patchLevel", TK_PATCH_LEVEL, TCL_GLOBAL_ONLY); | ||
Tcl_SetVar(interp, "tk_version", TK_VERSION, TCL_GLOBAL_ONLY); | ||
tsdPtr->numMainWindows++; | ||
return tkwin; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* Tk_CreateWindow -- | ||
* | ||
* Create a new internal or top-level window as a child of an | ||
* existing window. | ||
* | ||
* Results: | ||
* The return value is a token for the new window. This | ||
* is not the same as X's token for the window. If an error | ||
* occurred in creating the window (e.g. no such display or | ||
* screen), then an error message is left in the interp's result and | ||
* NULL is returned. | ||
* | ||
* Side effects: | ||
* A new window structure is allocated locally. An X | ||
* window is not initially created, but will be created | ||
* the first time the window is mapped. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
Tk_Window | ||
Tk_CreateWindow(interp, parent, name, screenName) | ||
Tcl_Interp *interp; /* Interpreter to use for error reporting. | ||
* the interp's result is assumed to be | ||
* initialized by the caller. */ | ||
Tk_Window parent; /* Token for parent of new window. */ | ||
char *name; /* Name for new window. Must be unique | ||
* among parent's children. */ | ||
char *screenName; /* If NULL, new window will be internal on | ||
* same screen as its parent. If non-NULL, | ||
* gives name of screen on which to create | ||
* new window; window will be a top-level | ||
* window. */ | ||
{ | ||
TkWindow *parentPtr = (TkWindow *) parent; | ||
TkWindow *winPtr; | ||
if ((parentPtr != NULL) && (parentPtr->flags & TK_ALREADY_DEAD)) { | ||
Tcl_AppendResult(interp, | ||
"can't create window: parent has been destroyed", | ||
(char *) NULL); | ||
return NULL; | ||
} else if ((parentPtr != NULL) && | ||
(parentPtr->flags & TK_CONTAINER)) { | ||
Tcl_AppendResult(interp, | ||
"can't create window: its parent has -container = yes", | ||
(char *) NULL); | ||
return NULL; | ||
} | ||
if (screenName == NULL) { | ||
winPtr = TkAllocWindow(parentPtr->dispPtr, parentPtr->screenNum, | ||
parentPtr); | ||
if (NameWindow(interp, winPtr, parentPtr, name) != TCL_OK) { | ||
Tk_DestroyWindow((Tk_Window) winPtr); | ||
return NULL; | ||
} else { | ||
return (Tk_Window) winPtr; | ||
} | ||
} else { | ||
return CreateTopLevelWindow(interp, parent, name, screenName); | ||
} | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tk_CreateWindowFromPath -- | ||
* | ||
* This procedure is similar to Tk_CreateWindow except that | ||
* it uses a path name to create the window, rather than a | ||
* parent and a child name. | ||
* | ||
* Results: | ||
* The return value is a token for the new window. This | ||
* is not the same as X's token for the window. If an error | ||
* occurred in creating the window (e.g. no such display or | ||
* screen), then an error message is left in the interp's result and | ||
* NULL is returned. | ||
* | ||
* Side effects: | ||
* A new window structure is allocated locally. An X | ||
* window is not initially created, but will be created | ||
* the first time the window is mapped. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
Tk_Window | ||
Tk_CreateWindowFromPath(interp, tkwin, pathName, screenName) | ||
Tcl_Interp *interp; /* Interpreter to use for error reporting. | ||
* the interp's result is assumed to be | ||
* initialized by the caller. */ | ||
Tk_Window tkwin; /* Token for any window in application | ||
* that is to contain new window. */ | ||
char *pathName; /* Path name for new window within the | ||
* application of tkwin. The parent of | ||
* this window must already exist, but | ||
* the window itself must not exist. */ | ||
char *screenName; /* If NULL, new window will be on same | ||
* screen as its parent. If non-NULL, | ||
* gives name of screen on which to create | ||
* new window; window will be a top-level | ||
* window. */ | ||
{ | ||
#define FIXED_SPACE 5 | ||
char fixedSpace[FIXED_SPACE+1]; | ||
char *p; | ||
Tk_Window parent; | ||
int numChars; | ||
/* | ||
* Strip the parent's name out of pathName (it's everything up | ||
* to the last dot). There are two tricky parts: (a) must | ||
* copy the parent's name somewhere else to avoid modifying | ||
* the pathName string (for large names, space for the copy | ||
* will have to be malloc'ed); (b) must special-case the | ||
* situation where the parent is ".". | ||
*/ | ||
p = strrchr(pathName, '.'); | ||
if (p == NULL) { | ||
Tcl_AppendResult(interp, "bad window path name \"", pathName, | ||
"\"", (char *) NULL); | ||
return NULL; | ||
} | ||
numChars = p-pathName; | ||
if (numChars > FIXED_SPACE) { | ||
p = (char *) ckalloc((unsigned) (numChars+1)); | ||
} else { | ||
p = fixedSpace; | ||
} | ||
if (numChars == 0) { | ||
*p = '.'; | ||
p[1] = '\0'; | ||
} else { | ||
strncpy(p, pathName, (size_t) numChars); | ||
p[numChars] = '\0'; | ||
} | ||
/* | ||
* Find the parent window. | ||
*/ | ||
parent = Tk_NameToWindow(interp, p, tkwin); | ||
if (p != fixedSpace) { | ||
ckfree(p); | ||
} | ||
if (parent == NULL) { | ||
return NULL; | ||
} | ||
if (((TkWindow *) parent)->flags & TK_ALREADY_DEAD) { | ||
Tcl_AppendResult(interp, | ||
"can't create window: parent has been destroyed", (char *) NULL); | ||
return NULL; | ||
} else if (((TkWindow *) parent)->flags & TK_CONTAINER) { | ||
Tcl_AppendResult(interp, | ||
"can't create window: its parent has -container = yes", | ||
(char *) NULL); | ||
return NULL; | ||
} | ||
/* | ||
* Create the window. | ||
*/ | ||
if (screenName == NULL) { | ||
TkWindow *parentPtr = (TkWindow *) parent; | ||
TkWindow *winPtr; | ||
winPtr = TkAllocWindow(parentPtr->dispPtr, parentPtr->screenNum, | ||
parentPtr); | ||
if (NameWindow(interp, winPtr, parentPtr, pathName+numChars+1) | ||
!= TCL_OK) { | ||
Tk_DestroyWindow((Tk_Window) winPtr); | ||
return NULL; | ||
} else { | ||
return (Tk_Window) winPtr; | ||
} | ||
} else { | ||
return CreateTopLevelWindow(interp, parent, pathName+numChars+1, | ||
screenName); | ||
} | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* Tk_DestroyWindow -- | ||
* | ||
* Destroy an existing window. After this call, the caller | ||
* should never again use the token. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* The window is deleted, along with all of its children. | ||
* Relevant callback procedures are invoked. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
void | ||
Tk_DestroyWindow(tkwin) | ||
Tk_Window tkwin; /* Window to destroy. */ | ||
{ | ||
TkWindow *winPtr = (TkWindow *) tkwin; | ||
TkDisplay *dispPtr = winPtr->dispPtr; | ||
XEvent event; | ||
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) | ||
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); | ||
if (winPtr->flags & TK_ALREADY_DEAD) { | ||
/* | ||
* A destroy event binding caused the window to be destroyed | ||
* again. Ignore the request. | ||
*/ | ||
return; | ||
} | ||
winPtr->flags |= TK_ALREADY_DEAD; | ||
/* | ||
* Some cleanup needs to be done immediately, rather than later, | ||
* because it needs information that will be destoyed before we | ||
* get to the main cleanup point. For example, TkFocusDeadWindow | ||
* needs to access the parentPtr field from a window, but if | ||
* a Destroy event handler deletes the window's parent this | ||
* field will be NULL before the main cleanup point is reached. | ||
*/ | ||
TkFocusDeadWindow(winPtr); | ||
/* | ||
* If this is a main window, remove it from the list of main | ||
* windows. This needs to be done now (rather than later with | ||
* all the other main window cleanup) to handle situations where | ||
* a destroy binding for a window calls "exit". In this case | ||
* the child window cleanup isn't complete when exit is called, | ||
* so the reference count of its application doesn't go to zero | ||
* when exit calls Tk_DestroyWindow on ".", so the main window | ||
* doesn't get removed from the list and exit loops infinitely. | ||
* Even worse, if "destroy ." is called by the destroy binding | ||
* before calling "exit", "exit" will attempt to destroy | ||
* mainPtr->winPtr, which no longer exists, and there may be a | ||
* core dump. | ||
* | ||
* Also decrement the display refcount so that if this is the | ||
* last Tk application in this process on this display, the display | ||
* can be closed and its data structures deleted. | ||
*/ | ||
if (winPtr->mainPtr->winPtr == winPtr) { | ||
dispPtr->refCount--; | ||
if (tsdPtr->mainWindowList == winPtr->mainPtr) { | ||
tsdPtr->mainWindowList = winPtr->mainPtr->nextPtr; | ||
} else { | ||
TkMainInfo *prevPtr; | ||
for (prevPtr = tsdPtr->mainWindowList; | ||
prevPtr->nextPtr != winPtr->mainPtr; | ||
prevPtr = prevPtr->nextPtr) { | ||
/* Empty loop body. */ | ||
} | ||
prevPtr->nextPtr = winPtr->mainPtr->nextPtr; | ||
} | ||
tsdPtr->numMainWindows--; | ||
} | ||
/* | ||
* Recursively destroy children. | ||
*/ | ||
dispPtr->destroyCount++; | ||
while (winPtr->childList != NULL) { | ||
TkWindow *childPtr; | ||
childPtr = winPtr->childList; | ||
childPtr->flags |= TK_DONT_DESTROY_WINDOW; | ||
Tk_DestroyWindow((Tk_Window) childPtr); | ||
if (winPtr->childList == childPtr) { | ||
/* | ||
* The child didn't remove itself from the child list, so | ||
* let's remove it here. This can happen in some strange | ||
* conditions, such as when a Delete event handler for a | ||
* window deletes the window's parent. | ||
*/ | ||
winPtr->childList = childPtr->nextPtr; | ||
childPtr->parentPtr = NULL; | ||
} | ||
} | ||
if ((winPtr->flags & (TK_CONTAINER|TK_BOTH_HALVES)) | ||
== (TK_CONTAINER|TK_BOTH_HALVES)) { | ||
/* | ||
* This is the container for an embedded application, and | ||
* the embedded application is also in this process. Delete | ||
* the embedded window in-line here, for the same reasons we | ||
* delete children in-line (otherwise, for example, the Tk | ||
* window may appear to exist even though its X window is | ||
* gone; this could cause errors). Special note: it's possible | ||
* that the embedded window has already been deleted, in which | ||
* case TkpGetOtherWindow will return NULL. | ||
*/ | ||
TkWindow *childPtr; | ||
childPtr = TkpGetOtherWindow(winPtr); | ||
if (childPtr != NULL) { | ||
childPtr->flags |= TK_DONT_DESTROY_WINDOW; | ||
Tk_DestroyWindow((Tk_Window) childPtr); | ||
} | ||
} | ||
/* | ||
* Generate a DestroyNotify event. In order for the DestroyNotify | ||
* event to be processed correctly, need to make sure the window | ||
* exists. This is a bit of a kludge, and may be unnecessarily | ||
* expensive, but without it no event handlers will get called for | ||
* windows that don't exist yet. | ||
* | ||
* Note: if the window's pathName is NULL it means that the window | ||
* was not successfully initialized in the first place, so we should | ||
* not make the window exist or generate the event. | ||
*/ | ||
if (winPtr->pathName != NULL) { | ||
if (winPtr->window == None) { | ||
Tk_MakeWindowExist(tkwin); | ||
} | ||
event.type = DestroyNotify; | ||
event.xdestroywindow.serial = | ||
LastKnownRequestProcessed(winPtr->display); | ||
event.xdestroywindow.send_event = False; | ||
event.xdestroywindow.display = winPtr->display; | ||
event.xdestroywindow.event = winPtr->window; | ||
event.xdestroywindow.window = winPtr->window; | ||
Tk_HandleEvent(&event); | ||
} | ||
/* | ||
* Cleanup the data structures associated with this window. | ||
*/ | ||
if (winPtr->flags & TK_TOP_LEVEL) { | ||
TkWmDeadWindow(winPtr); | ||
} else if (winPtr->flags & TK_WM_COLORMAP_WINDOW) { | ||
TkWmRemoveFromColormapWindows(winPtr); | ||
} | ||
if (winPtr->window != None) { | ||
#if defined(MAC_TCL) || defined(__WIN32__) | ||
XDestroyWindow(winPtr->display, winPtr->window); | ||
#else | ||
if ((winPtr->flags & TK_TOP_LEVEL) | ||
|| !(winPtr->flags & TK_DONT_DESTROY_WINDOW)) { | ||
/* | ||
* The parent has already been destroyed and this isn't | ||
* a top-level window, so this window will be destroyed | ||
* implicitly when the parent's X window is destroyed; | ||
* it's much faster not to do an explicit destroy of this | ||
* X window. | ||
*/ | ||
dispPtr->lastDestroyRequest = NextRequest(winPtr->display); | ||
XDestroyWindow(winPtr->display, winPtr->window); | ||
} | ||
#endif | ||
TkFreeWindowId(dispPtr, winPtr->window); | ||
Tcl_DeleteHashEntry(Tcl_FindHashEntry(&dispPtr->winTable, | ||
(char *) winPtr->window)); | ||
winPtr->window = None; | ||
} | ||
dispPtr->destroyCount--; | ||
UnlinkWindow(winPtr); | ||
TkEventDeadWindow(winPtr); | ||
TkBindDeadWindow(winPtr); | ||
#ifdef TK_USE_INPUT_METHODS | ||
if (winPtr->inputContext != NULL) { | ||
XDestroyIC(winPtr->inputContext); | ||
} | ||
#endif /* TK_USE_INPUT_METHODS */ | ||
if (winPtr->tagPtr != NULL) { | ||
TkFreeBindingTags(winPtr); | ||
} | ||
TkOptionDeadWindow(winPtr); | ||
TkSelDeadWindow(winPtr); | ||
TkGrabDeadWindow(winPtr); | ||
if (winPtr->mainPtr != NULL) { | ||
if (winPtr->pathName != NULL) { | ||
Tk_DeleteAllBindings(winPtr->mainPtr->bindingTable, | ||
(ClientData) winPtr->pathName); | ||
Tcl_DeleteHashEntry(Tcl_FindHashEntry(&winPtr->mainPtr->nameTable, | ||
winPtr->pathName)); | ||
} | ||
winPtr->mainPtr->refCount--; | ||
if (winPtr->mainPtr->refCount == 0) { | ||
register TkCmd *cmdPtr; | ||
/* | ||
* We just deleted the last window in the application. Delete | ||
* the TkMainInfo structure too and replace all of Tk's commands | ||
* with dummy commands that return errors. Also delete the | ||
* "send" command to unregister the interpreter. | ||
* | ||
* NOTE: Only replace the commands it if the interpreter is | ||
* not being deleted. If it *is*, the interpreter cleanup will | ||
* do all the needed work. | ||
*/ | ||
if ((winPtr->mainPtr->interp != NULL) && | ||
(!Tcl_InterpDeleted(winPtr->mainPtr->interp))) { | ||
for (cmdPtr = commands; cmdPtr->name != NULL; cmdPtr++) { | ||
Tcl_CreateCommand(winPtr->mainPtr->interp, cmdPtr->name, | ||
TkDeadAppCmd, (ClientData) NULL, | ||
(void (*) _ANSI_ARGS_((ClientData))) NULL); | ||
} | ||
Tcl_CreateCommand(winPtr->mainPtr->interp, "send", | ||
TkDeadAppCmd, (ClientData) NULL, | ||
(void (*) _ANSI_ARGS_((ClientData))) NULL); | ||
Tcl_UnlinkVar(winPtr->mainPtr->interp, "tk_strictMotif"); | ||
} | ||
Tcl_DeleteHashTable(&winPtr->mainPtr->nameTable); | ||
TkBindFree(winPtr->mainPtr); | ||
TkDeleteAllImages(winPtr->mainPtr); | ||
TkFontPkgFree(winPtr->mainPtr); | ||
/* | ||
* When embedding Tk into other applications, make sure | ||
* that all destroy events reach the server. Otherwise | ||
* the embedding application may also attempt to destroy | ||
* the windows, resulting in an X error | ||
*/ | ||
if (winPtr->flags & TK_EMBEDDED) { | ||
XSync(winPtr->display,False) ; | ||
} | ||
ckfree((char *) winPtr->mainPtr); | ||
/* | ||
* If no other applications are using the display, close the | ||
* display now and relinquish its data structures. | ||
*/ | ||
if (dispPtr->refCount <= 0) { | ||
#ifdef NOT_YET | ||
/* | ||
* I have disabled this code because on Windows there are | ||
* still order dependencies in close-down. All displays | ||
* and resources will get closed down properly anyway at | ||
* exit, through the exit handler. | ||
*/ | ||
TkDisplay *theDispPtr, *backDispPtr; | ||
/* | ||
* Splice this display out of the list of displays. | ||
*/ | ||
for (theDispPtr = displayList, backDispPtr = NULL; | ||
(theDispPtr != winPtr->dispPtr) && | ||
(theDispPtr != NULL); | ||
theDispPtr = theDispPtr->nextPtr) { | ||
backDispPtr = theDispPtr; | ||
} | ||
if (theDispPtr == NULL) { | ||
panic("could not find display to close!"); | ||
} | ||
if (backDispPtr == NULL) { | ||
displayList = theDispPtr->nextPtr; | ||
} else { | ||
backDispPtr->nextPtr = theDispPtr->nextPtr; | ||
} | ||
/* | ||
* Found and spliced it out, now actually do the cleanup. | ||
*/ | ||
if (dispPtr->name != NULL) { | ||
ckfree(dispPtr->name); | ||
} | ||
Tcl_DeleteHashTable(&(dispPtr->winTable)); | ||
/* | ||
* Cannot yet close the display because we still have | ||
* order of deletion problems. Defer until exit handling | ||
* instead. At that time, the display will cleanly shut | ||
* down (hopefully..). (JYL) | ||
*/ | ||
TkpCloseDisplay(dispPtr); | ||
/* | ||
* There is lots more to clean up, we leave it at this for | ||
* the time being. | ||
*/ | ||
#endif | ||
} | ||
} | ||
} | ||
ckfree((char *) winPtr); | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* Tk_MapWindow -- | ||
* | ||
* Map a window within its parent. This may require the | ||
* window and/or its parents to actually be created. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* The given window will be mapped. Windows may also | ||
* be created. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
void | ||
Tk_MapWindow(tkwin) | ||
Tk_Window tkwin; /* Token for window to map. */ | ||
{ | ||
register TkWindow *winPtr = (TkWindow *) tkwin; | ||
XEvent event; | ||
if (winPtr->flags & TK_MAPPED) { | ||
return; | ||
} | ||
if (winPtr->window == None) { | ||
Tk_MakeWindowExist(tkwin); | ||
} | ||
if (winPtr->flags & TK_TOP_LEVEL) { | ||
/* | ||
* Lots of special processing has to be done for top-level | ||
* windows. Let tkWm.c handle everything itself. | ||
*/ | ||
TkWmMapWindow(winPtr); | ||
return; | ||
} | ||
winPtr->flags |= TK_MAPPED; | ||
XMapWindow(winPtr->display, winPtr->window); | ||
event.type = MapNotify; | ||
event.xmap.serial = LastKnownRequestProcessed(winPtr->display); | ||
event.xmap.send_event = False; | ||
event.xmap.display = winPtr->display; | ||
event.xmap.event = winPtr->window; | ||
event.xmap.window = winPtr->window; | ||
event.xmap.override_redirect = winPtr->atts.override_redirect; | ||
Tk_HandleEvent(&event); | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* Tk_MakeWindowExist -- | ||
* | ||
* Ensure that a particular window actually exists. This | ||
* procedure shouldn't normally need to be invoked from | ||
* outside the Tk package, but may be needed if someone | ||
* wants to manipulate a window before mapping it. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* When the procedure returns, the X window associated with | ||
* tkwin is guaranteed to exist. This may require the | ||
* window's ancestors to be created also. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
void | ||
Tk_MakeWindowExist(tkwin) | ||
Tk_Window tkwin; /* Token for window. */ | ||
{ | ||
register TkWindow *winPtr = (TkWindow *) tkwin; | ||
TkWindow *winPtr2; | ||
Window parent; | ||
Tcl_HashEntry *hPtr; | ||
int new; | ||
if (winPtr->window != None) { | ||
return; | ||
} | ||
if ((winPtr->parentPtr == NULL) || (winPtr->flags & TK_TOP_LEVEL)) { | ||
parent = XRootWindow(winPtr->display, winPtr->screenNum); | ||
} else { | ||
if (winPtr->parentPtr->window == None) { | ||
Tk_MakeWindowExist((Tk_Window) winPtr->parentPtr); | ||
} | ||
parent = winPtr->parentPtr->window; | ||
} | ||
if (winPtr->classProcsPtr != NULL | ||
&& winPtr->classProcsPtr->createProc != NULL) { | ||
winPtr->window = (*winPtr->classProcsPtr->createProc)(tkwin, parent, | ||
winPtr->instanceData); | ||
} else { | ||
winPtr->window = TkpMakeWindow(winPtr, parent); | ||
} | ||
hPtr = Tcl_CreateHashEntry(&winPtr->dispPtr->winTable, | ||
(char *) winPtr->window, &new); | ||
Tcl_SetHashValue(hPtr, winPtr); | ||
winPtr->dirtyAtts = 0; | ||
winPtr->dirtyChanges = 0; | ||
#ifdef TK_USE_INPUT_METHODS | ||
winPtr->inputContext = NULL; | ||
#endif /* TK_USE_INPUT_METHODS */ | ||
if (!(winPtr->flags & TK_TOP_LEVEL)) { | ||
/* | ||
* If any siblings higher up in the stacking order have already | ||
* been created then move this window to its rightful position | ||
* in the stacking order. | ||
* | ||
* NOTE: this code ignores any changes anyone might have made | ||
* to the sibling and stack_mode field of the window's attributes, | ||
* so it really isn't safe for these to be manipulated except | ||
* by calling Tk_RestackWindow. | ||
*/ | ||
for (winPtr2 = winPtr->nextPtr; winPtr2 != NULL; | ||
winPtr2 = winPtr2->nextPtr) { | ||
if ((winPtr2->window != None) | ||
&& !(winPtr2->flags & (TK_TOP_LEVEL|TK_REPARENTED))) { | ||
XWindowChanges changes; | ||
changes.sibling = winPtr2->window; | ||
changes.stack_mode = Below; | ||
XConfigureWindow(winPtr->display, winPtr->window, | ||
CWSibling|CWStackMode, &changes); | ||
break; | ||
} | ||
} | ||
/* | ||
* If this window has a different colormap than its parent, add | ||
* the window to the WM_COLORMAP_WINDOWS property for its top-level. | ||
*/ | ||
if ((winPtr->parentPtr != NULL) && | ||
(winPtr->atts.colormap != winPtr->parentPtr->atts.colormap)) { | ||
TkWmAddToColormapWindows(winPtr); | ||
winPtr->flags |= TK_WM_COLORMAP_WINDOW; | ||
} | ||
} | ||
/* | ||
* Issue a ConfigureNotify event if there were deferred configuration | ||
* changes (but skip it if the window is being deleted; the | ||
* ConfigureNotify event could cause problems if we're being called | ||
* from Tk_DestroyWindow under some conditions). | ||
*/ | ||
if ((winPtr->flags & TK_NEED_CONFIG_NOTIFY) | ||
&& !(winPtr->flags & TK_ALREADY_DEAD)){ | ||
winPtr->flags &= ~TK_NEED_CONFIG_NOTIFY; | ||
TkDoConfigureNotify(winPtr); | ||
} | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* Tk_UnmapWindow, etc. -- | ||
* | ||
* There are several procedures under here, each of which | ||
* mirrors an existing X procedure. In addition to performing | ||
* the functions of the corresponding procedure, each | ||
* procedure also updates the local window structure and | ||
* synthesizes an X event (if the window's structure is being | ||
* managed internally). | ||
* | ||
* Results: | ||
* See the manual entries. | ||
* | ||
* Side effects: | ||
* See the manual entries. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
void | ||
Tk_UnmapWindow(tkwin) | ||
Tk_Window tkwin; /* Token for window to unmap. */ | ||
{ | ||
register TkWindow *winPtr = (TkWindow *) tkwin; | ||
if (!(winPtr->flags & TK_MAPPED) || (winPtr->flags & TK_ALREADY_DEAD)) { | ||
return; | ||
} | ||
if (winPtr->flags & TK_TOP_LEVEL) { | ||
/* | ||
* Special processing has to be done for top-level windows. Let | ||
* tkWm.c handle everything itself. | ||
*/ | ||
TkWmUnmapWindow(winPtr); | ||
return; | ||
} | ||
winPtr->flags &= ~TK_MAPPED; | ||
XUnmapWindow(winPtr->display, winPtr->window); | ||
if (!(winPtr->flags & TK_TOP_LEVEL)) { | ||
XEvent event; | ||
event.type = UnmapNotify; | ||
event.xunmap.serial = LastKnownRequestProcessed(winPtr->display); | ||
event.xunmap.send_event = False; | ||
event.xunmap.display = winPtr->display; | ||
event.xunmap.event = winPtr->window; | ||
event.xunmap.window = winPtr->window; | ||
event.xunmap.from_configure = False; | ||
Tk_HandleEvent(&event); | ||
} | ||
} | ||
void | ||
Tk_ConfigureWindow(tkwin, valueMask, valuePtr) | ||
Tk_Window tkwin; /* Window to re-configure. */ | ||
unsigned int valueMask; /* Mask indicating which parts of | ||
* *valuePtr are to be used. */ | ||
XWindowChanges *valuePtr; /* New values. */ | ||
{ | ||
register TkWindow *winPtr = (TkWindow *) tkwin; | ||
if (valueMask & CWX) { | ||
winPtr->changes.x = valuePtr->x; | ||
} | ||
if (valueMask & CWY) { | ||
winPtr->changes.y = valuePtr->y; | ||
} | ||
if (valueMask & CWWidth) { | ||
winPtr->changes.width = valuePtr->width; | ||
} | ||
if (valueMask & CWHeight) { | ||
winPtr->changes.height = valuePtr->height; | ||
} | ||
if (valueMask & CWBorderWidth) { | ||
winPtr->changes.border_width = valuePtr->border_width; | ||
} | ||
if (valueMask & (CWSibling|CWStackMode)) { | ||
panic("Can't set sibling or stack mode from Tk_ConfigureWindow."); | ||
} | ||
if (winPtr->window != None) { | ||
XConfigureWindow(winPtr->display, winPtr->window, | ||
valueMask, valuePtr); | ||
TkDoConfigureNotify(winPtr); | ||
} else { | ||
winPtr->dirtyChanges |= valueMask; | ||
winPtr->flags |= TK_NEED_CONFIG_NOTIFY; | ||
} | ||
} | ||
void | ||
Tk_MoveWindow(tkwin, x, y) | ||
Tk_Window tkwin; /* Window to move. */ | ||
int x, y; /* New location for window (within | ||
* parent). */ | ||
{ | ||
register TkWindow *winPtr = (TkWindow *) tkwin; | ||
winPtr->changes.x = x; | ||
winPtr->changes.y = y; | ||
if (winPtr->window != None) { | ||
XMoveWindow(winPtr->display, winPtr->window, x, y); | ||
TkDoConfigureNotify(winPtr); | ||
} else { | ||
winPtr->dirtyChanges |= CWX|CWY; | ||
winPtr->flags |= TK_NEED_CONFIG_NOTIFY; | ||
} | ||
} | ||
void | ||
Tk_ResizeWindow(tkwin, width, height) | ||
Tk_Window tkwin; /* Window to resize. */ | ||
int width, height; /* New dimensions for window. */ | ||
{ | ||
register TkWindow *winPtr = (TkWindow *) tkwin; | ||
winPtr->changes.width = (unsigned) width; | ||
winPtr->changes.height = (unsigned) height; | ||
if (winPtr->window != None) { | ||
XResizeWindow(winPtr->display, winPtr->window, (unsigned) width, | ||
(unsigned) height); | ||
TkDoConfigureNotify(winPtr); | ||
} else { | ||
winPtr->dirtyChanges |= CWWidth|CWHeight; | ||
winPtr->flags |= TK_NEED_CONFIG_NOTIFY; | ||
} | ||
} | ||
void | ||
Tk_MoveResizeWindow(tkwin, x, y, width, height) | ||
Tk_Window tkwin; /* Window to move and resize. */ | ||
int x, y; /* New location for window (within | ||
* parent). */ | ||
int width, height; /* New dimensions for window. */ | ||
{ | ||
register TkWindow *winPtr = (TkWindow *) tkwin; | ||
winPtr->changes.x = x; | ||
winPtr->changes.y = y; | ||
winPtr->changes.width = (unsigned) width; | ||
winPtr->changes.height = (unsigned) height; | ||
if (winPtr->window != None) { | ||
XMoveResizeWindow(winPtr->display, winPtr->window, x, y, | ||
(unsigned) width, (unsigned) height); | ||
TkDoConfigureNotify(winPtr); | ||
} else { | ||
winPtr->dirtyChanges |= CWX|CWY|CWWidth|CWHeight; | ||
winPtr->flags |= TK_NEED_CONFIG_NOTIFY; | ||
} | ||
} | ||
void | ||
Tk_SetWindowBorderWidth(tkwin, width) | ||
Tk_Window tkwin; /* Window to modify. */ | ||
int width; /* New border width for window. */ | ||
{ | ||
register TkWindow *winPtr = (TkWindow *) tkwin; | ||
winPtr->changes.border_width = width; | ||
if (winPtr->window != None) { | ||
XSetWindowBorderWidth(winPtr->display, winPtr->window, | ||
(unsigned) width); | ||
TkDoConfigureNotify(winPtr); | ||
} else { | ||
winPtr->dirtyChanges |= CWBorderWidth; | ||
winPtr->flags |= TK_NEED_CONFIG_NOTIFY; | ||
} | ||
} | ||
void | ||
Tk_ChangeWindowAttributes(tkwin, valueMask, attsPtr) | ||
Tk_Window tkwin; /* Window to manipulate. */ | ||
unsigned long valueMask; /* OR'ed combination of bits, | ||
* indicating which fields of | ||
* *attsPtr are to be used. */ | ||
register XSetWindowAttributes *attsPtr; | ||
/* New values for some attributes. */ | ||
{ | ||
register TkWindow *winPtr = (TkWindow *) tkwin; | ||
if (valueMask & CWBackPixmap) { | ||
winPtr->atts.background_pixmap = attsPtr->background_pixmap; | ||
} | ||
if (valueMask & CWBackPixel) { | ||
winPtr->atts.background_pixel = attsPtr->background_pixel; | ||
} | ||
if (valueMask & CWBorderPixmap) { | ||
winPtr->atts.border_pixmap = attsPtr->border_pixmap; | ||
} | ||
if (valueMask & CWBorderPixel) { | ||
winPtr->atts.border_pixel = attsPtr->border_pixel; | ||
} | ||
if (valueMask & CWBitGravity) { | ||
winPtr->atts.bit_gravity = attsPtr->bit_gravity; | ||
} | ||
if (valueMask & CWWinGravity) { | ||
winPtr->atts.win_gravity = attsPtr->win_gravity; | ||
} | ||
if (valueMask & CWBackingStore) { | ||
winPtr->atts.backing_store = attsPtr->backing_store; | ||
} | ||
if (valueMask & CWBackingPlanes) { | ||
winPtr->atts.backing_planes = attsPtr->backing_planes; | ||
} | ||
if (valueMask & CWBackingPixel) { | ||
winPtr->atts.backing_pixel = attsPtr->backing_pixel; | ||
} | ||
if (valueMask & CWOverrideRedirect) { | ||
winPtr->atts.override_redirect = attsPtr->override_redirect; | ||
} | ||
if (valueMask & CWSaveUnder) { | ||
winPtr->atts.save_under = attsPtr->save_under; | ||
} | ||
if (valueMask & CWEventMask) { | ||
winPtr->atts.event_mask = attsPtr->event_mask; | ||
} | ||
if (valueMask & CWDontPropagate) { | ||
winPtr->atts.do_not_propagate_mask | ||
= attsPtr->do_not_propagate_mask; | ||
} | ||
if (valueMask & CWColormap) { | ||
winPtr->atts.colormap = attsPtr->colormap; | ||
} | ||
if (valueMask & CWCursor) { | ||
winPtr->atts.cursor = attsPtr->cursor; | ||
} | ||
if (winPtr->window != None) { | ||
XChangeWindowAttributes(winPtr->display, winPtr->window, | ||
valueMask, attsPtr); | ||
} else { | ||
winPtr->dirtyAtts |= valueMask; | ||
} | ||
} | ||
void | ||
Tk_SetWindowBackground(tkwin, pixel) | ||
Tk_Window tkwin; /* Window to manipulate. */ | ||
unsigned long pixel; /* Pixel value to use for | ||
* window's background. */ | ||
{ | ||
register TkWindow *winPtr = (TkWindow *) tkwin; | ||
winPtr->atts.background_pixel = pixel; | ||
if (winPtr->window != None) { | ||
XSetWindowBackground(winPtr->display, winPtr->window, pixel); | ||
} else { | ||
winPtr->dirtyAtts = (winPtr->dirtyAtts & (unsigned) ~CWBackPixmap) | ||
| CWBackPixel; | ||
} | ||
} | ||
void | ||
Tk_SetWindowBackgroundPixmap(tkwin, pixmap) | ||
Tk_Window tkwin; /* Window to manipulate. */ | ||
Pixmap pixmap; /* Pixmap to use for window's | ||
* background. */ | ||
{ | ||
register TkWindow *winPtr = (TkWindow *) tkwin; | ||
winPtr->atts.background_pixmap = pixmap; | ||
if (winPtr->window != None) { | ||
XSetWindowBackgroundPixmap(winPtr->display, | ||
winPtr->window, pixmap); | ||
} else { | ||
winPtr->dirtyAtts = (winPtr->dirtyAtts & (unsigned) ~CWBackPixel) | ||
| CWBackPixmap; | ||
} | ||
} | ||
void | ||
Tk_SetWindowBorder(tkwin, pixel) | ||
Tk_Window tkwin; /* Window to manipulate. */ | ||
unsigned long pixel; /* Pixel value to use for | ||
* window's border. */ | ||
{ | ||
register TkWindow *winPtr = (TkWindow *) tkwin; | ||
winPtr->atts.border_pixel = pixel; | ||
if (winPtr->window != None) { | ||
XSetWindowBorder(winPtr->display, winPtr->window, pixel); | ||
} else { | ||
winPtr->dirtyAtts = (winPtr->dirtyAtts & (unsigned) ~CWBorderPixmap) | ||
| CWBorderPixel; | ||
} | ||
} | ||
void | ||
Tk_SetWindowBorderPixmap(tkwin, pixmap) | ||
Tk_Window tkwin; /* Window to manipulate. */ | ||
Pixmap pixmap; /* Pixmap to use for window's | ||
* border. */ | ||
{ | ||
register TkWindow *winPtr = (TkWindow *) tkwin; | ||
winPtr->atts.border_pixmap = pixmap; | ||
if (winPtr->window != None) { | ||
XSetWindowBorderPixmap(winPtr->display, | ||
winPtr->window, pixmap); | ||
} else { | ||
winPtr->dirtyAtts = (winPtr->dirtyAtts & (unsigned) ~CWBorderPixel) | ||
| CWBorderPixmap; | ||
} | ||
} | ||
void | ||
Tk_DefineCursor(tkwin, cursor) | ||
Tk_Window tkwin; /* Window to manipulate. */ | ||
Tk_Cursor cursor; /* Cursor to use for window (may be None). */ | ||
{ | ||
register TkWindow *winPtr = (TkWindow *) tkwin; | ||
#ifdef MAC_TCL | ||
winPtr->atts.cursor = (XCursor) cursor; | ||
#else | ||
winPtr->atts.cursor = (Cursor) cursor; | ||
#endif | ||
if (winPtr->window != None) { | ||
XDefineCursor(winPtr->display, winPtr->window, winPtr->atts.cursor); | ||
} else { | ||
winPtr->dirtyAtts = winPtr->dirtyAtts | CWCursor; | ||
} | ||
} | ||
void | ||
Tk_UndefineCursor(tkwin) | ||
Tk_Window tkwin; /* Window to manipulate. */ | ||
{ | ||
Tk_DefineCursor(tkwin, None); | ||
} | ||
void | ||
Tk_SetWindowColormap(tkwin, colormap) | ||
Tk_Window tkwin; /* Window to manipulate. */ | ||
Colormap colormap; /* Colormap to use for window. */ | ||
{ | ||
register TkWindow *winPtr = (TkWindow *) tkwin; | ||
winPtr->atts.colormap = colormap; | ||
if (winPtr->window != None) { | ||
XSetWindowColormap(winPtr->display, winPtr->window, colormap); | ||
if (!(winPtr->flags & TK_TOP_LEVEL)) { | ||
TkWmAddToColormapWindows(winPtr); | ||
winPtr->flags |= TK_WM_COLORMAP_WINDOW; | ||
} | ||
} else { | ||
winPtr->dirtyAtts |= CWColormap; | ||
} | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tk_SetWindowVisual -- | ||
* | ||
* This procedure is called to specify a visual to be used | ||
* for a Tk window when it is created. This procedure, if | ||
* called at all, must be called before the X window is created | ||
* (i.e. before Tk_MakeWindowExist is called). | ||
* | ||
* Results: | ||
* The return value is 1 if successful, or 0 if the X window has | ||
* been already created. | ||
* | ||
* Side effects: | ||
* The information given is stored for when the window is created. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
Tk_SetWindowVisual(tkwin, visual, depth, colormap) | ||
Tk_Window tkwin; /* Window to manipulate. */ | ||
Visual *visual; /* New visual for window. */ | ||
int depth; /* New depth for window. */ | ||
Colormap colormap; /* An appropriate colormap for the visual. */ | ||
{ | ||
register TkWindow *winPtr = (TkWindow *) tkwin; | ||
if( winPtr->window != None ){ | ||
/* Too late! */ | ||
return 0; | ||
} | ||
winPtr->visual = visual; | ||
winPtr->depth = depth; | ||
winPtr->atts.colormap = colormap; | ||
winPtr->dirtyAtts |= CWColormap; | ||
/* | ||
* The following code is needed to make sure that the window doesn't | ||
* inherit the parent's border pixmap, which would result in a BadMatch | ||
* error. | ||
*/ | ||
if (!(winPtr->dirtyAtts & CWBorderPixmap)) { | ||
winPtr->dirtyAtts |= CWBorderPixel; | ||
} | ||
return 1; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TkDoConfigureNotify -- | ||
* | ||
* Generate a ConfigureNotify event describing the current | ||
* configuration of a window. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* An event is generated and processed by Tk_HandleEvent. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
void | ||
TkDoConfigureNotify(winPtr) | ||
register TkWindow *winPtr; /* Window whose configuration | ||
* was just changed. */ | ||
{ | ||
XEvent event; | ||
event.type = ConfigureNotify; | ||
event.xconfigure.serial = LastKnownRequestProcessed(winPtr->display); | ||
event.xconfigure.send_event = False; | ||
event.xconfigure.display = winPtr->display; | ||
event.xconfigure.event = winPtr->window; | ||
event.xconfigure.window = winPtr->window; | ||
event.xconfigure.x = winPtr->changes.x; | ||
event.xconfigure.y = winPtr->changes.y; | ||
event.xconfigure.width = winPtr->changes.width; | ||
event.xconfigure.height = winPtr->changes.height; | ||
event.xconfigure.border_width = winPtr->changes.border_width; | ||
if (winPtr->changes.stack_mode == Above) { | ||
event.xconfigure.above = winPtr->changes.sibling; | ||
} else { | ||
event.xconfigure.above = None; | ||
} | ||
event.xconfigure.override_redirect = winPtr->atts.override_redirect; | ||
Tk_HandleEvent(&event); | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tk_SetClass -- | ||
* | ||
* This procedure is used to give a window a class. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* A new class is stored for tkwin, replacing any existing | ||
* class for it. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
void | ||
Tk_SetClass(tkwin, className) | ||
Tk_Window tkwin; /* Token for window to assign class. */ | ||
char *className; /* New class for tkwin. */ | ||
{ | ||
register TkWindow *winPtr = (TkWindow *) tkwin; | ||
winPtr->classUid = Tk_GetUid(className); | ||
if (winPtr->flags & TK_TOP_LEVEL) { | ||
TkWmSetClass(winPtr); | ||
} | ||
TkOptionClassChanged(winPtr); | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TkSetClassProcs -- | ||
* | ||
* This procedure is used to set the class procedures and | ||
* instance data for a window. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* A new set of class procedures and instance data is stored | ||
* for tkwin, replacing any existing values. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
void | ||
TkSetClassProcs(tkwin, procs, instanceData) | ||
Tk_Window tkwin; /* Token for window to modify. */ | ||
TkClassProcs *procs; /* Class procs structure. */ | ||
ClientData instanceData; /* Data to be passed to class procedures. */ | ||
{ | ||
register TkWindow *winPtr = (TkWindow *) tkwin; | ||
winPtr->classProcsPtr = procs; | ||
winPtr->instanceData = instanceData; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tk_NameToWindow -- | ||
* | ||
* Given a string name for a window, this procedure | ||
* returns the token for the window, if there exists a | ||
* window corresponding to the given name. | ||
* | ||
* Results: | ||
* The return result is either a token for the window corresponding | ||
* to "name", or else NULL to indicate that there is no such | ||
* window. In this case, an error message is left in the interp's result. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
Tk_Window | ||
Tk_NameToWindow(interp, pathName, tkwin) | ||
Tcl_Interp *interp; /* Where to report errors. */ | ||
char *pathName; /* Path name of window. */ | ||
Tk_Window tkwin; /* Token for window: name is assumed to | ||
* belong to the same main window as tkwin. */ | ||
{ | ||
Tcl_HashEntry *hPtr; | ||
if (tkwin == NULL) { | ||
/* | ||
* Either we're not really in Tk, or the main window was destroyed and | ||
* we're on our way out of the application | ||
*/ | ||
Tcl_AppendResult(interp, "NULL main window", (char *)NULL); | ||
return NULL; | ||
} | ||
hPtr = Tcl_FindHashEntry(&((TkWindow *) tkwin)->mainPtr->nameTable, | ||
pathName); | ||
if (hPtr == NULL) { | ||
Tcl_AppendResult(interp, "bad window path name \"", | ||
pathName, "\"", (char *) NULL); | ||
return NULL; | ||
} | ||
return (Tk_Window) Tcl_GetHashValue(hPtr); | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tk_IdToWindow -- | ||
* | ||
* Given an X display and window ID, this procedure returns the | ||
* Tk token for the window, if there exists a Tk window corresponding | ||
* to the given ID. | ||
* | ||
* Results: | ||
* The return result is either a token for the window corresponding | ||
* to the given X id, or else NULL to indicate that there is no such | ||
* window. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
Tk_Window | ||
Tk_IdToWindow(display, window) | ||
Display *display; /* X display containing the window. */ | ||
Window window; /* X window window id. */ | ||
{ | ||
TkDisplay *dispPtr; | ||
Tcl_HashEntry *hPtr; | ||
for (dispPtr = TkGetDisplayList(); ; dispPtr = dispPtr->nextPtr) { | ||
if (dispPtr == NULL) { | ||
return NULL; | ||
} | ||
if (dispPtr->display == display) { | ||
break; | ||
} | ||
} | ||
hPtr = Tcl_FindHashEntry(&dispPtr->winTable, (char *) window); | ||
if (hPtr == NULL) { | ||
return NULL; | ||
} | ||
return (Tk_Window) Tcl_GetHashValue(hPtr); | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tk_DisplayName -- | ||
* | ||
* Return the textual name of a window's display. | ||
* | ||
* Results: | ||
* The return value is the string name of the display associated | ||
* with tkwin. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
char * | ||
Tk_DisplayName(tkwin) | ||
Tk_Window tkwin; /* Window whose display name is desired. */ | ||
{ | ||
return ((TkWindow *) tkwin)->dispPtr->name; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* UnlinkWindow -- | ||
* | ||
* This procedure removes a window from the childList of its | ||
* parent. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* The window is unlinked from its childList. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static void | ||
UnlinkWindow(winPtr) | ||
TkWindow *winPtr; /* Child window to be unlinked. */ | ||
{ | ||
TkWindow *prevPtr; | ||
if (winPtr->parentPtr == NULL) { | ||
return; | ||
} | ||
prevPtr = winPtr->parentPtr->childList; | ||
if (prevPtr == winPtr) { | ||
winPtr->parentPtr->childList = winPtr->nextPtr; | ||
if (winPtr->nextPtr == NULL) { | ||
winPtr->parentPtr->lastChildPtr = NULL; | ||
} | ||
} else { | ||
while (prevPtr->nextPtr != winPtr) { | ||
prevPtr = prevPtr->nextPtr; | ||
if (prevPtr == NULL) { | ||
panic("UnlinkWindow couldn't find child in parent"); | ||
} | ||
} | ||
prevPtr->nextPtr = winPtr->nextPtr; | ||
if (winPtr->nextPtr == NULL) { | ||
winPtr->parentPtr->lastChildPtr = prevPtr; | ||
} | ||
} | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tk_RestackWindow -- | ||
* | ||
* Change a window's position in the stacking order. | ||
* | ||
* Results: | ||
* TCL_OK is normally returned. If other is not a descendant | ||
* of tkwin's parent then TCL_ERROR is returned and tkwin is | ||
* not repositioned. | ||
* | ||
* Side effects: | ||
* Tkwin is repositioned in the stacking order. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
Tk_RestackWindow(tkwin, aboveBelow, other) | ||
Tk_Window tkwin; /* Token for window whose position in | ||
* the stacking order is to change. */ | ||
int aboveBelow; /* Indicates new position of tkwin relative | ||
* to other; must be Above or Below. */ | ||
Tk_Window other; /* Tkwin will be moved to a position that | ||
* puts it just above or below this window. | ||
* If NULL then tkwin goes above or below | ||
* all windows in the same parent. */ | ||
{ | ||
TkWindow *winPtr = (TkWindow *) tkwin; | ||
TkWindow *otherPtr = (TkWindow *) other; | ||
/* | ||
* Special case: if winPtr is a top-level window then just find | ||
* the top-level ancestor of otherPtr and restack winPtr above | ||
* otherPtr without changing any of Tk's childLists. | ||
*/ | ||
if (winPtr->flags & TK_TOP_LEVEL) { | ||
while ((otherPtr != NULL) && !(otherPtr->flags & TK_TOP_LEVEL)) { | ||
otherPtr = otherPtr->parentPtr; | ||
} | ||
TkWmRestackToplevel(winPtr, aboveBelow, otherPtr); | ||
return TCL_OK; | ||
} | ||
/* | ||
* Find an ancestor of otherPtr that is a sibling of winPtr. | ||
*/ | ||
if (winPtr->parentPtr == NULL) { | ||
/* | ||
* Window is going to be deleted shortly; don't do anything. | ||
*/ | ||
return TCL_OK; | ||
} | ||
if (otherPtr == NULL) { | ||
if (aboveBelow == Above) { | ||
otherPtr = winPtr->parentPtr->lastChildPtr; | ||
} else { | ||
otherPtr = winPtr->parentPtr->childList; | ||
} | ||
} else { | ||
while (winPtr->parentPtr != otherPtr->parentPtr) { | ||
if ((otherPtr == NULL) || (otherPtr->flags & TK_TOP_LEVEL)) { | ||
return TCL_ERROR; | ||
} | ||
otherPtr = otherPtr->parentPtr; | ||
} | ||
} | ||
if (otherPtr == winPtr) { | ||
return TCL_OK; | ||
} | ||
/* | ||
* Reposition winPtr in the stacking order. | ||
*/ | ||
UnlinkWindow(winPtr); | ||
if (aboveBelow == Above) { | ||
winPtr->nextPtr = otherPtr->nextPtr; | ||
if (winPtr->nextPtr == NULL) { | ||
winPtr->parentPtr->lastChildPtr = winPtr; | ||
} | ||
otherPtr->nextPtr = winPtr; | ||
} else { | ||
TkWindow *prevPtr; | ||
prevPtr = winPtr->parentPtr->childList; | ||
if (prevPtr == otherPtr) { | ||
winPtr->parentPtr->childList = winPtr; | ||
} else { | ||
while (prevPtr->nextPtr != otherPtr) { | ||
prevPtr = prevPtr->nextPtr; | ||
} | ||
prevPtr->nextPtr = winPtr; | ||
} | ||
winPtr->nextPtr = otherPtr; | ||
} | ||
/* | ||
* Notify the X server of the change. If winPtr hasn't yet been | ||
* created then there's no need to tell the X server now, since | ||
* the stacking order will be handled properly when the window | ||
* is finally created. | ||
*/ | ||
if (winPtr->window != None) { | ||
XWindowChanges changes; | ||
unsigned int mask; | ||
mask = CWStackMode; | ||
changes.stack_mode = Above; | ||
for (otherPtr = winPtr->nextPtr; otherPtr != NULL; | ||
otherPtr = otherPtr->nextPtr) { | ||
if ((otherPtr->window != None) | ||
&& !(otherPtr->flags & (TK_TOP_LEVEL|TK_REPARENTED))){ | ||
changes.sibling = otherPtr->window; | ||
changes.stack_mode = Below; | ||
mask = CWStackMode|CWSibling; | ||
break; | ||
} | ||
} | ||
XConfigureWindow(winPtr->display, winPtr->window, mask, &changes); | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tk_MainWindow -- | ||
* | ||
* Returns the main window for an application. | ||
* | ||
* Results: | ||
* If interp has a Tk application associated with it, the main | ||
* window for the application is returned. Otherwise NULL is | ||
* returned and an error message is left in the interp's result. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
Tk_Window | ||
Tk_MainWindow(interp) | ||
Tcl_Interp *interp; /* Interpreter that embodies the | ||
* application. Used for error | ||
* reporting also. */ | ||
{ | ||
TkMainInfo *mainPtr; | ||
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) | ||
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); | ||
for (mainPtr = tsdPtr->mainWindowList; mainPtr != NULL; | ||
mainPtr = mainPtr->nextPtr) { | ||
if (mainPtr->interp == interp) { | ||
return (Tk_Window) mainPtr->winPtr; | ||
} | ||
} | ||
Tcl_SetResult(interp, "this isn't a Tk application", TCL_STATIC); | ||
return NULL; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tk_StrictMotif -- | ||
* | ||
* Indicates whether strict Motif compliance has been specified | ||
* for the given window. | ||
* | ||
* Results: | ||
* The return value is 1 if strict Motif compliance has been | ||
* requested for tkwin's application by setting the tk_strictMotif | ||
* variable in its interpreter to a true value. 0 is returned | ||
* if tk_strictMotif has a false value. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
Tk_StrictMotif(tkwin) | ||
Tk_Window tkwin; /* Window whose application is | ||
* to be checked. */ | ||
{ | ||
return ((TkWindow *) tkwin)->mainPtr->strictMotif; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* OpenIM -- | ||
* | ||
* Tries to open an X input method, associated with the | ||
* given display. Right now we can only deal with a bare-bones | ||
* input style: no preedit, and no status. | ||
* | ||
* Results: | ||
* Stores the input method in dispPtr->inputMethod; if there isn't | ||
* a suitable input method, then NULL is stored in dispPtr->inputMethod. | ||
* | ||
* Side effects: | ||
* An input method gets opened. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static void | ||
OpenIM(dispPtr) | ||
TkDisplay *dispPtr; /* Tk's structure for the display. */ | ||
{ | ||
#ifndef TK_USE_INPUT_METHODS | ||
return; | ||
#else | ||
unsigned short i; | ||
XIMStyles *stylePtr; | ||
dispPtr->inputMethod = XOpenIM(dispPtr->display, NULL, NULL, NULL); | ||
if (dispPtr->inputMethod == NULL) { | ||
return; | ||
} | ||
if ((XGetIMValues(dispPtr->inputMethod, XNQueryInputStyle, &stylePtr, | ||
NULL) != NULL) || (stylePtr == NULL)) { | ||
goto error; | ||
} | ||
for (i = 0; i < stylePtr->count_styles; i++) { | ||
if (stylePtr->supported_styles[i] | ||
== (XIMPreeditNothing|XIMStatusNothing)) { | ||
XFree(stylePtr); | ||
return; | ||
} | ||
} | ||
XFree(stylePtr); | ||
error: | ||
/* | ||
* Should close the input method, but this causes core dumps on some | ||
* systems (e.g. Solaris 2.3 as of 1/6/95). | ||
* XCloseIM(dispPtr->inputMethod); | ||
*/ | ||
dispPtr->inputMethod = NULL; | ||
return; | ||
#endif /* TK_USE_INPUT_METHODS */ | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tk_GetNumMainWindows -- | ||
* | ||
* This procedure returns the number of main windows currently | ||
* open in this process. | ||
* | ||
* Results: | ||
* The number of main windows open in this process. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
Tk_GetNumMainWindows() | ||
{ | ||
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) | ||
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); | ||
return tsdPtr->numMainWindows; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* DeleteWindowsExitProc -- | ||
* | ||
* This procedure is invoked as an exit handler. It deletes all | ||
* of the main windows in the process. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static void | ||
DeleteWindowsExitProc(clientData) | ||
ClientData clientData; /* Not used. */ | ||
{ | ||
TkDisplay *displayPtr, *nextPtr; | ||
Tcl_Interp *interp; | ||
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) | ||
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); | ||
while (tsdPtr->mainWindowList != NULL) { | ||
/* | ||
* We must protect the interpreter while deleting the window, | ||
* because of <Destroy> bindings which could destroy the interpreter | ||
* while the window is being deleted. This would leave frames on | ||
* the call stack pointing at deleted memory, causing core dumps. | ||
*/ | ||
interp = tsdPtr->mainWindowList->winPtr->mainPtr->interp; | ||
Tcl_Preserve((ClientData) interp); | ||
Tk_DestroyWindow((Tk_Window) tsdPtr->mainWindowList->winPtr); | ||
Tcl_Release((ClientData) interp); | ||
} | ||
displayPtr = tsdPtr->displayList; | ||
tsdPtr->displayList = NULL; | ||
/* | ||
* Iterate destroying the displays until no more displays remain. | ||
* It is possible for displays to get recreated during exit by any | ||
* code that calls GetScreen, so we must destroy these new displays | ||
* as well as the old ones. | ||
*/ | ||
for (displayPtr = tsdPtr->displayList; | ||
displayPtr != NULL; | ||
displayPtr = tsdPtr->displayList) { | ||
/* | ||
* Now iterate over the current list of open displays, and first | ||
* set the global pointer to NULL so we will be able to notice if | ||
* any new displays got created during deletion of the current set. | ||
* We must also do this to ensure that Tk_IdToWindow does not find | ||
* the old display as it is being destroyed, when it wants to see | ||
* if it needs to dispatch a message. | ||
*/ | ||
for (tsdPtr->displayList = NULL; displayPtr != NULL; | ||
displayPtr = nextPtr) { | ||
nextPtr = displayPtr->nextPtr; | ||
if (displayPtr->name != (char *) NULL) { | ||
ckfree(displayPtr->name); | ||
} | ||
Tcl_DeleteHashTable(&(displayPtr->winTable)); | ||
TkpCloseDisplay(displayPtr); | ||
} | ||
} | ||
tsdPtr->numMainWindows = 0; | ||
tsdPtr->mainWindowList = NULL; | ||
tsdPtr->initialized = 0; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tk_Init -- | ||
* | ||
* This procedure is invoked to add Tk to an interpreter. It | ||
* incorporates all of Tk's commands into the interpreter and | ||
* creates the main window for a new Tk application. If the | ||
* interpreter contains a variable "argv", this procedure | ||
* extracts several arguments from that variable, uses them | ||
* to configure the main window, and modifies argv to exclude | ||
* the arguments (see the "wish" documentation for a list of | ||
* the arguments that are extracted). | ||
* | ||
* Results: | ||
* Returns a standard Tcl completion code and sets the interp's result | ||
* if there is an error. | ||
* | ||
* Side effects: | ||
* Depends on various initialization scripts that get invoked. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
Tk_Init(interp) | ||
Tcl_Interp *interp; /* Interpreter to initialize. */ | ||
{ | ||
return Initialize(interp); | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tk_SafeInit -- | ||
* | ||
* This procedure is invoked to add Tk to a safe interpreter. It | ||
* invokes the internal procedure that does the real work. | ||
* | ||
* Results: | ||
* Returns a standard Tcl completion code and sets the interp's result | ||
* if there is an error. | ||
* | ||
* Side effects: | ||
* Depends on various initialization scripts that are invoked. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
Tk_SafeInit(interp) | ||
Tcl_Interp *interp; /* Interpreter to initialize. */ | ||
{ | ||
/* | ||
* Initialize the interpreter with Tk, safely. This removes | ||
* all the Tk commands that are unsafe. | ||
* | ||
* Rationale: | ||
* | ||
* - Toplevel and menu are unsafe because they can be used to cover | ||
* the entire screen and to steal input from the user. | ||
* - Continuous ringing of the bell is a nuisance. | ||
* - Cannot allow access to the clipboard because a malicious script | ||
* can replace the contents with the string "rm -r *" and lead to | ||
* surprises when the contents of the clipboard are pasted. We do | ||
* not currently hide the selection command.. Should we? | ||
* - Cannot allow send because it can be used to cause unsafe | ||
* interpreters to execute commands. The tk command recreates the | ||
* send command, so that too must be hidden. | ||
* - Focus can be used to grab the focus away from another window, | ||
* in effect stealing user input. Cannot allow that. | ||
* NOTE: We currently do *not* hide focus as it would make it | ||
* impossible to provide keyboard input to Tk in a safe interpreter. | ||
* - Grab can be used to block the user from using any other apps | ||
* on the screen. | ||
* - Tkwait can block the containing process forever. Use bindings, | ||
* fileevents and split the protocol into before-the-wait and | ||
* after-the-wait parts. More work but necessary. | ||
* - Wm is unsafe because (if toplevels are allowed, in the future) | ||
* it can be used to remove decorations, move windows around, cover | ||
* the entire screen etc etc. | ||
* | ||
* Current risks: | ||
* | ||
* - No CPU time limit, no memory allocation limits, no color limits. | ||
* | ||
* The actual code called is the same as Tk_Init but Tcl_IsSafe() | ||
* is checked at several places to differentiate the two initialisations. | ||
*/ | ||
return Initialize(interp); | ||
} | ||
extern TkStubs tkStubs; | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Initialize -- | ||
* | ||
* | ||
* Results: | ||
* A standard Tcl result. Also leaves an error message in the interp's | ||
* result if there was an error. | ||
* | ||
* Side effects: | ||
* Depends on the initialization scripts that are invoked. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static int | ||
Initialize(interp) | ||
Tcl_Interp *interp; /* Interpreter to initialize. */ | ||
{ | ||
char *p; | ||
int argc, code; | ||
char **argv, *args[20]; | ||
Tcl_DString class; | ||
ThreadSpecificData *tsdPtr; | ||
/* | ||
* Ensure that we are getting the matching version of Tcl. This is | ||
* really only an issue when Tk is loaded dynamically. | ||
*/ | ||
if (Tcl_InitStubs(interp, TCL_VERSION, 1) == NULL) { | ||
return TCL_ERROR; | ||
} | ||
tsdPtr = (ThreadSpecificData *) | ||
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); | ||
/* | ||
* Start by initializing all the static variables to default acceptable | ||
* values so that no information is leaked from a previous run of this | ||
* code. | ||
*/ | ||
Tcl_MutexLock(&windowMutex); | ||
synchronize = 0; | ||
name = NULL; | ||
display = NULL; | ||
geometry = NULL; | ||
colormap = NULL; | ||
use = NULL; | ||
visual = NULL; | ||
rest = 0; | ||
/* | ||
* We start by resetting the result because it might not be clean | ||
*/ | ||
Tcl_ResetResult(interp); | ||
if (Tcl_IsSafe(interp)) { | ||
/* | ||
* Get the clearance to start Tk and the "argv" parameters | ||
* from the master. | ||
*/ | ||
Tcl_DString ds; | ||
/* | ||
* Step 1 : find the master and construct the interp name | ||
* (could be a function if new APIs were ok). | ||
* We could also construct the path while walking, but there | ||
* is no API to get the name of an interp either. | ||
*/ | ||
Tcl_Interp *master = interp; | ||
while (1) { | ||
master = Tcl_GetMaster(master); | ||
if (master == NULL) { | ||
Tcl_DStringFree(&ds); | ||
Tcl_AppendResult(interp, "NULL master", (char *) NULL); | ||
Tcl_MutexUnlock(&windowMutex); | ||
return TCL_ERROR; | ||
} | ||
if (!Tcl_IsSafe(master)) { | ||
/* Found the trusted master. */ | ||
break; | ||
} | ||
} | ||
/* | ||
* Construct the name (rewalk...) | ||
*/ | ||
if (Tcl_GetInterpPath(master, interp) != TCL_OK) { | ||
Tcl_AppendResult(interp, "error in Tcl_GetInterpPath", | ||
(char *) NULL); | ||
Tcl_MutexUnlock(&windowMutex); | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* Build the string to eval. | ||
*/ | ||
Tcl_DStringInit(&ds); | ||
Tcl_DStringAppendElement(&ds, "::safe::TkInit"); | ||
Tcl_DStringAppendElement(&ds, Tcl_GetStringResult(master)); | ||
/* | ||
* Step 2 : Eval in the master. The argument is the *reversed* | ||
* interp path of the slave. | ||
*/ | ||
if (Tcl_Eval(master, Tcl_DStringValue(&ds)) != TCL_OK) { | ||
/* | ||
* We might want to transfer the error message or not. | ||
* We don't. (no API to do it and maybe security reasons). | ||
*/ | ||
Tcl_DStringFree(&ds); | ||
Tcl_AppendResult(interp, | ||
"not allowed to start Tk by master's safe::TkInit", | ||
(char *) NULL); | ||
Tcl_MutexUnlock(&windowMutex); | ||
return TCL_ERROR; | ||
} | ||
Tcl_DStringFree(&ds); | ||
/* | ||
* Use the master's result as argv. | ||
* Note: We don't use the Obj interfaces to avoid dealing with | ||
* cross interp refcounting and changing the code below. | ||
*/ | ||
p = Tcl_GetStringResult(master); | ||
} else { | ||
/* | ||
* If there is an "argv" variable, get its value, extract out | ||
* relevant arguments from it, and rewrite the variable without | ||
* the arguments that we used. | ||
*/ | ||
p = Tcl_GetVar2(interp, "argv", (char *) NULL, TCL_GLOBAL_ONLY); | ||
} | ||
argv = NULL; | ||
if (p != NULL) { | ||
char buffer[TCL_INTEGER_SPACE]; | ||
if (Tcl_SplitList(interp, p, &argc, &argv) != TCL_OK) { | ||
argError: | ||
Tcl_AddErrorInfo(interp, | ||
"\n (processing arguments in argv variable)"); | ||
Tcl_MutexUnlock(&windowMutex); | ||
return TCL_ERROR; | ||
} | ||
if (Tk_ParseArgv(interp, (Tk_Window) NULL, &argc, argv, | ||
argTable, TK_ARGV_DONT_SKIP_FIRST_ARG|TK_ARGV_NO_DEFAULTS) | ||
!= TCL_OK) { | ||
ckfree((char *) argv); | ||
goto argError; | ||
} | ||
p = Tcl_Merge(argc, argv); | ||
Tcl_SetVar2(interp, "argv", (char *) NULL, p, TCL_GLOBAL_ONLY); | ||
sprintf(buffer, "%d", argc); | ||
Tcl_SetVar2(interp, "argc", (char *) NULL, buffer, TCL_GLOBAL_ONLY); | ||
ckfree(p); | ||
} | ||
/* | ||
* Figure out the application's name and class. | ||
*/ | ||
Tcl_DStringInit(&class); | ||
if (name == NULL) { | ||
int offset; | ||
TkpGetAppName(interp, &class); | ||
offset = Tcl_DStringLength(&class)+1; | ||
Tcl_DStringSetLength(&class, offset); | ||
Tcl_DStringAppend(&class, Tcl_DStringValue(&class), offset-1); | ||
name = Tcl_DStringValue(&class) + offset; | ||
} else { | ||
Tcl_DStringAppend(&class, name, -1); | ||
} | ||
p = Tcl_DStringValue(&class); | ||
if (*p) { | ||
Tcl_UtfToTitle(p); | ||
} | ||
/* | ||
* Create an argument list for creating the top-level window, | ||
* using the information parsed from argv, if any. | ||
*/ | ||
args[0] = "toplevel"; | ||
args[1] = "."; | ||
args[2] = "-class"; | ||
args[3] = Tcl_DStringValue(&class); | ||
argc = 4; | ||
if (display != NULL) { | ||
args[argc] = "-screen"; | ||
args[argc+1] = display; | ||
argc += 2; | ||
/* | ||
* If this is the first application for this process, save | ||
* the display name in the DISPLAY environment variable so | ||
* that it will be available to subprocesses created by us. | ||
*/ | ||
if (tsdPtr->numMainWindows == 0) { | ||
Tcl_SetVar2(interp, "env", "DISPLAY", display, TCL_GLOBAL_ONLY); | ||
} | ||
} | ||
if (colormap != NULL) { | ||
args[argc] = "-colormap"; | ||
args[argc+1] = colormap; | ||
argc += 2; | ||
colormap = NULL; | ||
} | ||
if (use != NULL) { | ||
args[argc] = "-use"; | ||
args[argc+1] = use; | ||
argc += 2; | ||
use = NULL; | ||
} | ||
if (visual != NULL) { | ||
args[argc] = "-visual"; | ||
args[argc+1] = visual; | ||
argc += 2; | ||
visual = NULL; | ||
} | ||
args[argc] = NULL; | ||
code = TkCreateFrame((ClientData) NULL, interp, argc, args, 1, name); | ||
Tcl_DStringFree(&class); | ||
if (code != TCL_OK) { | ||
goto done; | ||
} | ||
Tcl_ResetResult(interp); | ||
if (synchronize) { | ||
XSynchronize(Tk_Display(Tk_MainWindow(interp)), True); | ||
} | ||
/* | ||
* Set the geometry of the main window, if requested. Put the | ||
* requested geometry into the "geometry" variable. | ||
*/ | ||
if (geometry != NULL) { | ||
Tcl_SetVar(interp, "geometry", geometry, TCL_GLOBAL_ONLY); | ||
code = Tcl_VarEval(interp, "wm geometry . ", geometry, (char *) NULL); | ||
if (code != TCL_OK) { | ||
goto done; | ||
} | ||
geometry = NULL; | ||
} | ||
Tcl_MutexUnlock(&windowMutex); | ||
if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, 1) == NULL) { | ||
code = TCL_ERROR; | ||
goto done; | ||
} | ||
/* | ||
* Provide Tk and its stub table. | ||
*/ | ||
code = Tcl_PkgProvideEx(interp, "Tk", TK_VERSION, (ClientData) &tkStubs); | ||
if (code != TCL_OK) { | ||
goto done; | ||
} | ||
#ifdef Tk_InitStubs | ||
#undef Tk_InitStubs | ||
#endif | ||
Tk_InitStubs(interp, TK_VERSION, 1); | ||
/* | ||
* Invoke platform-specific initialization. | ||
*/ | ||
code = TkpInit(interp); | ||
done: | ||
if (argv != NULL) { | ||
ckfree((char *) argv); | ||
} | ||
return code; | ||
} | ||
/* $History: tkWindow.c $ | ||
* | ||
* ***************** Version 1 ***************** | ||
* User: Dtashley Date: 1/02/01 Time: 3:16a | ||
* Created in $/IjuScripter, IjuConsole/Source/Tk Base | ||
* Initial check-in. | ||
*/ | ||
/* End of TKWINDOW.C */ | ||
1 | /* $Header$ */ | |
2 | ||
3 | /* | |
4 | * tkWindow.c -- | |
5 | * | |
6 | * This file provides basic window-manipulation procedures, | |
7 | * which are equivalent to procedures in Xlib (and even | |
8 | * invoke them) but also maintain the local Tk_Window | |
9 | * structure. | |
10 | * | |
11 | * Copyright (c) 1989-1994 The Regents of the University of California. | |
12 | * Copyright (c) 1994-1997 Sun Microsystems, Inc. | |
13 | * | |
14 | * See the file "license.terms" for information on usage and redistribution | |
15 | * of this file, and for a DISCLAIMER OF ALL WARRANTIES. | |
16 | * | |
17 | * RCS: @(#) $Id: tkwindow.c,v 1.1.1.1 2001/06/13 05:12:54 dtashley Exp $ | |
18 | */ | |
19 | ||
20 | #include "tkPort.h" | |
21 | #include "tkInt.h" | |
22 | ||
23 | #if !defined(__WIN32__) && !defined(MAC_TCL) | |
24 | #include "tkUnixInt.h" | |
25 | #endif | |
26 | ||
27 | ||
28 | typedef struct ThreadSpecificData { | |
29 | int numMainWindows; /* Count of numver of main windows currently | |
30 | * open in this thread. */ | |
31 | TkMainInfo *mainWindowList; | |
32 | /* First in list of all main windows managed | |
33 | * by this thread. */ | |
34 | TkDisplay *displayList; | |
35 | /* List of all displays currently in use by | |
36 | * the current thread. */ | |
37 | int initialized; /* 0 means the structures above need | |
38 | * initializing. */ | |
39 | } ThreadSpecificData; | |
40 | static Tcl_ThreadDataKey dataKey; | |
41 | ||
42 | /* | |
43 | * The Mutex below is used to lock access to the Tk_Uid structs above. | |
44 | */ | |
45 | ||
46 | TCL_DECLARE_MUTEX(windowMutex) | |
47 | ||
48 | /* | |
49 | * Default values for "changes" and "atts" fields of TkWindows. Note | |
50 | * that Tk always requests all events for all windows, except StructureNotify | |
51 | * events on internal windows: these events are generated internally. | |
52 | */ | |
53 | ||
54 | static XWindowChanges defChanges = { | |
55 | 0, 0, 1, 1, 0, 0, Above | |
56 | }; | |
57 | #define ALL_EVENTS_MASK \ | |
58 | KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask| \ | |
59 | EnterWindowMask|LeaveWindowMask|PointerMotionMask|ExposureMask| \ | |
60 | VisibilityChangeMask|PropertyChangeMask|ColormapChangeMask | |
61 | static XSetWindowAttributes defAtts= { | |
62 | None, /* background_pixmap */ | |
63 | 0, /* background_pixel */ | |
64 | CopyFromParent, /* border_pixmap */ | |
65 | 0, /* border_pixel */ | |
66 | NorthWestGravity, /* bit_gravity */ | |
67 | NorthWestGravity, /* win_gravity */ | |
68 | NotUseful, /* backing_store */ | |
69 | (unsigned) ~0, /* backing_planes */ | |
70 | 0, /* backing_pixel */ | |
71 | False, /* save_under */ | |
72 | ALL_EVENTS_MASK, /* event_mask */ | |
73 | 0, /* do_not_propagate_mask */ | |
74 | False, /* override_redirect */ | |
75 | CopyFromParent, /* colormap */ | |
76 | None /* cursor */ | |
77 | }; | |
78 | ||
79 | /* | |
80 | * The following structure defines all of the commands supported by | |
81 | * Tk, and the C procedures that execute them. | |
82 | */ | |
83 | ||
84 | typedef struct { | |
85 | char *name; /* Name of command. */ | |
86 | Tcl_CmdProc *cmdProc; /* Command's string-based procedure. */ | |
87 | Tcl_ObjCmdProc *objProc; /* Command's object-based procedure. */ | |
88 | int isSafe; /* If !0, this command will be exposed in | |
89 | * a safe interpreter. Otherwise it will be | |
90 | * hidden in a safe interpreter. */ | |
91 | int passMainWindow; /* 0 means provide NULL clientData to | |
92 | * command procedure; 1 means pass main | |
93 | * window as clientData to command | |
94 | * procedure. */ | |
95 | } TkCmd; | |
96 | ||
97 | static TkCmd commands[] = { | |
98 | /* | |
99 | * Commands that are part of the intrinsics: | |
100 | */ | |
101 | ||
102 | {"bell", NULL, Tk_BellObjCmd, 0, 1}, | |
103 | {"bind", Tk_BindCmd, NULL, 1, 1}, | |
104 | {"bindtags", Tk_BindtagsCmd, NULL, 1, 1}, | |
105 | {"clipboard", Tk_ClipboardCmd, NULL, 0, 1}, | |
106 | {"destroy", NULL, Tk_DestroyObjCmd, 1, 1}, | |
107 | {"event", NULL, Tk_EventObjCmd, 1, 1}, | |
108 | {"focus", NULL, Tk_FocusObjCmd, 1, 1}, | |
109 | {"font", NULL, Tk_FontObjCmd, 1, 1}, | |
110 | {"grab", Tk_GrabCmd, NULL, 0, 1}, | |
111 | {"grid", Tk_GridCmd, NULL, 1, 1}, | |
112 | {"image", NULL, Tk_ImageObjCmd, 1, 1}, | |
113 | {"lower", NULL, Tk_LowerObjCmd, 1, 1}, | |
114 | {"option", NULL, Tk_OptionObjCmd, 1, 1}, | |
115 | {"pack", Tk_PackCmd, NULL, 1, 1}, | |
116 | {"place", Tk_PlaceCmd, NULL, 1, 1}, | |
117 | {"raise", NULL, Tk_RaiseObjCmd, 1, 1}, | |
118 | {"selection", Tk_SelectionCmd, NULL, 0, 1}, | |
119 | {"tk", NULL, Tk_TkObjCmd, 0, 1}, | |
120 | {"tkwait", Tk_TkwaitCmd, NULL, 1, 1}, | |
121 | #if defined(__WIN32__) || defined(MAC_TCL) | |
122 | {"tk_chooseColor", NULL, Tk_ChooseColorObjCmd, 0, 1}, | |
123 | {"tk_chooseDirectory", NULL, Tk_ChooseDirectoryObjCmd, 0, 1}, | |
124 | {"tk_getOpenFile", NULL, Tk_GetOpenFileObjCmd, 0, 1}, | |
125 | {"tk_getSaveFile", NULL, Tk_GetSaveFileObjCmd, 0, 1}, | |
126 | #endif | |
127 | #ifdef __WIN32__ | |
128 | {"tk_messageBox", NULL, Tk_MessageBoxObjCmd, 0, 1}, | |
129 | #endif | |
130 | {"update", NULL, Tk_UpdateObjCmd, 1, 1}, | |
131 | {"winfo", NULL, Tk_WinfoObjCmd, 1, 1}, | |
132 | {"wm", Tk_WmCmd, NULL, 0, 1}, | |
133 | ||
134 | /* | |
135 | * Widget class commands. | |
136 | */ | |
137 | ||
138 | {"button", NULL, Tk_ButtonObjCmd, 1, 0}, | |
139 | {"canvas", NULL, Tk_CanvasObjCmd, 1, 1}, | |
140 | {"checkbutton", NULL, Tk_CheckbuttonObjCmd, 1, 0}, | |
141 | {"entry", NULL, Tk_EntryObjCmd, 1, 0}, | |
142 | {"frame", NULL, Tk_FrameObjCmd, 1, 1}, | |
143 | {"label", NULL, Tk_LabelObjCmd, 1, 0}, | |
144 | {"listbox", NULL, Tk_ListboxObjCmd, 1, 0}, | |
145 | {"menubutton", NULL, Tk_MenubuttonObjCmd, 1, 0}, | |
146 | {"message", Tk_MessageCmd, NULL, 1, 1}, | |
147 | {"radiobutton", NULL, Tk_RadiobuttonObjCmd, 1, 0}, | |
148 | {"scale", NULL, Tk_ScaleObjCmd, 1, 0}, | |
149 | {"scrollbar", Tk_ScrollbarCmd, NULL, 1, 1}, | |
150 | {"text", Tk_TextCmd, NULL, 1, 1}, | |
151 | {"toplevel", NULL, Tk_ToplevelObjCmd, 0, 1}, | |
152 | ||
153 | /* | |
154 | * Misc. | |
155 | */ | |
156 | ||
157 | #ifdef MAC_TCL | |
158 | {"unsupported1", TkUnsupported1Cmd, NULL, 1, 1}, | |
159 | #endif | |
160 | {(char *) NULL, (int (*) _ANSI_ARGS_((ClientData, Tcl_Interp *, int, char **))) NULL, NULL, 0} | |
161 | }; | |
162 | ||
163 | /* | |
164 | * The variables and table below are used to parse arguments from | |
165 | * the "argv" variable in Tk_Init. | |
166 | */ | |
167 | ||
168 | static int synchronize = 0; | |
169 | static char *name = NULL; | |
170 | static char *display = NULL; | |
171 | static char *geometry = NULL; | |
172 | static char *colormap = NULL; | |
173 | static char *use = NULL; | |
174 | static char *visual = NULL; | |
175 | static int rest = 0; | |
176 | ||
177 | static Tk_ArgvInfo argTable[] = { | |
178 | {"-colormap", TK_ARGV_STRING, (char *) NULL, (char *) &colormap, | |
179 | "Colormap for main window"}, | |
180 | {"-display", TK_ARGV_STRING, (char *) NULL, (char *) &display, | |
181 | "Display to use"}, | |
182 | {"-geometry", TK_ARGV_STRING, (char *) NULL, (char *) &geometry, | |
183 | "Initial geometry for window"}, | |
184 | {"-name", TK_ARGV_STRING, (char *) NULL, (char *) &name, | |
185 | "Name to use for application"}, | |
186 | {"-sync", TK_ARGV_CONSTANT, (char *) 1, (char *) &synchronize, | |
187 | "Use synchronous mode for display server"}, | |
188 | {"-visual", TK_ARGV_STRING, (char *) NULL, (char *) &visual, | |
189 | "Visual for main window"}, | |
190 | {"-use", TK_ARGV_STRING, (char *) NULL, (char *) &use, | |
191 | "Id of window in which to embed application"}, | |
192 | {"--", TK_ARGV_REST, (char *) 1, (char *) &rest, | |
193 | "Pass all remaining arguments through to script"}, | |
194 | {(char *) NULL, TK_ARGV_END, (char *) NULL, (char *) NULL, | |
195 | (char *) NULL} | |
196 | }; | |
197 | ||
198 | /* | |
199 | * Forward declarations to procedures defined later in this file: | |
200 | */ | |
201 | ||
202 | static Tk_Window CreateTopLevelWindow _ANSI_ARGS_((Tcl_Interp *interp, | |
203 | Tk_Window parent, char *name, char *screenName)); | |
204 | static void DeleteWindowsExitProc _ANSI_ARGS_(( | |
205 | ClientData clientData)); | |
206 | static TkDisplay * GetScreen _ANSI_ARGS_((Tcl_Interp *interp, | |
207 | char *screenName, int *screenPtr)); | |
208 | static int Initialize _ANSI_ARGS_((Tcl_Interp *interp)); | |
209 | static int NameWindow _ANSI_ARGS_((Tcl_Interp *interp, | |
210 | TkWindow *winPtr, TkWindow *parentPtr, | |
211 | char *name)); | |
212 | static void OpenIM _ANSI_ARGS_((TkDisplay *dispPtr)); | |
213 | static void UnlinkWindow _ANSI_ARGS_((TkWindow *winPtr)); | |
214 | ||
215 | /* | |
216 | *---------------------------------------------------------------------- | |
217 | * | |
218 | * CreateTopLevelWindow -- | |
219 | * | |
220 | * Make a new window that will be at top-level (its parent will | |
221 | * be the root window of a screen). | |
222 | * | |
223 | * Results: | |
224 | * The return value is a token for the new window, or NULL if | |
225 | * an error prevented the new window from being created. If | |
226 | * NULL is returned, an error message will be left in | |
227 | * the interp's result. | |
228 | * | |
229 | * Side effects: | |
230 | * A new window structure is allocated locally. An X | |
231 | * window is NOT initially created, but will be created | |
232 | * the first time the window is mapped. | |
233 | * | |
234 | *---------------------------------------------------------------------- | |
235 | */ | |
236 | ||
237 | static Tk_Window | |
238 | CreateTopLevelWindow(interp, parent, name, screenName) | |
239 | Tcl_Interp *interp; /* Interpreter to use for error reporting. */ | |
240 | Tk_Window parent; /* Token for logical parent of new window | |
241 | * (used for naming, options, etc.). May | |
242 | * be NULL. */ | |
243 | char *name; /* Name for new window; if parent is | |
244 | * non-NULL, must be unique among parent's | |
245 | * children. */ | |
246 | char *screenName; /* Name of screen on which to create | |
247 | * window. NULL means use DISPLAY environment | |
248 | * variable to determine. Empty string means | |
249 | * use parent's screen, or DISPLAY if no | |
250 | * parent. */ | |
251 | { | |
252 | register TkWindow *winPtr; | |
253 | register TkDisplay *dispPtr; | |
254 | int screenId; | |
255 | ThreadSpecificData *tsdPtr = (ThreadSpecificData *) | |
256 | Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); | |
257 | ||
258 | if (!tsdPtr->initialized) { | |
259 | tsdPtr->initialized = 1; | |
260 | ||
261 | /* | |
262 | * Create built-in image types. | |
263 | */ | |
264 | ||
265 | Tk_CreateImageType(&tkBitmapImageType); | |
266 | Tk_CreateImageType(&tkPhotoImageType); | |
267 | ||
268 | /* | |
269 | * Create built-in photo image formats. | |
270 | */ | |
271 | ||
272 | Tk_CreatePhotoImageFormat(&tkImgFmtGIF); | |
273 | Tk_CreateOldPhotoImageFormat(&tkImgFmtPPM); | |
274 | ||
275 | /* | |
276 | * Create exit handler to delete all windows when the application | |
277 | * exits. | |
278 | */ | |
279 | ||
280 | Tcl_CreateExitHandler(DeleteWindowsExitProc, (ClientData) NULL); | |
281 | } | |
282 | ||
283 | if ((parent != NULL) && (screenName != NULL) && (screenName[0] == '\0')) { | |
284 | dispPtr = ((TkWindow *) parent)->dispPtr; | |
285 | screenId = Tk_ScreenNumber(parent); | |
286 | } else { | |
287 | dispPtr = GetScreen(interp, screenName, &screenId); | |
288 | if (dispPtr == NULL) { | |
289 | return (Tk_Window) NULL; | |
290 | } | |
291 | } | |
292 | ||
293 | winPtr = TkAllocWindow(dispPtr, screenId, (TkWindow *) parent); | |
294 | ||
295 | /* | |
296 | * Force the window to use a border pixel instead of border pixmap. | |
297 | * This is needed for the case where the window doesn't use the | |
298 | * default visual. In this case, the default border is a pixmap | |
299 | * inherited from the root window, which won't work because it will | |
300 | * have the wrong visual. | |
301 | */ | |
302 | ||
303 | winPtr->dirtyAtts |= CWBorderPixel; | |
304 | ||
305 | /* | |
306 | * (Need to set the TK_TOP_LEVEL flag immediately here; otherwise | |
307 | * Tk_DestroyWindow will core dump if it is called before the flag | |
308 | * has been set.) | |
309 | */ | |
310 | ||
311 | winPtr->flags |= TK_TOP_LEVEL; | |
312 | ||
313 | if (parent != NULL) { | |
314 | if (NameWindow(interp, winPtr, (TkWindow *) parent, name) != TCL_OK) { | |
315 | Tk_DestroyWindow((Tk_Window) winPtr); | |
316 | return (Tk_Window) NULL; | |
317 | } | |
318 | } | |
319 | TkWmNewWindow(winPtr); | |
320 | ||
321 | return (Tk_Window) winPtr; | |
322 | } | |
323 | ||
324 | /* | |
325 | *---------------------------------------------------------------------- | |
326 | * | |
327 | * GetScreen -- | |
328 | * | |
329 | * Given a string name for a display-plus-screen, find the | |
330 | * TkDisplay structure for the display and return the screen | |
331 | * number too. | |
332 | * | |
333 | * Results: | |
334 | * The return value is a pointer to information about the display, | |
335 | * or NULL if the display couldn't be opened. In this case, an | |
336 | * error message is left in the interp's result. The location at | |
337 | * *screenPtr is overwritten with the screen number parsed from | |
338 | * screenName. | |
339 | * | |
340 | * Side effects: | |
341 | * A new connection is opened to the display if there is no | |
342 | * connection already. A new TkDisplay data structure is also | |
343 | * setup, if necessary. | |
344 | * | |
345 | *---------------------------------------------------------------------- | |
346 | */ | |
347 | ||
348 | static TkDisplay * | |
349 | GetScreen(interp, screenName, screenPtr) | |
350 | Tcl_Interp *interp; /* Place to leave error message. */ | |
351 | char *screenName; /* Name for screen. NULL or empty means | |
352 | * use DISPLAY envariable. */ | |
353 | int *screenPtr; /* Where to store screen number. */ | |
354 | { | |
355 | register TkDisplay *dispPtr; | |
356 | char *p; | |
357 | int screenId; | |
358 | size_t length; | |
359 | ThreadSpecificData *tsdPtr = (ThreadSpecificData *) | |
360 | Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); | |
361 | ||
362 | /* | |
363 | * Separate the screen number from the rest of the display | |
364 | * name. ScreenName is assumed to have the syntax | |
365 | * <display>.<screen> with the dot and the screen being | |
366 | * optional. | |
367 | */ | |
368 | ||
369 | screenName = TkGetDefaultScreenName(interp, screenName); | |
370 | if (screenName == NULL) { | |
371 | Tcl_SetResult(interp, | |
372 | "no display name and no $DISPLAY environment variable", | |
373 | TCL_STATIC); | |
374 | return (TkDisplay *) NULL; | |
375 | } | |
376 | length = strlen(screenName); | |
377 | screenId = 0; | |
378 | p = screenName+length-1; | |
379 | while (isdigit(UCHAR(*p)) && (p != screenName)) { | |
380 | p--; | |
381 | } | |
382 | if ((*p == '.') && (p[1] != '\0')) { | |
383 | length = p - screenName; | |
384 | screenId = strtoul(p+1, (char **) NULL, 10); | |
385 | } | |
386 | ||
387 | /* | |
388 | * See if we already have a connection to this display. If not, | |
389 | * then open a new connection. | |
390 | */ | |
391 | ||
392 | for (dispPtr = TkGetDisplayList(); ; dispPtr = dispPtr->nextPtr) { | |
393 | if (dispPtr == NULL) { | |
394 | dispPtr = TkpOpenDisplay(screenName); | |
395 | if (dispPtr == NULL) { | |
396 | Tcl_AppendResult(interp, "couldn't connect to display \"", | |
397 | screenName, "\"", (char *) NULL); | |
398 | return (TkDisplay *) NULL; | |
399 | } | |
400 | dispPtr->nextPtr = TkGetDisplayList(); | |
401 | dispPtr->name = (char *) ckalloc((unsigned) (length+1)); | |
402 | dispPtr->lastEventTime = CurrentTime; | |
403 | dispPtr->borderInit = 0; | |
404 | dispPtr->atomInit = 0; | |
405 | dispPtr->bindInfoStale = 1; | |
406 | dispPtr->modeModMask = 0; | |
407 | dispPtr->metaModMask = 0; | |
408 | dispPtr->altModMask = 0; | |
409 | dispPtr->numModKeyCodes = 0; | |
410 | dispPtr->modKeyCodes = NULL; | |
411 | dispPtr->bitmapInit = 0; | |
412 | dispPtr->bitmapAutoNumber = 0; | |
413 | dispPtr->numIdSearches = 0; | |
414 | dispPtr->numSlowSearches = 0; | |
415 | dispPtr->colorInit = 0; | |
416 | dispPtr->stressPtr = NULL; | |
417 | dispPtr->cursorInit = 0; | |
418 | dispPtr->cursorString[0] = '\0'; | |
419 | dispPtr->cursorFont = None; | |
420 | dispPtr->errorPtr = NULL; | |
421 | dispPtr->deleteCount = 0; | |
422 | dispPtr->delayedMotionPtr = NULL; | |
423 | dispPtr->focusDebug = 0; | |
424 | dispPtr->implicitWinPtr = NULL; | |
425 | dispPtr->focusPtr = NULL; | |
426 | dispPtr->gcInit = 0; | |
427 | dispPtr->geomInit = 0; | |
428 | dispPtr->uidInit = 0; | |
429 | dispPtr->grabWinPtr = NULL; | |
430 | dispPtr->eventualGrabWinPtr = NULL; | |
431 | dispPtr->buttonWinPtr = NULL; | |
432 | dispPtr->serverWinPtr = NULL; | |
433 | dispPtr->firstGrabEventPtr = NULL; | |
434 | dispPtr->lastGrabEventPtr = NULL; | |
435 | dispPtr->grabFlags = 0; | |
436 | dispPtr->mouseButtonState = 0; | |
437 | dispPtr->warpInProgress = 0; | |
438 | dispPtr->warpWindow = None; | |
439 | dispPtr->warpX = 0; | |
440 | dispPtr->warpY = 0; | |
441 | dispPtr->gridInit = 0; | |
442 | dispPtr->imageId = 0; | |
443 | dispPtr->packInit = 0; | |
444 | dispPtr->placeInit = 0; | |
445 | dispPtr->selectionInfoPtr = NULL; | |
446 | dispPtr->multipleAtom = None; | |
447 | dispPtr->clipWindow = NULL; | |
448 | dispPtr->clipboardActive = 0; | |
449 | dispPtr->clipboardAppPtr = NULL; | |
450 | dispPtr->clipTargetPtr = NULL; | |
451 | dispPtr->commTkwin = NULL; | |
452 | dispPtr->wmTracing = 0; | |
453 | dispPtr->firstWmPtr = NULL; | |
454 | dispPtr->foregroundWmPtr = NULL; | |
455 | dispPtr->destroyCount = 0; | |
456 | dispPtr->lastDestroyRequest = 0; | |
457 | dispPtr->cmapPtr = NULL; | |
458 | Tcl_InitHashTable(&dispPtr->winTable, TCL_ONE_WORD_KEYS); | |
459 | ||
460 | dispPtr->refCount = 0; | |
461 | strncpy(dispPtr->name, screenName, length); | |
462 | dispPtr->name[length] = '\0'; | |
463 | dispPtr->useInputMethods = 0; | |
464 | OpenIM(dispPtr); | |
465 | TkInitXId(dispPtr); | |
466 | ||
467 | tsdPtr->displayList = dispPtr; | |
468 | break; | |
469 | } | |
470 | if ((strncmp(dispPtr->name, screenName, length) == 0) | |
471 | && (dispPtr->name[length] == '\0')) { | |
472 | break; | |
473 | } | |
474 | } | |
475 | if (screenId >= ScreenCount(dispPtr->display)) { | |
476 | char buf[32 + TCL_INTEGER_SPACE]; | |
477 | ||
478 | sprintf(buf, "bad screen number \"%d\"", screenId); | |
479 | Tcl_SetResult(interp, buf, TCL_VOLATILE); | |
480 | return (TkDisplay *) NULL; | |
481 | } | |
482 | *screenPtr = screenId; | |
483 | return dispPtr; | |
484 | } | |
485 | ||
486 | /* | |
487 | *---------------------------------------------------------------------- | |
488 | * | |
489 | * TkGetDisplay -- | |
490 | * | |
491 | * Given an X display, TkGetDisplay returns the TkDisplay | |
492 | * structure for the display. | |
493 | * | |
494 | * Results: | |
495 | * The return value is a pointer to information about the display, | |
496 | * or NULL if the display did not have a TkDisplay structure. | |
497 | * | |
498 | * Side effects: | |
499 | * None. | |
500 | * | |
501 | *---------------------------------------------------------------------- | |
502 | */ | |
503 | ||
504 | TkDisplay * | |
505 | TkGetDisplay(display) | |
506 | Display *display; /* X's display pointer */ | |
507 | { | |
508 | TkDisplay *dispPtr; | |
509 | ThreadSpecificData *tsdPtr = (ThreadSpecificData *) | |
510 | Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); | |
511 | ||
512 | for (dispPtr = tsdPtr->displayList; dispPtr != NULL; | |
513 | dispPtr = dispPtr->nextPtr) { | |
514 | if (dispPtr->display == display) { | |
515 | break; | |
516 | } | |
517 | } | |
518 | return dispPtr; | |
519 | } | |
520 | ||
521 | /* | |
522 | *-------------------------------------------------------------- | |
523 | * | |
524 | * TkGetDisplayList -- | |
525 | * | |
526 | * This procedure returns a pointer to the thread-local | |
527 | * list of TkDisplays corresponding to the open displays. | |
528 | * | |
529 | * Results: | |
530 | * The return value is a pointer to the first TkDisplay | |
531 | * structure in thread-local-storage. | |
532 | * | |
533 | * Side effects: | |
534 | * None. | |
535 | * | |
536 | *-------------------------------------------------------------- | |
537 | */ | |
538 | TkDisplay * | |
539 | TkGetDisplayList() | |
540 | { | |
541 | ThreadSpecificData *tsdPtr = (ThreadSpecificData *) | |
542 | Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); | |
543 | ||
544 | return tsdPtr->displayList; | |
545 | } | |
546 | ||
547 | /* | |
548 | *-------------------------------------------------------------- | |
549 | * | |
550 | * TkGetMainInfoList -- | |
551 | * | |
552 | * This procedure returns a pointer to the list of structures | |
553 | * containing information about all main windows for the | |
554 | * current thread. | |
555 | * | |
556 | * Results: | |
557 | * The return value is a pointer to the first TkMainInfo | |
558 | * structure in thread local storage. | |
559 | * | |
560 | * Side effects: | |
561 | * None. | |
562 | * | |
563 | *-------------------------------------------------------------- | |
564 | */ | |
565 | TkMainInfo * | |
566 | TkGetMainInfoList() | |
567 | { | |
568 | ThreadSpecificData *tsdPtr = (ThreadSpecificData *) | |
569 | Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); | |
570 | ||
571 | return tsdPtr->mainWindowList; | |
572 | } | |
573 | /* | |
574 | *-------------------------------------------------------------- | |
575 | * | |
576 | * TkAllocWindow -- | |
577 | * | |
578 | * This procedure creates and initializes a TkWindow structure. | |
579 | * | |
580 | * Results: | |
581 | * The return value is a pointer to the new window. | |
582 | * | |
583 | * Side effects: | |
584 | * A new window structure is allocated and all its fields are | |
585 | * initialized. | |
586 | * | |
587 | *-------------------------------------------------------------- | |
588 | */ | |
589 | ||
590 | TkWindow * | |
591 | TkAllocWindow(dispPtr, screenNum, parentPtr) | |
592 | TkDisplay *dispPtr; /* Display associated with new window. */ | |
593 | int screenNum; /* Index of screen for new window. */ | |
594 | TkWindow *parentPtr; /* Parent from which this window should | |
595 | * inherit visual information. NULL means | |
596 | * use screen defaults instead of | |
597 | * inheriting. */ | |
598 | { | |
599 | register TkWindow *winPtr; | |
600 | ||
601 | winPtr = (TkWindow *) ckalloc(sizeof(TkWindow)); | |
602 | winPtr->display = dispPtr->display; | |
603 | winPtr->dispPtr = dispPtr; | |
604 | winPtr->screenNum = screenNum; | |
605 | if ((parentPtr != NULL) && (parentPtr->display == winPtr->display) | |
606 | && (parentPtr->screenNum == winPtr->screenNum)) { | |
607 | winPtr->visual = parentPtr->visual; | |
608 | winPtr->depth = parentPtr->depth; | |
609 | } else { | |
610 | winPtr->visual = DefaultVisual(dispPtr->display, screenNum); | |
611 | winPtr->depth = DefaultDepth(dispPtr->display, screenNum); | |
612 | } | |
613 | winPtr->window = None; | |
614 | winPtr->childList = NULL; | |
615 | winPtr->lastChildPtr = NULL; | |
616 | winPtr->parentPtr = NULL; | |
617 | winPtr->nextPtr = NULL; | |
618 | winPtr->mainPtr = NULL; | |
619 | winPtr->pathName = NULL; | |
620 | winPtr->nameUid = NULL; | |
621 | winPtr->classUid = NULL; | |
622 | winPtr->changes = defChanges; | |
623 | winPtr->dirtyChanges = CWX|CWY|CWWidth|CWHeight|CWBorderWidth; | |
624 | winPtr->atts = defAtts; | |
625 | if ((parentPtr != NULL) && (parentPtr->display == winPtr->display) | |
626 | && (parentPtr->screenNum == winPtr->screenNum)) { | |
627 | winPtr->atts.colormap = parentPtr->atts.colormap; | |
628 | } else { | |
629 | winPtr->atts.colormap = DefaultColormap(dispPtr->display, screenNum); | |
630 | } | |
631 | winPtr->dirtyAtts = CWEventMask|CWColormap|CWBitGravity; | |
632 | winPtr->flags = 0; | |
633 | winPtr->handlerList = NULL; | |
634 | #ifdef TK_USE_INPUT_METHODS | |
635 | winPtr->inputContext = NULL; | |
636 | #endif /* TK_USE_INPUT_METHODS */ | |
637 | winPtr->tagPtr = NULL; | |
638 | winPtr->numTags = 0; | |
639 | winPtr->optionLevel = -1; | |
640 | winPtr->selHandlerList = NULL; | |
641 | winPtr->geomMgrPtr = NULL; | |
642 | winPtr->geomData = NULL; | |
643 | winPtr->reqWidth = winPtr->reqHeight = 1; | |
644 | winPtr->internalBorderWidth = 0; | |
645 | winPtr->wmInfoPtr = NULL; | |
646 | winPtr->classProcsPtr = NULL; | |
647 | winPtr->instanceData = NULL; | |
648 | winPtr->privatePtr = NULL; | |
649 | ||
650 | return winPtr; | |
651 | } | |
652 | ||
653 | /* | |
654 | *---------------------------------------------------------------------- | |
655 | * | |
656 | * NameWindow -- | |
657 | * | |
658 | * This procedure is invoked to give a window a name and insert | |
659 | * the window into the hierarchy associated with a particular | |
660 | * application. | |
661 | * | |
662 | * Results: | |
663 | * A standard Tcl return value. | |
664 | * | |
665 | * Side effects: | |
666 | * See above. | |
667 | * | |
668 | *---------------------------------------------------------------------- | |
669 | */ | |
670 | ||
671 | static int | |
672 | NameWindow(interp, winPtr, parentPtr, name) | |
673 | Tcl_Interp *interp; /* Interpreter to use for error reporting. */ | |
674 | register TkWindow *winPtr; /* Window that is to be named and inserted. */ | |
675 | TkWindow *parentPtr; /* Pointer to logical parent for winPtr | |
676 | * (used for naming, options, etc.). */ | |
677 | char *name; /* Name for winPtr; must be unique among | |
678 | * parentPtr's children. */ | |
679 | { | |
680 | #define FIXED_SIZE 200 | |
681 | char staticSpace[FIXED_SIZE]; | |
682 | char *pathName; | |
683 | int new; | |
684 | Tcl_HashEntry *hPtr; | |
685 | int length1, length2; | |
686 | ||
687 | /* | |
688 | * Setup all the stuff except name right away, then do the name stuff | |
689 | * last. This is so that if the name stuff fails, everything else | |
690 | * will be properly initialized (needed to destroy the window cleanly | |
691 | * after the naming failure). | |
692 | */ | |
693 | winPtr->parentPtr = parentPtr; | |
694 | winPtr->nextPtr = NULL; | |
695 | if (parentPtr->childList == NULL) { | |
696 | parentPtr->childList = winPtr; | |
697 | } else { | |
698 | parentPtr->lastChildPtr->nextPtr = winPtr; | |
699 | } | |
700 | parentPtr->lastChildPtr = winPtr; | |
701 | winPtr->mainPtr = parentPtr->mainPtr; | |
702 | winPtr->mainPtr->refCount++; | |
703 | winPtr->nameUid = Tk_GetUid(name); | |
704 | ||
705 | /* | |
706 | * Don't permit names that start with an upper-case letter: this | |
707 | * will just cause confusion with class names in the option database. | |
708 | */ | |
709 | ||
710 | if (isupper(UCHAR(name[0]))) { | |
711 | Tcl_AppendResult(interp, | |
712 | "window name starts with an upper-case letter: \"", | |
713 | name, "\"", (char *) NULL); | |
714 | return TCL_ERROR; | |
715 | } | |
716 | ||
717 | /* | |
718 | * To permit names of arbitrary length, must be prepared to malloc | |
719 | * a buffer to hold the new path name. To run fast in the common | |
720 | * case where names are short, use a fixed-size buffer on the | |
721 | * stack. | |
722 | */ | |
723 | ||
724 | length1 = strlen(parentPtr->pathName); | |
725 | length2 = strlen(name); | |
726 | if ((length1+length2+2) <= FIXED_SIZE) { | |
727 | pathName = staticSpace; | |
728 | } else { | |
729 | pathName = (char *) ckalloc((unsigned) (length1+length2+2)); | |
730 | } | |
731 | if (length1 == 1) { | |
732 | pathName[0] = '.'; | |
733 | strcpy(pathName+1, name); | |
734 | } else { | |
735 | strcpy(pathName, parentPtr->pathName); | |
736 | pathName[length1] = '.'; | |
737 | strcpy(pathName+length1+1, name); | |
738 | } | |
739 | hPtr = Tcl_CreateHashEntry(&parentPtr->mainPtr->nameTable, pathName, &new); | |
740 | if (pathName != staticSpace) { | |
741 | ckfree(pathName); | |
742 | } | |
743 | if (!new) { | |
744 | Tcl_AppendResult(interp, "window name \"", name, | |
745 | "\" already exists in parent", (char *) NULL); | |
746 | return TCL_ERROR; | |
747 | } | |
748 | Tcl_SetHashValue(hPtr, winPtr); | |
749 | winPtr->pathName = Tcl_GetHashKey(&parentPtr->mainPtr->nameTable, hPtr); | |
750 | return TCL_OK; | |
751 | } | |
752 | ||
753 | /* | |
754 | *---------------------------------------------------------------------- | |
755 | * | |
756 | * TkCreateMainWindow -- | |
757 | * | |
758 | * Make a new main window. A main window is a special kind of | |
759 | * top-level window used as the outermost window in an | |
760 | * application. | |
761 | * | |
762 | * Results: | |
763 | * The return value is a token for the new window, or NULL if | |
764 | * an error prevented the new window from being created. If | |
765 | * NULL is returned, an error message will be left in | |
766 | * the interp's result. | |
767 | * | |
768 | * Side effects: | |
769 | * A new window structure is allocated locally; "interp" is | |
770 | * associated with the window and registered for "send" commands | |
771 | * under "baseName". BaseName may be extended with an instance | |
772 | * number in the form "#2" if necessary to make it globally | |
773 | * unique. Tk-related commands are bound into interp. | |
774 | * | |
775 | *---------------------------------------------------------------------- | |
776 | */ | |
777 | ||
778 | Tk_Window | |
779 | TkCreateMainWindow(interp, screenName, baseName) | |
780 | Tcl_Interp *interp; /* Interpreter to use for error reporting. */ | |
781 | char *screenName; /* Name of screen on which to create | |
782 | * window. Empty or NULL string means | |
783 | * use DISPLAY environment variable. */ | |
784 | char *baseName; /* Base name for application; usually of the | |
785 | * form "prog instance". */ | |
786 | { | |
787 | Tk_Window tkwin; | |
788 | int dummy; | |
789 | int isSafe; | |
790 | Tcl_HashEntry *hPtr; | |
791 | register TkMainInfo *mainPtr; | |
792 | register TkWindow *winPtr; | |
793 | register TkCmd *cmdPtr; | |
794 | ClientData clientData; | |
795 | ThreadSpecificData *tsdPtr = (ThreadSpecificData *) | |
796 | Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); | |
797 | ||
798 | /* | |
799 | * Panic if someone updated the TkWindow structure without | |
800 | * also updating the Tk_FakeWin structure (or vice versa). | |
801 | */ | |
802 | ||
803 | if (sizeof(TkWindow) != sizeof(Tk_FakeWin)) { | |
804 | panic("TkWindow and Tk_FakeWin are not the same size"); | |
805 | } | |
806 | ||
807 | /* | |
808 | * Create the basic TkWindow structure. | |
809 | */ | |
810 | ||
811 | tkwin = CreateTopLevelWindow(interp, (Tk_Window) NULL, baseName, | |
812 | screenName); | |
813 | if (tkwin == NULL) { | |
814 | return NULL; | |
815 | } | |
816 | ||
817 | /* | |
818 | * Create the TkMainInfo structure for this application, and set | |
819 | * up name-related information for the new window. | |
820 | */ | |
821 | ||
822 | winPtr = (TkWindow *) tkwin; | |
823 | mainPtr = (TkMainInfo *) ckalloc(sizeof(TkMainInfo)); | |
824 | mainPtr->winPtr = winPtr; | |
825 | mainPtr->refCount = 1; | |
826 | mainPtr->interp = interp; | |
827 | Tcl_InitHashTable(&mainPtr->nameTable, TCL_STRING_KEYS); | |
828 | TkEventInit(); | |
829 | TkBindInit(mainPtr); | |
830 | TkFontPkgInit(mainPtr); | |
831 | mainPtr->tlFocusPtr = NULL; | |
832 | mainPtr->displayFocusPtr = NULL; | |
833 | mainPtr->optionRootPtr = NULL; | |
834 | Tcl_InitHashTable(&mainPtr->imageTable, TCL_STRING_KEYS); | |
835 | mainPtr->strictMotif = 0; | |
836 | if (Tcl_LinkVar(interp, "tk_strictMotif", (char *) &mainPtr->strictMotif, | |
837 | TCL_LINK_BOOLEAN) != TCL_OK) { | |
838 | Tcl_ResetResult(interp); | |
839 | } | |
840 | mainPtr->nextPtr = tsdPtr->mainWindowList; | |
841 | tsdPtr->mainWindowList = mainPtr; | |
842 | winPtr->mainPtr = mainPtr; | |
843 | hPtr = Tcl_CreateHashEntry(&mainPtr->nameTable, ".", &dummy); | |
844 | Tcl_SetHashValue(hPtr, winPtr); | |
845 | winPtr->pathName = Tcl_GetHashKey(&mainPtr->nameTable, hPtr); | |
846 | ||
847 | /* | |
848 | * We have just created another Tk application; increment the refcount | |
849 | * on the display pointer. | |
850 | */ | |
851 | ||
852 | winPtr->dispPtr->refCount++; | |
853 | ||
854 | /* | |
855 | * Register the interpreter for "send" purposes. | |
856 | */ | |
857 | ||
858 | winPtr->nameUid = Tk_GetUid(Tk_SetAppName(tkwin, baseName)); | |
859 | ||
860 | /* | |
861 | * Bind in Tk's commands. | |
862 | */ | |
863 | ||
864 | isSafe = Tcl_IsSafe(interp); | |
865 | for (cmdPtr = commands; cmdPtr->name != NULL; cmdPtr++) { | |
866 | if ((cmdPtr->cmdProc == NULL) && (cmdPtr->objProc == NULL)) { | |
867 | panic("TkCreateMainWindow: builtin command with NULL string and object procs"); | |
868 | } | |
869 | if (cmdPtr->passMainWindow) { | |
870 | clientData = (ClientData) tkwin; | |
871 | } else { | |
872 | clientData = (ClientData) NULL; | |
873 | } | |
874 | if (cmdPtr->cmdProc != NULL) { | |
875 | Tcl_CreateCommand(interp, cmdPtr->name, cmdPtr->cmdProc, | |
876 | clientData, (void (*) _ANSI_ARGS_((ClientData))) NULL); | |
877 | } else { | |
878 | Tcl_CreateObjCommand(interp, cmdPtr->name, cmdPtr->objProc, | |
879 | clientData, NULL); | |
880 | } | |
881 | if (isSafe) { | |
882 | if (!(cmdPtr->isSafe)) { | |
883 | Tcl_HideCommand(interp, cmdPtr->name, cmdPtr->name); | |
884 | } | |
885 | } | |
886 | } | |
887 | ||
888 | TkCreateMenuCmd(interp); | |
889 | ||
890 | /* | |
891 | * Set variables for the intepreter. | |
892 | */ | |
893 | ||
894 | Tcl_SetVar(interp, "tk_patchLevel", TK_PATCH_LEVEL, TCL_GLOBAL_ONLY); | |
895 | Tcl_SetVar(interp, "tk_version", TK_VERSION, TCL_GLOBAL_ONLY); | |
896 | ||
897 | tsdPtr->numMainWindows++; | |
898 | return tkwin; | |
899 | } | |
900 | ||
901 | /* | |
902 | *-------------------------------------------------------------- | |
903 | * | |
904 | * Tk_CreateWindow -- | |
905 | * | |
906 | * Create a new internal or top-level window as a child of an | |
907 | * existing window. | |
908 | * | |
909 | * Results: | |
910 | * The return value is a token for the new window. This | |
911 | * is not the same as X's token for the window. If an error | |
912 | * occurred in creating the window (e.g. no such display or | |
913 | * screen), then an error message is left in the interp's result and | |
914 | * NULL is returned. | |
915 | * | |
916 | * Side effects: | |
917 | * A new window structure is allocated locally. An X | |
918 | * window is not initially created, but will be created | |
919 | * the first time the window is mapped. | |
920 | * | |
921 | *-------------------------------------------------------------- | |
922 | */ | |
923 | ||
924 | Tk_Window | |
925 | Tk_CreateWindow(interp, parent, name, screenName) | |
926 | Tcl_Interp *interp; /* Interpreter to use for error reporting. | |
927 | * the interp's result is assumed to be | |
928 | * initialized by the caller. */ | |
929 | Tk_Window parent; /* Token for parent of new window. */ | |
930 | char *name; /* Name for new window. Must be unique | |
931 | * among parent's children. */ | |
932 | char *screenName; /* If NULL, new window will be internal on | |
933 | * same screen as its parent. If non-NULL, | |
934 | * gives name of screen on which to create | |
935 | * new window; window will be a top-level | |
936 | * window. */ | |
937 | { | |
938 | TkWindow *parentPtr = (TkWindow *) parent; | |
939 | TkWindow *winPtr; | |
940 | ||
941 | if ((parentPtr != NULL) && (parentPtr->flags & TK_ALREADY_DEAD)) { | |
942 | Tcl_AppendResult(interp, | |
943 | "can't create window: parent has been destroyed", | |
944 | (char *) NULL); | |
945 | return NULL; | |
946 | } else if ((parentPtr != NULL) && | |
947 | (parentPtr->flags & TK_CONTAINER)) { | |
948 | Tcl_AppendResult(interp, | |
949 | "can't create window: its parent has -container = yes", | |
950 | (char *) NULL); | |
951 | return NULL; | |
952 | } | |
953 | if (screenName == NULL) { | |
954 | winPtr = TkAllocWindow(parentPtr->dispPtr, parentPtr->screenNum, | |
955 | parentPtr); | |
956 | if (NameWindow(interp, winPtr, parentPtr, name) != TCL_OK) { | |
957 | Tk_DestroyWindow((Tk_Window) winPtr); | |
958 | return NULL; | |
959 | } else { | |
960 | return (Tk_Window) winPtr; | |
961 | } | |
962 | } else { | |
963 | return CreateTopLevelWindow(interp, parent, name, screenName); | |
964 | } | |
965 | } | |
966 | ||
967 | /* | |
968 | *---------------------------------------------------------------------- | |
969 | * | |
970 | * Tk_CreateWindowFromPath -- | |
971 | * | |
972 | * This procedure is similar to Tk_CreateWindow except that | |
973 | * it uses a path name to create the window, rather than a | |
974 | * parent and a child name. | |
975 | * | |
976 | * Results: | |
977 | * The return value is a token for the new window. This | |
978 | * is not the same as X's token for the window. If an error | |
979 | * occurred in creating the window (e.g. no such display or | |
980 | * screen), then an error message is left in the interp's result and | |
981 | * NULL is returned. | |
982 | * | |
983 | * Side effects: | |
984 | * A new window structure is allocated locally. An X | |
985 | * window is not initially created, but will be created | |
986 | * the first time the window is mapped. | |
987 | * | |
988 | *---------------------------------------------------------------------- | |
989 | */ | |
990 | ||
991 | Tk_Window | |
992 | Tk_CreateWindowFromPath(interp, tkwin, pathName, screenName) | |
993 | Tcl_Interp *interp; /* Interpreter to use for error reporting. | |
994 | * the interp's result is assumed to be | |
995 | * initialized by the caller. */ | |
996 | Tk_Window tkwin; /* Token for any window in application | |
997 | * that is to contain new window. */ | |
998 | char *pathName; /* Path name for new window within the | |
999 | * application of tkwin. The parent of | |
1000 | * this window must already exist, but | |
1001 | * the window itself must not exist. */ | |
1002 | char *screenName; /* If NULL, new window will be on same | |
1003 | * screen as its parent. If non-NULL, | |
1004 | * gives name of screen on which to create | |
1005 | * new window; window will be a top-level | |
1006 | * window. */ | |
1007 | { | |
1008 | #define FIXED_SPACE 5 | |
1009 | char fixedSpace[FIXED_SPACE+1]; | |
1010 | char *p; | |
1011 | Tk_Window parent; | |
1012 | int numChars; | |
1013 | ||
1014 | /* | |
1015 | * Strip the parent's name out of pathName (it's everything up | |
1016 | * to the last dot). There are two tricky parts: (a) must | |
1017 | * copy the parent's name somewhere else to avoid modifying | |
1018 | * the pathName string (for large names, space for the copy | |
1019 | * will have to be malloc'ed); (b) must special-case the | |
1020 | * situation where the parent is ".". | |
1021 | */ | |
1022 | ||
1023 | p = strrchr(pathName, '.'); | |
1024 | if (p == NULL) { | |
1025 | Tcl_AppendResult(interp, "bad window path name \"", pathName, | |
1026 | "\"", (char *) NULL); | |
1027 | return NULL; | |
1028 | } | |
1029 | numChars = p-pathName; | |
1030 | if (numChars > FIXED_SPACE) { | |
1031 | p = (char *) ckalloc((unsigned) (numChars+1)); | |
1032 | } else { | |
1033 | p = fixedSpace; | |
1034 | } | |
1035 | if (numChars == 0) { | |
1036 | *p = '.'; | |
1037 | p[1] = '\0'; | |
1038 | } else { | |
1039 | strncpy(p, pathName, (size_t) numChars); | |
1040 | p[numChars] = '\0'; | |
1041 | } | |
1042 | ||
1043 | /* | |
1044 | * Find the parent window. | |
1045 | */ | |
1046 | ||
1047 | parent = Tk_NameToWindow(interp, p, tkwin); | |
1048 | if (p != fixedSpace) { | |
1049 | ckfree(p); | |
1050 | } | |
1051 | if (parent == NULL) { | |
1052 | return NULL; | |
1053 | } | |
1054 | if (((TkWindow *) parent)->flags & TK_ALREADY_DEAD) { | |
1055 | Tcl_AppendResult(interp, | |
1056 | "can't create window: parent has been destroyed", (char *) NULL); | |
1057 | return NULL; | |
1058 | } else if (((TkWindow *) parent)->flags & TK_CONTAINER) { | |
1059 | Tcl_AppendResult(interp, | |
1060 | "can't create window: its parent has -container = yes", | |
1061 | (char *) NULL); | |
1062 | return NULL; | |
1063 | } | |
1064 | ||
1065 | /* | |
1066 | * Create the window. | |
1067 | */ | |
1068 | ||
1069 | if (screenName == NULL) { | |
1070 | TkWindow *parentPtr = (TkWindow *) parent; | |
1071 | TkWindow *winPtr; | |
1072 | ||
1073 | winPtr = TkAllocWindow(parentPtr->dispPtr, parentPtr->screenNum, | |
1074 | parentPtr); | |
1075 | if (NameWindow(interp, winPtr, parentPtr, pathName+numChars+1) | |
1076 | != TCL_OK) { | |
1077 | Tk_DestroyWindow((Tk_Window) winPtr); | |
1078 | return NULL; | |
1079 | } else { | |
1080 | return (Tk_Window) winPtr; | |
1081 | } | |
1082 | } else { | |
1083 | return CreateTopLevelWindow(interp, parent, pathName+numChars+1, | |
1084 | screenName); | |
1085 | } | |
1086 | } | |
1087 | ||
1088 | /* | |
1089 | *-------------------------------------------------------------- | |
1090 | * | |
1091 | * Tk_DestroyWindow -- | |
1092 | * | |
1093 | * Destroy an existing window. After this call, the caller | |
1094 | * should never again use the token. | |
1095 | * | |
1096 | * Results: | |
1097 | * None. | |
1098 | * | |
1099 | * Side effects: | |
1100 | * The window is deleted, along with all of its children. | |
1101 | * Relevant callback procedures are invoked. | |
1102 | * | |
1103 | *-------------------------------------------------------------- | |
1104 | */ | |
1105 | ||
1106 | void | |
1107 | Tk_DestroyWindow(tkwin) | |
1108 | Tk_Window tkwin; /* Window to destroy. */ | |
1109 | { | |
1110 | TkWindow *winPtr = (TkWindow *) tkwin; | |
1111 | TkDisplay *dispPtr = winPtr->dispPtr; | |
1112 | XEvent event; | |
1113 | ThreadSpecificData *tsdPtr = (ThreadSpecificData *) | |
1114 | Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); | |
1115 | ||
1116 | if (winPtr->flags & TK_ALREADY_DEAD) { | |
1117 | /* | |
1118 | * A destroy event binding caused the window to be destroyed | |
1119 | * again. Ignore the request. | |
1120 | */ | |
1121 | ||
1122 | return; | |
1123 | } | |
1124 | winPtr->flags |= TK_ALREADY_DEAD; | |
1125 | ||
1126 | /* | |
1127 | * Some cleanup needs to be done immediately, rather than later, | |
1128 | * because it needs information that will be destoyed before we | |
1129 | * get to the main cleanup point. For example, TkFocusDeadWindow | |
1130 | * needs to access the parentPtr field from a window, but if | |
1131 | * a Destroy event handler deletes the window's parent this | |
1132 | * field will be NULL before the main cleanup point is reached. | |
1133 | */ | |
1134 | ||
1135 | TkFocusDeadWindow(winPtr); | |
1136 | ||
1137 | /* | |
1138 | * If this is a main window, remove it from the list of main | |
1139 | * windows. This needs to be done now (rather than later with | |
1140 | * all the other main window cleanup) to handle situations where | |
1141 | * a destroy binding for a window calls "exit". In this case | |
1142 | * the child window cleanup isn't complete when exit is called, | |
1143 | * so the reference count of its application doesn't go to zero | |
1144 | * when exit calls Tk_DestroyWindow on ".", so the main window | |
1145 | * doesn't get removed from the list and exit loops infinitely. | |
1146 | * Even worse, if "destroy ." is called by the destroy binding | |
1147 | * before calling "exit", "exit" will attempt to destroy | |
1148 | * mainPtr->winPtr, which no longer exists, and there may be a | |
1149 | * core dump. | |
1150 | * | |
1151 | * Also decrement the display refcount so that if this is the | |
1152 | * last Tk application in this process on this display, the display | |
1153 | * can be closed and its data structures deleted. | |
1154 | */ | |
1155 | ||
1156 | if (winPtr->mainPtr->winPtr == winPtr) { | |
1157 | dispPtr->refCount--; | |
1158 | if (tsdPtr->mainWindowList == winPtr->mainPtr) { | |
1159 | tsdPtr->mainWindowList = winPtr->mainPtr->nextPtr; | |
1160 | } else { | |
1161 | TkMainInfo *prevPtr; | |
1162 | ||
1163 | for (prevPtr = tsdPtr->mainWindowList; | |
1164 | prevPtr->nextPtr != winPtr->mainPtr; | |
1165 | prevPtr = prevPtr->nextPtr) { | |
1166 | /* Empty loop body. */ | |
1167 | } | |
1168 | prevPtr->nextPtr = winPtr->mainPtr->nextPtr; | |
1169 | } | |
1170 | tsdPtr->numMainWindows--; | |
1171 | } | |
1172 | ||
1173 | /* | |
1174 | * Recursively destroy children. | |
1175 | */ | |
1176 | ||
1177 | dispPtr->destroyCount++; | |
1178 | while (winPtr->childList != NULL) { | |
1179 | TkWindow *childPtr; | |
1180 | childPtr = winPtr->childList; | |
1181 | childPtr->flags |= TK_DONT_DESTROY_WINDOW; | |
1182 | Tk_DestroyWindow((Tk_Window) childPtr); | |
1183 | if (winPtr->childList == childPtr) { | |
1184 | /* | |
1185 | * The child didn't remove itself from the child list, so | |
1186 | * let's remove it here. This can happen in some strange | |
1187 | * conditions, such as when a Delete event handler for a | |
1188 | * window deletes the window's parent. | |
1189 | */ | |
1190 | ||
1191 | winPtr->childList = childPtr->nextPtr; | |
1192 | childPtr->parentPtr = NULL; | |
1193 | } | |
1194 | } | |
1195 | if ((winPtr->flags & (TK_CONTAINER|TK_BOTH_HALVES)) | |
1196 | == (TK_CONTAINER|TK_BOTH_HALVES)) { | |
1197 | /* | |
1198 | * This is the container for an embedded application, and | |
1199 | * the embedded application is also in this process. Delete | |
1200 | * the embedded window in-line here, for the same reasons we | |
1201 | * delete children in-line (otherwise, for example, the Tk | |
1202 | * window may appear to exist even though its X window is | |
1203 | * gone; this could cause errors). Special note: it's possible | |
1204 | * that the embedded window has already been deleted, in which | |
1205 | * case TkpGetOtherWindow will return NULL. | |
1206 | */ | |
1207 | ||
1208 | TkWindow *childPtr; | |
1209 | childPtr = TkpGetOtherWindow(winPtr); | |
1210 | if (childPtr != NULL) { | |
1211 | childPtr->flags |= TK_DONT_DESTROY_WINDOW; | |
1212 | Tk_DestroyWindow((Tk_Window) childPtr); | |
1213 | } | |
1214 | } | |
1215 | ||
1216 | /* | |
1217 | * Generate a DestroyNotify event. In order for the DestroyNotify | |
1218 | * event to be processed correctly, need to make sure the window | |
1219 | * exists. This is a bit of a kludge, and may be unnecessarily | |
1220 | * expensive, but without it no event handlers will get called for | |
1221 | * windows that don't exist yet. | |
1222 | * | |
1223 | * Note: if the window's pathName is NULL it means that the window | |
1224 | * was not successfully initialized in the first place, so we should | |
1225 | * not make the window exist or generate the event. | |
1226 | */ | |
1227 | ||
1228 | if (winPtr->pathName != NULL) { | |
1229 | if (winPtr->window == None) { | |
1230 | Tk_MakeWindowExist(tkwin); | |
1231 | } | |
1232 | event.type = DestroyNotify; | |
1233 | event.xdestroywindow.serial = | |
1234 | LastKnownRequestProcessed(winPtr->display); | |
1235 | event.xdestroywindow.send_event = False; | |
1236 | event.xdestroywindow.display = winPtr->display; | |
1237 | event.xdestroywindow.event = winPtr->window; | |
1238 | event.xdestroywindow.window = winPtr->window; | |
1239 | Tk_HandleEvent(&event); | |
1240 | } | |
1241 | ||
1242 | /* | |
1243 | * Cleanup the data structures associated with this window. | |
1244 | */ | |
1245 | ||
1246 | if (winPtr->flags & TK_TOP_LEVEL) { | |
1247 | TkWmDeadWindow(winPtr); | |
1248 | } else if (winPtr->flags & TK_WM_COLORMAP_WINDOW) { | |
1249 | TkWmRemoveFromColormapWindows(winPtr); | |
1250 | } | |
1251 | if (winPtr->window != None) { | |
1252 | #if defined(MAC_TCL) || defined(__WIN32__) | |
1253 | XDestroyWindow(winPtr->display, winPtr->window); | |
1254 | #else | |
1255 | if ((winPtr->flags & TK_TOP_LEVEL) | |
1256 | || !(winPtr->flags & TK_DONT_DESTROY_WINDOW)) { | |
1257 | /* | |
1258 | * The parent has already been destroyed and this isn't | |
1259 | * a top-level window, so this window will be destroyed | |
1260 | * implicitly when the parent's X window is destroyed; | |
1261 | * it's much faster not to do an explicit destroy of this | |
1262 | * X window. | |
1263 | */ | |
1264 | ||
1265 | dispPtr->lastDestroyRequest = NextRequest(winPtr->display); | |
1266 | XDestroyWindow(winPtr->display, winPtr->window); | |
1267 | } | |
1268 | #endif | |
1269 | TkFreeWindowId(dispPtr, winPtr->window); | |
1270 | Tcl_DeleteHashEntry(Tcl_FindHashEntry(&dispPtr->winTable, | |
1271 | (char *) winPtr->window)); | |
1272 | winPtr->window = None; | |
1273 | } | |
1274 | dispPtr->destroyCount--; | |
1275 | UnlinkWindow(winPtr); | |
1276 | TkEventDeadWindow(winPtr); | |
1277 | TkBindDeadWindow(winPtr); | |
1278 | #ifdef TK_USE_INPUT_METHODS | |
1279 | if (winPtr->inputContext != NULL) { | |
1280 | XDestroyIC(winPtr->inputContext); | |
1281 | } | |
1282 | #endif /* TK_USE_INPUT_METHODS */ | |
1283 | if (winPtr->tagPtr != NULL) { | |
1284 | TkFreeBindingTags(winPtr); | |
1285 | } | |
1286 | TkOptionDeadWindow(winPtr); | |
1287 | TkSelDeadWindow(winPtr); | |
1288 | TkGrabDeadWindow(winPtr); | |
1289 | if (winPtr->mainPtr != NULL) { | |
1290 | if (winPtr->pathName != NULL) { | |
1291 | Tk_DeleteAllBindings(winPtr->mainPtr->bindingTable, | |
1292 | (ClientData) winPtr->pathName); | |
1293 | Tcl_DeleteHashEntry(Tcl_FindHashEntry(&winPtr->mainPtr->nameTable, | |
1294 | winPtr->pathName)); | |
1295 | } | |
1296 | winPtr->mainPtr->refCount--; | |
1297 | if (winPtr->mainPtr->refCount == 0) { | |
1298 | register TkCmd *cmdPtr; | |
1299 | ||
1300 | /* | |
1301 | * We just deleted the last window in the application. Delete | |
1302 | * the TkMainInfo structure too and replace all of Tk's commands | |
1303 | * with dummy commands that return errors. Also delete the | |
1304 | * "send" command to unregister the interpreter. | |
1305 | * | |
1306 | * NOTE: Only replace the commands it if the interpreter is | |
1307 | * not being deleted. If it *is*, the interpreter cleanup will | |
1308 | * do all the needed work. | |
1309 | */ | |
1310 | ||
1311 | if ((winPtr->mainPtr->interp != NULL) && | |
1312 | (!Tcl_InterpDeleted(winPtr->mainPtr->interp))) { | |
1313 | for (cmdPtr = commands; cmdPtr->name != NULL; cmdPtr++) { | |
1314 | Tcl_CreateCommand(winPtr->mainPtr->interp, cmdPtr->name, | |
1315 | TkDeadAppCmd, (ClientData) NULL, | |
1316 | (void (*) _ANSI_ARGS_((ClientData))) NULL); | |
1317 | } | |
1318 | Tcl_CreateCommand(winPtr->mainPtr->interp, "send", | |
1319 | TkDeadAppCmd, (ClientData) NULL, | |
1320 | (void (*) _ANSI_ARGS_((ClientData))) NULL); | |
1321 | Tcl_UnlinkVar(winPtr->mainPtr->interp, "tk_strictMotif"); | |
1322 | } | |
1323 | ||
1324 | Tcl_DeleteHashTable(&winPtr->mainPtr->nameTable); | |
1325 | TkBindFree(winPtr->mainPtr); | |
1326 | TkDeleteAllImages(winPtr->mainPtr); | |
1327 | TkFontPkgFree(winPtr->mainPtr); | |
1328 | ||
1329 | /* | |
1330 | * When embedding Tk into other applications, make sure | |
1331 | * that all destroy events reach the server. Otherwise | |
1332 | * the embedding application may also attempt to destroy | |
1333 | * the windows, resulting in an X error | |
1334 | */ | |
1335 | ||
1336 | if (winPtr->flags & TK_EMBEDDED) { | |
1337 | XSync(winPtr->display,False) ; | |
1338 | } | |
1339 | ckfree((char *) winPtr->mainPtr); | |
1340 | ||
1341 | /* | |
1342 | * If no other applications are using the display, close the | |
1343 | * display now and relinquish its data structures. | |
1344 | */ | |
1345 | ||
1346 | if (dispPtr->refCount <= 0) { | |
1347 | #ifdef NOT_YET | |
1348 | /* | |
1349 | * I have disabled this code because on Windows there are | |
1350 | * still order dependencies in close-down. All displays | |
1351 | * and resources will get closed down properly anyway at | |
1352 | * exit, through the exit handler. | |
1353 | */ | |
1354 | ||
1355 | TkDisplay *theDispPtr, *backDispPtr; | |
1356 | ||
1357 | /* | |
1358 | * Splice this display out of the list of displays. | |
1359 | */ | |
1360 | ||
1361 | for (theDispPtr = displayList, backDispPtr = NULL; | |
1362 | (theDispPtr != winPtr->dispPtr) && | |
1363 | (theDispPtr != NULL); | |
1364 | theDispPtr = theDispPtr->nextPtr) { | |
1365 | backDispPtr = theDispPtr; | |
1366 | } | |
1367 | if (theDispPtr == NULL) { | |
1368 | panic("could not find display to close!"); | |
1369 | } | |
1370 | if (backDispPtr == NULL) { | |
1371 | displayList = theDispPtr->nextPtr; | |
1372 | } else { | |
1373 | backDispPtr->nextPtr = theDispPtr->nextPtr; | |
1374 | } | |
1375 | ||
1376 | /* | |
1377 | * Found and spliced it out, now actually do the cleanup. | |
1378 | */ | |
1379 | ||
1380 | if (dispPtr->name != NULL) { | |
1381 | ckfree(dispPtr->name); | |
1382 | } | |
1383 | ||
1384 | Tcl_DeleteHashTable(&(dispPtr->winTable)); | |
1385 | ||
1386 | /* | |
1387 | * Cannot yet close the display because we still have | |
1388 | * order of deletion problems. Defer until exit handling | |
1389 | * instead. At that time, the display will cleanly shut | |
1390 | * down (hopefully..). (JYL) | |
1391 | */ | |
1392 | ||
1393 | TkpCloseDisplay(dispPtr); | |
1394 | ||
1395 | /* | |
1396 | * There is lots more to clean up, we leave it at this for | |
1397 | * the time being. | |
1398 | */ | |
1399 | #endif | |
1400 | } | |
1401 | } | |
1402 | } | |
1403 | ckfree((char *) winPtr); | |
1404 | } | |
1405 | ||
1406 | /* | |
1407 | *-------------------------------------------------------------- | |
1408 | * | |
1409 | * Tk_MapWindow -- | |
1410 | * | |
1411 | * Map a window within its parent. This may require the | |
1412 | * window and/or its parents to actually be created. | |
1413 | * | |
1414 | * Results: | |
1415 | * None. | |
1416 | * | |
1417 | * Side effects: | |
1418 | * The given window will be mapped. Windows may also | |
1419 | * be created. | |
1420 | * | |
1421 | *-------------------------------------------------------------- | |
1422 | */ | |
1423 | ||
1424 | void | |
1425 | Tk_MapWindow(tkwin) | |
1426 | Tk_Window tkwin; /* Token for window to map. */ | |
1427 | { | |
1428 | register TkWindow *winPtr = (TkWindow *) tkwin; | |
1429 | XEvent event; | |
1430 | ||
1431 | if (winPtr->flags & TK_MAPPED) { | |
1432 | return; | |
1433 | } | |
1434 | if (winPtr->window == None) { | |
1435 | Tk_MakeWindowExist(tkwin); | |
1436 | } | |
1437 | if (winPtr->flags & TK_TOP_LEVEL) { | |
1438 | /* | |
1439 | * Lots of special processing has to be done for top-level | |
1440 | * windows. Let tkWm.c handle everything itself. | |
1441 | */ | |
1442 | ||
1443 | TkWmMapWindow(winPtr); | |
1444 | return; | |
1445 | } | |
1446 | winPtr->flags |= TK_MAPPED; | |
1447 | XMapWindow(winPtr->display, winPtr->window); | |
1448 | event.type = MapNotify; | |
1449 | event.xmap.serial = LastKnownRequestProcessed(winPtr->display); | |
1450 | event.xmap.send_event = False; | |
1451 | event.xmap.display = winPtr->display; | |
1452 | event.xmap.event = winPtr->window; | |
1453 | event.xmap.window = winPtr->window; | |
1454 | event.xmap.override_redirect = winPtr->atts.override_redirect; | |
1455 | Tk_HandleEvent(&event); | |
1456 | } | |
1457 | ||
1458 | /* | |
1459 | *-------------------------------------------------------------- | |
1460 | * | |
1461 | * Tk_MakeWindowExist -- | |
1462 | * | |
1463 | * Ensure that a particular window actually exists. This | |
1464 | * procedure shouldn't normally need to be invoked from | |
1465 | * outside the Tk package, but may be needed if someone | |
1466 | * wants to manipulate a window before mapping it. | |
1467 | * | |
1468 | * Results: | |
1469 | * None. | |
1470 | * | |
1471 | * Side effects: | |
1472 | * When the procedure returns, the X window associated with | |
1473 | * tkwin is guaranteed to exist. This may require the | |
1474 | * window's ancestors to be created also. | |
1475 | * | |
1476 | *-------------------------------------------------------------- | |
1477 | */ | |
1478 | ||
1479 | void | |
1480 | Tk_MakeWindowExist(tkwin) | |
1481 | Tk_Window tkwin; /* Token for window. */ | |
1482 | { | |
1483 | register TkWindow *winPtr = (TkWindow *) tkwin; | |
1484 | TkWindow *winPtr2; | |
1485 | Window parent; | |
1486 | Tcl_HashEntry *hPtr; | |
1487 | int new; | |
1488 | ||
1489 | if (winPtr->window != None) { | |
1490 | return; | |
1491 | } | |
1492 | ||
1493 | if ((winPtr->parentPtr == NULL) || (winPtr->flags & TK_TOP_LEVEL)) { | |
1494 | parent = XRootWindow(winPtr->display, winPtr->screenNum); | |
1495 | } else { | |
1496 | if (winPtr->parentPtr->window == None) { | |
1497 | Tk_MakeWindowExist((Tk_Window) winPtr->parentPtr); | |
1498 | } | |
1499 | parent = winPtr->parentPtr->window; | |
1500 | } | |
1501 | ||
1502 | if (winPtr->classProcsPtr != NULL | |
1503 | && winPtr->classProcsPtr->createProc != NULL) { | |
1504 | winPtr->window = (*winPtr->classProcsPtr->createProc)(tkwin, parent, | |
1505 | winPtr->instanceData); | |
1506 | } else { | |
1507 | winPtr->window = TkpMakeWindow(winPtr, parent); | |
1508 | } | |
1509 | ||
1510 | hPtr = Tcl_CreateHashEntry(&winPtr->dispPtr->winTable, | |
1511 | (char *) winPtr->window, &new); | |
1512 | Tcl_SetHashValue(hPtr, winPtr); | |
1513 | winPtr->dirtyAtts = 0; | |
1514 | winPtr->dirtyChanges = 0; | |
1515 | #ifdef TK_USE_INPUT_METHODS | |
1516 | winPtr->inputContext = NULL; | |
1517 | #endif /* TK_USE_INPUT_METHODS */ | |
1518 | ||
1519 | if (!(winPtr->flags & TK_TOP_LEVEL)) { | |
1520 | /* | |
1521 | * If any siblings higher up in the stacking order have already | |
1522 | * been created then move this window to its rightful position | |
1523 | * in the stacking order. | |
1524 | * | |
1525 | * NOTE: this code ignores any changes anyone might have made | |
1526 | * to the sibling and stack_mode field of the window's attributes, | |
1527 | * so it really isn't safe for these to be manipulated except | |
1528 | * by calling Tk_RestackWindow. | |
1529 | */ | |
1530 | ||
1531 | for (winPtr2 = winPtr->nextPtr; winPtr2 != NULL; | |
1532 | winPtr2 = winPtr2->nextPtr) { | |
1533 | if ((winPtr2->window != None) | |
1534 | && !(winPtr2->flags & (TK_TOP_LEVEL|TK_REPARENTED))) { | |
1535 | XWindowChanges changes; | |
1536 | changes.sibling = winPtr2->window; | |
1537 | changes.stack_mode = Below; | |
1538 | XConfigureWindow(winPtr->display, winPtr->window, | |
1539 | CWSibling|CWStackMode, &changes); | |
1540 | break; | |
1541 | } | |
1542 | } | |
1543 | ||
1544 | /* | |
1545 | * If this window has a different colormap than its parent, add | |
1546 | * the window to the WM_COLORMAP_WINDOWS property for its top-level. | |
1547 | */ | |
1548 | ||
1549 | if ((winPtr->parentPtr != NULL) && | |
1550 | (winPtr->atts.colormap != winPtr->parentPtr->atts.colormap)) { | |
1551 | TkWmAddToColormapWindows(winPtr); | |
1552 | winPtr->flags |= TK_WM_COLORMAP_WINDOW; | |
1553 | } | |
1554 | } | |
1555 | ||
1556 | /* | |
1557 | * Issue a ConfigureNotify event if there were deferred configuration | |
1558 | * changes (but skip it if the window is being deleted; the | |
1559 | * ConfigureNotify event could cause problems if we're being called | |
1560 | * from Tk_DestroyWindow under some conditions). | |
1561 | */ | |
1562 | ||
1563 | if ((winPtr->flags & TK_NEED_CONFIG_NOTIFY) | |
1564 | && !(winPtr->flags & TK_ALREADY_DEAD)){ | |
1565 | winPtr->flags &= ~TK_NEED_CONFIG_NOTIFY; | |
1566 | TkDoConfigureNotify(winPtr); | |
1567 | } | |
1568 | } | |
1569 | ||
1570 | /* | |
1571 | *-------------------------------------------------------------- | |
1572 | * | |
1573 | * Tk_UnmapWindow, etc. -- | |
1574 | * | |
1575 | * There are several procedures under here, each of which | |
1576 | * mirrors an existing X procedure. In addition to performing | |
1577 | * the functions of the corresponding procedure, each | |
1578 | * procedure also updates the local window structure and | |
1579 | * synthesizes an X event (if the window's structure is being | |
1580 | * managed internally). | |
1581 | * | |
1582 | * Results: | |
1583 | * See the manual entries. | |
1584 | * | |
1585 | * Side effects: | |
1586 | * See the manual entries. | |
1587 | * | |
1588 | *-------------------------------------------------------------- | |
1589 | */ | |
1590 | ||
1591 | void | |
1592 | Tk_UnmapWindow(tkwin) | |
1593 | Tk_Window tkwin; /* Token for window to unmap. */ | |
1594 | { | |
1595 | register TkWindow *winPtr = (TkWindow *) tkwin; | |
1596 | ||
1597 | if (!(winPtr->flags & TK_MAPPED) || (winPtr->flags & TK_ALREADY_DEAD)) { | |
1598 | return; | |
1599 | } | |
1600 | if (winPtr->flags & TK_TOP_LEVEL) { | |
1601 | /* | |
1602 | * Special processing has to be done for top-level windows. Let | |
1603 | * tkWm.c handle everything itself. | |
1604 | */ | |
1605 | ||
1606 | TkWmUnmapWindow(winPtr); | |
1607 | return; | |
1608 | } | |
1609 | winPtr->flags &= ~TK_MAPPED; | |
1610 | XUnmapWindow(winPtr->display, winPtr->window); | |
1611 | if (!(winPtr->flags & TK_TOP_LEVEL)) { | |
1612 | XEvent event; | |
1613 | ||
1614 | event.type = UnmapNotify; | |
1615 | event.xunmap.serial = LastKnownRequestProcessed(winPtr->display); | |
1616 | event.xunmap.send_event = False; | |
1617 | event.xunmap.display = winPtr->display; | |
1618 | event.xunmap.event = winPtr->window; | |
1619 | event.xunmap.window = winPtr->window; | |
1620 | event.xunmap.from_configure = False; | |
1621 | Tk_HandleEvent(&event); | |
1622 | } | |
1623 | } | |
1624 | ||
1625 | void | |
1626 | Tk_ConfigureWindow(tkwin, valueMask, valuePtr) | |
1627 | Tk_Window tkwin; /* Window to re-configure. */ | |
1628 | unsigned int valueMask; /* Mask indicating which parts of | |
1629 | * *valuePtr are to be used. */ | |
1630 | XWindowChanges *valuePtr; /* New values. */ | |
1631 | { | |
1632 | register TkWindow *winPtr = (TkWindow *) tkwin; | |
1633 | ||
1634 | if (valueMask & CWX) { | |
1635 | winPtr->changes.x = valuePtr->x; | |
1636 | } | |
1637 | if (valueMask & CWY) { | |
1638 | winPtr->changes.y = valuePtr->y; | |
1639 | } | |
1640 | if (valueMask & CWWidth) { | |
1641 | winPtr->changes.width = valuePtr->width; | |
1642 | } | |
1643 | if (valueMask & CWHeight) { | |
1644 | winPtr->changes.height = valuePtr->height; | |
1645 | } | |
1646 | if (valueMask & CWBorderWidth) { | |
1647 | winPtr->changes.border_width = valuePtr->border_width; | |
1648 | } | |
1649 | if (valueMask & (CWSibling|CWStackMode)) { | |
1650 | panic("Can't set sibling or stack mode from Tk_ConfigureWindow."); | |
1651 | } | |
1652 | ||
1653 | if (winPtr->window != None) { | |
1654 | XConfigureWindow(winPtr->display, winPtr->window, | |
1655 | valueMask, valuePtr); | |
1656 | TkDoConfigureNotify(winPtr); | |
1657 | } else { | |
1658 | winPtr->dirtyChanges |= valueMask; | |
1659 | winPtr->flags |= TK_NEED_CONFIG_NOTIFY; | |
1660 | } | |
1661 | } | |
1662 | ||
1663 | void | |
1664 | Tk_MoveWindow(tkwin, x, y) | |
1665 | Tk_Window tkwin; /* Window to move. */ | |
1666 | int x, y; /* New location for window (within | |
1667 | * parent). */ | |
1668 | { | |
1669 | register TkWindow *winPtr = (TkWindow *) tkwin; | |
1670 | ||
1671 | winPtr->changes.x = x; | |
1672 | winPtr->changes.y = y; | |
1673 | if (winPtr->window != None) { | |
1674 | XMoveWindow(winPtr->display, winPtr->window, x, y); | |
1675 | TkDoConfigureNotify(winPtr); | |
1676 | } else { | |
1677 | winPtr->dirtyChanges |= CWX|CWY; | |
1678 | winPtr->flags |= TK_NEED_CONFIG_NOTIFY; | |
1679 | } | |
1680 | } | |
1681 | ||
1682 | void | |
1683 | Tk_ResizeWindow(tkwin, width, height) | |
1684 | Tk_Window tkwin; /* Window to resize. */ | |
1685 | int width, height; /* New dimensions for window. */ | |
1686 | { | |
1687 | register TkWindow *winPtr = (TkWindow *) tkwin; | |
1688 | ||
1689 | winPtr->changes.width = (unsigned) width; | |
1690 | winPtr->changes.height = (unsigned) height; | |
1691 | if (winPtr->window != None) { | |
1692 | XResizeWindow(winPtr->display, winPtr->window, (unsigned) width, | |
1693 | (unsigned) height); | |
1694 | TkDoConfigureNotify(winPtr); | |
1695 | } else { | |
1696 | winPtr->dirtyChanges |= CWWidth|CWHeight; | |
1697 | winPtr->flags |= TK_NEED_CONFIG_NOTIFY; | |
1698 | } | |
1699 | } | |
1700 | ||
1701 | void | |
1702 | Tk_MoveResizeWindow(tkwin, x, y, width, height) | |
1703 | Tk_Window tkwin; /* Window to move and resize. */ | |
1704 | int x, y; /* New location for window (within | |
1705 | * parent). */ | |
1706 | int width, height; /* New dimensions for window. */ | |
1707 | { | |
1708 | register TkWindow *winPtr = (TkWindow *) tkwin; | |
1709 | ||
1710 | winPtr->changes.x = x; | |
1711 | winPtr->changes.y = y; | |
1712 | winPtr->changes.width = (unsigned) width; | |
1713 | winPtr->changes.height = (unsigned) height; | |
1714 | if (winPtr->window != None) { | |