/*
* 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.56.2.1 2003/07/16 22:54:26 hobbs Exp $
*/
#include "tkPort.h"
#include "tkInt.h"
#include "tkOption.h"
#include "tkOption.m"
#undef Tk_OptionObjCmd
#if !( defined(__WIN32__) || defined(MAC_TCL) || defined(MAC_OSX_TK))
#include "tkUnixInt.h"
#endif
/*
* Type used to keep track of Window objects that were
* only partically deallocated by Tk_DestroyWindow.
*/
#define HD_CLEANUP 1
#define HD_FOCUS 2
#define HD_MAIN_WIN 4
#define HD_DESTROY_COUNT 8
#define HD_DESTROY_EVENT 0x10
typedef struct TkHalfdeadWindow {
int flags;
struct TkWindow *winPtr;
struct TkHalfdeadWindow *nextPtr;
} TkHalfdeadWindow;
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. */
TkHalfdeadWindow *halfdeadWindowList;
/* First in list of partially deallocated
* windows. */
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_ObjCmdProc *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;
#ifdef _LANG
#define LangLoaded(x) NULL
#else
#define LangLoaded(x) x
#endif
static TkCmd commands[] = {
/*
* Commands that are part of the intrinsics:
*/
{"bell", NULL, Tk_BellObjCmd, 0, 1},
{"bind", NULL, Tk_BindObjCmd, 1, 1},
{"bindtags", NULL, Tk_BindtagsObjCmd, 1, 1},
{"clipboard", NULL, Tk_ClipboardObjCmd, 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", NULL, Tk_GrabObjCmd, 0, 1},
{"grid", NULL, Tk_GridObjCmd, 1, 1},
{"image", NULL, Tk_ImageObjCmd, 1, 1},
{"lower", NULL, Tk_LowerObjCmd, 1, 1},
{"option", NULL, Tk_OptionObjCmd, 1, 1},
{"pack", NULL, Tk_PackObjCmd, 1, 1},
{"place", NULL, Tk_PlaceObjCmd, 1, 0},
{"raise", NULL, Tk_RaiseObjCmd, 1, 1},
{"selection", NULL, Tk_SelectionObjCmd, 0, 1},
{"tk", NULL, Tk_TkObjCmd, 1, 1},
{"tkwait", NULL, Tk_TkwaitObjCmd, 1, 1},
#if defined(__WIN32__) || defined(MAC_TCL) || defined(MAC_OSX_TK)
{"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", NULL, Tk_WmObjCmd, 0, 1},
/*
* Widget class commands.
*/
{"button", NULL, Tk_ButtonObjCmd, 1, 0},
{"canvas", NULL, LangLoaded(Tk_CanvasObjCmd), 1, 1},
{"checkbutton", NULL, Tk_CheckbuttonObjCmd, 1, 0},
{"entry", NULL, LangLoaded(Tk_EntryObjCmd), 1, 0},
{"frame", NULL, Tk_FrameObjCmd, 1, 0},
{"label", NULL, Tk_LabelObjCmd, 1, 0},
{"labelframe", NULL, Tk_LabelframeObjCmd, 1, 0},
{"listbox", NULL, LangLoaded(Tk_ListboxObjCmd), 1, 0},
{"menubutton", NULL, LangLoaded(Tk_MenubuttonObjCmd), 1, 0},
{"message", NULL, Tk_MessageObjCmd, 1, 0},
{"panedwindow", NULL, Tk_PanedWindowObjCmd, 1, 0},
{"radiobutton", NULL, Tk_RadiobuttonObjCmd, 1, 0},
{"scale", NULL, LangLoaded(Tk_ScaleObjCmd), 1, 0},
{"scrollbar", LangLoaded(Tk_ScrollbarCmd), NULL, 1, 1},
{"spinbox", NULL, LangLoaded(Tk_SpinboxObjCmd), 1, 0},
{"text", LangLoaded(Tk_TextCmd), NULL, 1, 1},
{"toplevel", NULL, Tk_ToplevelObjCmd, 0, 0},
/*
* Misc.
*/
#if defined(MAC_TCL) || defined(MAC_OSX_TK)
{"::tk::unsupported::MacWindowStyle",
TkUnsupported1Cmd, NULL, 1, 1},
#endif
{(char *) NULL, (int (*) _ANSI_ARGS_((ClientData, Tcl_Interp *, int, Tcl_Obj *CONST *))) 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 Tcl_Obj * name;
static Tcl_Obj * display;
static Tcl_Obj * geometry;
static Tcl_Obj * colormap = NULL;
static Tcl_Obj * visual = NULL;
static Tcl_Obj * use = 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, CONST char *name,
CONST char *screenName, unsigned int flags));
static void DeleteWindowsExitProc _ANSI_ARGS_((
ClientData clientData));
static TkDisplay * GetScreen _ANSI_ARGS_((Tcl_Interp *interp,
CONST char *screenName, int *screenPtr));
static int Initialize _ANSI_ARGS_((Tcl_Interp *interp));
static int NameWindow _ANSI_ARGS_((Tcl_Interp *interp,
TkWindow *winPtr, TkWindow *parentPtr,
CONST char *name));
static void UnlinkWindow _ANSI_ARGS_((TkWindow *winPtr));
/*
*----------------------------------------------------------------------
*
* TkCloseDisplay --
* Closing the display can lead to order of deletion problems.
* We defer it until exit handling for Mac/Win, but since Unix can
* use many displays, try and clean it up as best as possible.
*
* Results:
* None.
*
* Side effects:
* Resources associated with the display will be free.
* The display may not be referenced at all after this.
*----------------------------------------------------------------------
*/
static void
TkCloseDisplay(TkDisplay *dispPtr)
{
TkClipCleanup(dispPtr);
if (dispPtr->name != NULL) {
ckfree(dispPtr->name);
}
if (dispPtr->atomInit) {
Tcl_DeleteHashTable(&dispPtr->nameTable);
Tcl_DeleteHashTable(&dispPtr->atomTable);
dispPtr->atomInit = 0;
}
if (dispPtr->errorPtr != NULL) {
TkErrorHandler *errorPtr;
for (errorPtr = dispPtr->errorPtr;
errorPtr != NULL;
errorPtr = dispPtr->errorPtr) {
dispPtr->errorPtr = errorPtr->nextPtr;
ckfree((char *) errorPtr);
}
}
TkGCCleanup(dispPtr);
TkpCloseDisplay(dispPtr);
/*
* Delete winTable after TkpCloseDisplay since special windows
* may need call Tk_DestroyWindow and it checks the winTable.
*/
Tcl_DeleteHashTable(&dispPtr->winTable);
ckfree((char *) dispPtr);
/*
* There is more to clean up, we leave it at this for the time being.
*/
}
/*
*----------------------------------------------------------------------
*
* 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, flags)
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. */
CONST char *name; /* Name for new window; if parent is
* non-NULL, must be unique among parent's
* children. */
CONST 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. */
unsigned int flags; /* Additional flags to set on the window. */
{
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);
#ifndef _LANG
Tk_CreateImageType(&tkPhotoImageType);
/*
* Create built-in photo image formats.
*/
Tk_CreatePhotoImageFormat(&tkImgFmtGIF);
Tk_CreateOldPhotoImageFormat(&tkImgFmtPPM);
#endif
/*
* 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);
/*
* Set the flags specified in the call.
*/
winPtr->flags |= flags;
/*
* 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_HIERARCHY flag immediately here; otherwise
* Tk_DestroyWindow will core dump if it is called before the flag
* has been set.)
*/
winPtr->flags |= TK_TOP_HIERARCHY|TK_TOP_LEVEL|TK_HAS_WRAPPER|TK_WIN_MANAGED;
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. */
CONST char *screenName; /* Name for screen. NULL or empty means
* use DISPLAY envariable. */
int *screenPtr; /* Where to store screen number. */
{
register TkDisplay *dispPtr;
CONST 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 = tsdPtr->displayList; ; dispPtr = dispPtr->nextPtr) {
if (dispPtr == NULL) {
/*
* The private function zeros out dispPtr when it is created,
* so we only need to initialize the non-zero items.
*/
dispPtr = TkpOpenDisplay(screenName);
if (dispPtr == NULL) {
Tcl_AppendResult(interp, "couldn't connect to display \"",
screenName, "\"", (char *) NULL);
return (TkDisplay *) NULL;
}
dispPtr->nextPtr = tsdPtr->displayList; /* TkGetDisplayList(); */
tsdPtr->displayList = dispPtr;
dispPtr->lastEventTime = CurrentTime;
dispPtr->bindInfoStale = 1;
dispPtr->cursorFont = None;
dispPtr->warpWindow = None;
dispPtr->multipleAtom = None;
/*
* By default we do want to collapse motion events in
* Tk_QueueWindowEvent.
*/
dispPtr->flags |= TK_DISPLAY_COLLAPSE_MOTION_EVENTS;
#ifdef TK_USE_INPUT_METHODS
#ifdef X_HAVE_UTF8_STRING
/* If X can do UTF-8 for us avoid locale/encode issues
by defaulting to using input methods that way.
*/
dispPtr->flags |= TK_DISPLAY_USE_IM;
#endif
#endif
Tcl_InitHashTable(&dispPtr->winTable, TCL_ONE_WORD_KEYS);
dispPtr->name = (char *) ckalloc((unsigned) (length+1));
strncpy(dispPtr->name, screenName, length);
dispPtr->name[length] = '\0';
TkInitXId(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->internalBorderLeft = 0;
winPtr->wmInfoPtr = NULL;
winPtr->classProcsPtr = NULL;
winPtr->instanceData = NULL;
winPtr->privatePtr = NULL;
winPtr->internalBorderRight = 0;
winPtr->internalBorderTop = 0;
winPtr->internalBorderBottom = 0;
winPtr->minReqWidth = 0;
winPtr->minReqHeight = 0;
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.). */
CONST 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++;
/*
* If this is an anonymous window (ie, it has no name), just return OK
* now.
*/
if (winPtr->flags & TK_ANONYMOUS_WINDOW) {
return TCL_OK;
}
/*
* For non-anonymous windows, set up the window name.
*/
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. */
CONST 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;
Var variable;
char *libDir = LangLibraryDir();
static char *argv[] = {(char *) NULL};
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, /* flags */ 0);
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);
mainPtr->deletionEpoch = 0l;
TkEventInit();
TkBindInit(mainPtr);
TkFontPkgInit(mainPtr);
TkStylePkgInit(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));
Lang_NewMainWindow(interp, tkwin);
/*
* Set variables for the intepreter.
*/
isSafe = Tcl_IsSafe(interp);
for (cmdPtr = commands; cmdPtr->name != NULL; cmdPtr++) {
#ifndef _LANG
if ((cmdPtr->cmdProc == NULL) && (cmdPtr->objProc == NULL)) {
panic("TkCreateMainWindow: builtin command with NULL string and object procs");
}
#endif
if (cmdPtr->passMainWindow) {
clientData = (ClientData) tkwin;
} else {
clientData = (ClientData) NULL;
}
if (cmdPtr->cmdProc != NULL) {
Tcl_CreateObjCommand(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);
}
}
}
Tcl_SetVar(interp, "tk_library", libDir, TCL_GLOBAL_ONLY);
TkCreateMenuCmd(interp);
/*
* Make the main window into a toplevel widget, and give it an initial
* requested size.
*/
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. */
CONST char *name; /* Name for new window. Must be unique
* among parent's children. */
CONST 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,
/* flags */ 0);
}
}
/*
*--------------------------------------------------------------
*
* Tk_CreateAnonymousWindow --
*
* Create a new internal or top-level window as a child of an
* existing window; this window will be anonymous (unnamed), so
* it will not be visible at the Tcl level.
*
* 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_CreateAnonymousWindow(interp, parent, 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. */
CONST 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);
/*
* Add the anonymous window flag now, so that NameWindow will behave
* correctly.
*/
winPtr->flags |= TK_ANONYMOUS_WINDOW;
if (NameWindow(interp, winPtr, parentPtr, (char *)NULL) != TCL_OK) {
Tk_DestroyWindow((Tk_Window) winPtr);
return NULL;
}
return (Tk_Window) winPtr;
} else {
return CreateTopLevelWindow(interp, parent, (char *)NULL, screenName,
TK_ANONYMOUS_WINDOW);
}
}
/*
*----------------------------------------------------------------------
*
* 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. */
CONST 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. */
CONST 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 = (int) (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, /* flags */ 0);
}
}
/*
*--------------------------------------------------------------
*
* Tk_DestroyWindow --
*
* Destroy an existing window. After this call, the caller
* should never again use the token. Note that this function
* can be reentered to destroy a window that was only
* partially destroyed before a call to exit.
*
* 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;
TkHalfdeadWindow *halfdeadPtr, *prev_halfdeadPtr;
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;
/*
* Unless we are cleaning up a half dead
* window from DeleteWindowsExitProc,
* add this window to the half dead list.
*/
if (tsdPtr->halfdeadWindowList &&
(tsdPtr->halfdeadWindowList->flags & HD_CLEANUP) &&
(tsdPtr->halfdeadWindowList->winPtr == winPtr)) {
halfdeadPtr = tsdPtr->halfdeadWindowList;
} else {
halfdeadPtr = (TkHalfdeadWindow *) ckalloc(sizeof(TkHalfdeadWindow));
halfdeadPtr->flags = 0;
halfdeadPtr->winPtr = winPtr;
halfdeadPtr->nextPtr = tsdPtr->halfdeadWindowList;
tsdPtr->halfdeadWindowList = halfdeadPtr;
}
/*
* 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.
*/
if (!(halfdeadPtr->flags & HD_FOCUS)) {
halfdeadPtr->flags |= HD_FOCUS;
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.
* This situation is dealt with using the half dead window
* list. Windows that are half dead gets cleaned up during exit.
*
* 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 (!(halfdeadPtr->flags & HD_MAIN_WIN) &&
winPtr->mainPtr != NULL && winPtr->mainPtr->winPtr == winPtr) {
halfdeadPtr->flags |= HD_MAIN_WIN;
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. Note that this child
* window block may need to be run multiple times
* in the case where a child window has a Destroy
* binding that calls exit.
*/
if (!(halfdeadPtr->flags & HD_DESTROY_COUNT)) {
halfdeadPtr->flags |= HD_DESTROY_COUNT;
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 Destroy event handler for a
* window destroys 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 and the window is not an
* anonymous window, 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 (!(halfdeadPtr->flags & HD_DESTROY_EVENT) &&
winPtr->pathName != NULL &&
!(winPtr->flags & TK_ANONYMOUS_WINDOW)) {
halfdeadPtr->flags |= HD_DESTROY_EVENT;
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);
}
/*
* No additional bindings that could call exit
* should be invoked from this point on,
* so it is safe to remove this window
* from the half dead list.
*/
for (prev_halfdeadPtr = NULL,
halfdeadPtr = tsdPtr->halfdeadWindowList;
halfdeadPtr != NULL; ) {
if (halfdeadPtr->winPtr == winPtr) {
if (prev_halfdeadPtr == NULL)
tsdPtr->halfdeadWindowList = halfdeadPtr->nextPtr;
else
prev_halfdeadPtr->nextPtr = halfdeadPtr->nextPtr;
ckfree((char *) halfdeadPtr);
break;
}
prev_halfdeadPtr = halfdeadPtr;
halfdeadPtr = halfdeadPtr->nextPtr;
}
if (halfdeadPtr == NULL)
panic("window not found on half dead list");
/*
* Cleanup the data structures associated with this window.
*/
if (winPtr->flags & TK_WIN_MANAGED) {
TkWmDeadWindow(winPtr);
} else if (winPtr->flags & TK_WM_COLORMAP_WINDOW) {
TkWmRemoveFromColormapWindows(winPtr);
}
if (winPtr->window != None) {
#if defined(MAC_TCL) || defined(MAC_OSX_TK) || defined(__WIN32__) || defined(__PM__)
XDestroyWindow(winPtr->display, winPtr->window);
#else
if ((winPtr->flags & TK_TOP_HIERARCHY)
|| !(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);
winPtr->inputContext = NULL;
}
#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);
LangDeadWindow(winPtr->mainPtr->interp, (Tk_Window) winPtr);
Tcl_DeleteHashEntry(Tcl_FindHashEntry(&winPtr->mainPtr->nameTable,
winPtr->pathName));
/*
* The memory pointed to by pathName has been deallocated.
* Keep users from accessing it after the window has been
* destroyed by setting it to NULL.
*/
winPtr->pathName = NULL;
/*
* Invalidate all objects referring to windows
* with the same main window
*/
winPtr->mainPtr->deletionEpoch++;
}
winPtr->mainPtr->refCount--;
if (winPtr->mainPtr->refCount == 0) {
/*
* 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 1 /* Do a Lang dependant cleanup */
Lang_DeadMainWindow(winPtr->mainPtr->interp, (Tk_Window) winPtr);
Tcl_UnlinkVar(winPtr->mainPtr->interp, "tk_strictMotif");
#else /* This is what Tcl does */
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");
}
#endif
Tcl_DeleteHashTable(&winPtr->mainPtr->nameTable);
TkBindFree(winPtr->mainPtr);
/* Do images before fonts - they may use fonts ... */
TkDeleteAllImages(winPtr->mainPtr);
TkFontPkgFree(winPtr->mainPtr);
TkFocusFree(winPtr->mainPtr);
TkStylePkgFree(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 !defined(WIN32) && !defined(MAC_TCL) && defined(NOT_YET)
if (dispPtr->refCount <= 0) {
/*
* 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. -- jyl
*/
/*
* Ideally this should be enabled, as unix Tk can use multiple
* displays. However, there are order issues still, as well
* as the handling of queued events and such that must be
* addressed before this can be enabled. The current cleanup
* works except for send event issues. -- hobbs 04/2002
*/
TkDisplay *theDispPtr, *backDispPtr;
/*
* Splice this display out of the list of displays.
*/
for (theDispPtr = tsdPtr->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) {
tsdPtr->displayList = theDispPtr->nextPtr;
} else {
backDispPtr->nextPtr = theDispPtr->nextPtr;
}
/*
* Calling XSync creates X server traffic, but addresses a
* focus issue on close (but not the send issue). -- hobbs
XSync(dispPtr->display, True);
*/
/*
* Found and spliced it out, now actually do the cleanup.
*/
TkCloseDisplay(dispPtr);
}
#endif
}
}
Tcl_EventuallyFree((ClientData) winPtr, TCL_DYNAMIC);
}
/*
*--------------------------------------------------------------
*
* 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. */
{
TkWindow *winPtr = (TkWindow *) tkwin;
XEvent event;
if (winPtr->flags & TK_MAPPED) {
return;
}
if (winPtr->window == None) {
Tk_MakeWindowExist(tkwin);
}
if (winPtr->flags & TK_WIN_MANAGED) {
/*
* 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;
Tk_ClassCreateProc *createProc;
int new;
if (winPtr->window != None) {
return;
}
if ((winPtr->parentPtr == NULL) || (winPtr->flags & TK_TOP_HIERARCHY)) {
parent = XRootWindow(winPtr->display, winPtr->screenNum);
} else {
if (winPtr->parentPtr->window == None) {
Tk_MakeWindowExist((Tk_Window) winPtr->parentPtr);
}
parent = winPtr->parentPtr->window;
}
createProc = Tk_GetClassProc(winPtr->classProcsPtr, createProc);
if (createProc != NULL) {
winPtr->window = (*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;
if (!(winPtr->flags & TK_TOP_HIERARCHY)) {
/*
* 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_HIERARCHY|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_WIN_MANAGED) {
/*
* 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_HIERARCHY)) {
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;
#if defined(MAC_TCL) || defined(MAC_OSX_TK)
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_WIN_MANAGED)) {
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. */
CONST char *className; /* New class for tkwin. */
{
register TkWindow *winPtr = (TkWindow *) tkwin;
winPtr->classUid = Tk_GetUid(className);
if (winPtr->flags & TK_WIN_MANAGED) {
TkWmSetClass(winPtr);
}
TkOptionClassChanged(winPtr);
}
/*
*----------------------------------------------------------------------
*
* Tk_SetClassProcs --
*
* 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
Tk_SetClassProcs(tkwin, procs, instanceData)
Tk_Window tkwin; /* Token for window to modify. */
Tk_ClassProcs *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. */
CONST 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.
*
*----------------------------------------------------------------------
*/
CONST 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_WIN_MANAGED) {
while ((otherPtr != NULL) && !(otherPtr->flags & TK_TOP_HIERARCHY)) {
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_HIERARCHY)) {
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_HIERARCHY|TK_REPARENTED))){
changes.sibling = otherPtr->window;
changes.stack_mode = Below;
mask = CWStackMode|CWSibling;
break;
}
}
XConfigureWindow(winPtr->display, winPtr->window, mask, &changes);
}
return TCL_OK;
}
/* TCL Specific stuff */
#if 0
/*
*----------------------------------------------------------------------
*
* Tk_Init --
*
* This procedure is typically invoked by Tcl_AppInit procedures
* to perform additional Tk initialization for a Tcl interpreter,
* such as sourcing the "tk.tcl" script.
*
* Results:
* Returns a standard Tcl completion code and sets interp->result
* if there is an error.
*
* Side effects:
* Depends on what's in the tk.tcl script.
*
*----------------------------------------------------------------------
*/
int
Tk_Init(interp)
Tcl_Interp *interp; /* Interpreter to initialize. */
{
static char initCmd[] =
"if [file exists $tk_library/tk.tcl] {\n\
source $tk_library/tk.tcl\n\
} else {\n\
set msg \"can't find $tk_library/tk.tcl; perhaps you \"\n\
append msg \"need to\\ninstall Tk or set your TK_LIBRARY \"\n\
append msg \"environment variable?\"\n\
error $msg\n\
}";
return Tcl_Eval(interp, initCmd);
}
/*
*----------------------------------------------------------------------
*
* 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;
if (interp == NULL) {
return NULL;
}
#ifdef USE_TCL_STUBS
if (tclStubsPtr == NULL) {
return NULL;
}
#endif
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;
}
#endif
/*
*----------------------------------------------------------------------
*
* 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;
}
/*
*----------------------------------------------------------------------
*
* 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;
#ifdef USE_TCL_STUBS
if (tclStubsPtr == NULL) {
return 0;
}
#endif
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 current thread. We really should
* be using a thread local exit handler to delete windows and a
* process exit handler to close the display but Tcl does
* not provide support for this usage.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
DeleteWindowsExitProc(clientData)
ClientData clientData; /* Not used. */
{
TkDisplay *dispPtr, *nextPtr;
Tcl_Interp *interp;
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
/*
* Finish destroying any windows that are in a
* half-dead state. We must protect the interpreter
* while destroying 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.
*/
while (tsdPtr->halfdeadWindowList != NULL) {
interp = tsdPtr->halfdeadWindowList->winPtr->mainPtr->interp;
Tcl_Preserve((ClientData) interp);
tsdPtr->halfdeadWindowList->flags |= HD_CLEANUP;
tsdPtr->halfdeadWindowList->winPtr->flags &= ~TK_ALREADY_DEAD;
Tk_DestroyWindow((Tk_Window) tsdPtr->halfdeadWindowList->winPtr);
Tcl_Release((ClientData) interp);
}
/*
* Destroy any remaining main windows.
*/
while (tsdPtr->mainWindowList != NULL) {
interp = tsdPtr->mainWindowList->interp;
Tcl_Preserve((ClientData) interp);
Tk_DestroyWindow((Tk_Window) tsdPtr->mainWindowList->winPtr);
Tcl_Release((ClientData) interp);
}
/*
* 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 (dispPtr = tsdPtr->displayList;
dispPtr != NULL;
dispPtr = 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; dispPtr != NULL;
dispPtr = nextPtr) {
nextPtr = dispPtr->nextPtr;
TkCloseDisplay(dispPtr);
}
}
tsdPtr->numMainWindows = 0;
tsdPtr->mainWindowList = NULL;
tsdPtr->initialized = 0;
}
#if 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. Similarly,
* the selection command is blocked.
* - 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. */
{
Var p;
int argc, code;
CONST char **argv;
char *args[20];
CONST char *argString = NULL;
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;
}
/*
* Ensure that our obj-types are registered with the Tcl runtime.
*/
TkRegisterObjTypes();
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_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.
*/
argString = 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.
*/
argString = Tcl_GetVar2(interp, "argv", (char *) NULL, TCL_GLOBAL_ONLY);
}
argv = NULL;
if (argString != NULL) {
char buffer[TCL_INTEGER_SPACE];
if (Tcl_SplitList(interp, argString, &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.
*/
argvt[0] = "toplevel";
argvt[1] = ".";
argvt[2] = "-class";
argvt[3] = Tcl_DStringValue(&class);
argc = 4;
if (display != NULL) {
argvt[argc] = "-screen";
argvt[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) {
argvt[argc] = "-colormap";
argvt[argc+1] = colormap;
argc += 2;
colormap = NULL;
}
if (use != NULL) {
argvt[argc] = "-use";
argvt[argc+1] = use;
argc += 2;
use = NULL;
}
if (visual != NULL) {
argvt[argc] = "-visual";
argvt[argc+1] = visual;
argc += 2;
visual = NULL;
}
argvt[argc] = NULL;
code = TkCreateFrame((ClientData) NULL, interp, argc, argvt, 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;
} else {
/*
* If we were able to provide ourselves as a package, then set
* the main loop procedure in Tcl to our main loop proc. This
* will cause tclsh to be event-aware when Tk is dynamically
* loaded. This will have no effect in wish, which already is
* prepared to run the event loop.
*/
Tcl_SetMainLoop(Tk_MainLoop);
}
#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;
}
#endif