/* * tkWinX.c -- * * This file contains Windows emulation procedures for X routines. * * Copyright (c) 1995-1996 Sun Microsystems, Inc. * Copyright (c) 1994 Software Research Associates, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * SCCS: @(#) tkWinX.c 1.28 96/07/23 16:58:44 */ #include "tkInt.h" #include "tkWinInt.h" /* * The following declaration is a special purpose backdoor into the * Tcl notifier. It is used to process events on the Tcl event queue, * without reentering the system event queue. */ extern void TclWinFlushEvents _ANSI_ARGS_((void)); /* * Declarations of static variables used in this file. */ static HINSTANCE appInstance = (HINSTANCE) NULL; /* Global application instance handle. */ static Display *winDisplay; /* Display that represents Windows screen. */ static Tcl_HashTable windowTable; /* Table of child windows indexed by handle. */ static char winScreenName[] = ":0"; /* Default name of windows display. */ static ATOM topLevelAtom, childAtom; /* Atoms for the classes registered by Tk. */ /* * Forward declarations of procedures used in this file. */ static void DeleteWindow _ANSI_ARGS_((HWND hwnd)); static void GetTranslatedKey _ANSI_ARGS_((XKeyEvent *xkey)); static void TranslateEvent _ANSI_ARGS_((HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)); /* *---------------------------------------------------------------------- * * TkGetServerInfo -- * * Given a window, this procedure returns information about * the window server for that window. This procedure provides * the guts of the "winfo server" command. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ void TkGetServerInfo(interp, tkwin) Tcl_Interp *interp; /* The server information is returned in * this interpreter's result. */ Tk_Window tkwin; /* Token for window; this selects a * particular display and server. */ { char buffer[50]; OSVERSIONINFO info; int index = 0; static char* os[] = {"Win32", "Win32s", "Win32c"}; info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&info); if (info.dwPlatformId == VER_PLATFORM_WIN32s) { index = 1; } else if (info.dwPlatformId == VER_PLATFORM_WIN32s) { index = 2; } sprintf(buffer, "Windows %d.%d %d ", info.dwMajorVersion, info.dwMinorVersion, info.dwBuildNumber); Tcl_AppendResult(interp, buffer, os[index], (char *) NULL); } /* *---------------------------------------------------------------------- * * TkWinGetTkModule -- * * This function returns the module handle for the Tk DLL. * * Results: * Returns the library module handle. * * Side effects: * None. * *---------------------------------------------------------------------- */ HMODULE TkWinGetTkModule() { #ifdef STk_CODE return GetModuleHandle("stk.exe"); #else char libName[13]; sprintf(libName, "tk%d%d.dll", TK_MAJOR_VERSION, TK_MINOR_VERSION); return GetModuleHandle(libName); #endif } /* *---------------------------------------------------------------------- * * TkWinGetAppInstance -- * * Retrieves the global application instance handle. * * Results: * Returns the global application instance handle. * * Side effects: * None. * *---------------------------------------------------------------------- */ HINSTANCE TkWinGetAppInstance() { return appInstance; } /* *---------------------------------------------------------------------- * * TkWinXInit -- * * Initialize Xlib emulation layer. * * Results: * None. * * Side effects: * Sets up various data structures. * *---------------------------------------------------------------------- */ void TkWinXInit(hInstance) HINSTANCE hInstance; { WNDCLASS class; static initialized = 0; if (initialized != 0) { return; } initialized = 1; appInstance = hInstance; class.style = CS_HREDRAW | CS_VREDRAW; class.cbClsExtra = 0; class.cbWndExtra = 0; class.hInstance = hInstance; class.hbrBackground = NULL; class.lpszMenuName = NULL; /* * Register the TopLevel window class. */ class.lpszClassName = TK_WIN_TOPLEVEL_CLASS_NAME; class.lpfnWndProc = TkWinTopLevelProc; class.hIcon = LoadIcon(TkWinGetTkModule(), "tk"); class.hCursor = LoadCursor(NULL, IDC_ARROW); topLevelAtom = RegisterClass(&class); if (topLevelAtom == 0) { panic("Unable to register TkTopLevel class"); } /* * Register the Child window class. */ class.lpszClassName = TK_WIN_CHILD_CLASS_NAME; class.lpfnWndProc = TkWinChildProc; class.hIcon = NULL; class.hCursor = NULL; childAtom = RegisterClass(&class); if (childAtom == 0) { UnregisterClass((LPCTSTR)topLevelAtom, hInstance); panic("Unable to register TkChild class"); } } /* *---------------------------------------------------------------------- * * TkGetDefaultScreenName -- * * Returns the name of the screen that Tk should use during * initialization. * * Results: * Returns a statically allocated string. * * Side effects: * None. * *---------------------------------------------------------------------- */ char * TkGetDefaultScreenName(interp, screenName) Tcl_Interp *interp; /* Not used. */ char *screenName; /* If NULL, use default string. */ { if ((screenName == NULL) || (screenName[0] == '\0')) { screenName = winScreenName; } return screenName; } /* *---------------------------------------------------------------------- * * XOpenDisplay -- * * Create the Display structure and fill it with device * specific information. * * Results: * Returns a Display structure on success or NULL on failure. * * Side effects: * Allocates a new Display structure. * *---------------------------------------------------------------------- */ Display * XOpenDisplay(display_name) _Xconst char *display_name; { Screen *screen; HDC dc; TkWinDrawable *twdPtr; TkWinPointerInit(); Tcl_InitHashTable(&windowTable, TCL_ONE_WORD_KEYS); if (winDisplay != NULL) { if (strcmp(winDisplay->display_name, display_name) == 0) { return winDisplay; } else { panic("XOpenDisplay: tried to open multiple displays"); return NULL; } } winDisplay = (Display *) ckalloc(sizeof(Display)); winDisplay->display_name = (char *) ckalloc(strlen(display_name)+1); strcpy(winDisplay->display_name, display_name); winDisplay->cursor_font = 1; winDisplay->nscreens = 1; winDisplay->request = 1; winDisplay->qlen = 0; screen = (Screen *) ckalloc(sizeof(Screen)); screen->display = winDisplay; dc = GetDC(NULL); screen->width = GetDeviceCaps(dc, HORZRES); screen->height = GetDeviceCaps(dc, VERTRES); screen->mwidth = GetDeviceCaps(dc, HORZSIZE); screen->mheight = GetDeviceCaps(dc, VERTSIZE); /* * Set up the root window. */ twdPtr = (TkWinDrawable*) ckalloc(sizeof(TkWinDrawable)); if (twdPtr == NULL) { return None; } twdPtr->type = TWD_WINDOW; twdPtr->window.winPtr = NULL; twdPtr->window.handle = NULL; screen->root = (Window)twdPtr; screen->root_depth = GetDeviceCaps(dc, BITSPIXEL); screen->root_visual = (Visual *) ckalloc(sizeof(Visual)); screen->root_visual->visualid = 0; if (GetDeviceCaps(dc, RASTERCAPS) & RC_PALETTE) { screen->root_visual->map_entries = GetDeviceCaps(dc, SIZEPALETTE); screen->root_visual->class = PseudoColor; screen->root_visual->red_mask = 0x0; screen->root_visual->green_mask = 0x0; screen->root_visual->blue_mask = 0x0; } else { if (screen->root_depth == 4) { screen->root_visual->class = StaticColor; screen->root_visual->map_entries = 16; } else if (screen->root_depth == 16) { screen->root_visual->class = TrueColor; screen->root_visual->map_entries = 64; screen->root_visual->red_mask = 0xf8; screen->root_visual->green_mask = 0xfc00; screen->root_visual->blue_mask = 0xf80000; } else if (screen->root_depth >= 24) { screen->root_visual->class = TrueColor; screen->root_visual->map_entries = 256; screen->root_visual->red_mask = 0xff; screen->root_visual->green_mask = 0xff00; screen->root_visual->blue_mask = 0xff0000; } } screen->root_visual->bits_per_rgb = screen->root_depth; ReleaseDC(NULL, dc); /* * Note that these pixel values are not palette relative. */ screen->white_pixel = RGB(255, 255, 255); screen->black_pixel = RGB(0, 0, 0); winDisplay->screens = screen; winDisplay->nscreens = 1; winDisplay->default_screen = 0; screen->cmap = XCreateColormap(winDisplay, None, screen->root_visual, AllocNone); return winDisplay; } /* *---------------------------------------------------------------------- * * XBell -- * * Generate a beep. * * Results: * None. * * Side effects: * Plays a sounds out the system speakers. * *---------------------------------------------------------------------- */ void XBell(display, percent) Display* display; int percent; { MessageBeep(MB_OK); } /* *---------------------------------------------------------------------- * * TkWinTopLevelProc -- * * Callback from Windows whenever an event occurs on a top level * window. * * Results: * Standard Windows return value. * * Side effects: * Default window behavior. * *---------------------------------------------------------------------- */ LRESULT CALLBACK TkWinTopLevelProc(hwnd, message, wParam, lParam) HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; { static inMoveSize = 0; if (inMoveSize) { TclWinFlushEvents(); } switch (message) { case WM_ENTERSIZEMOVE: inMoveSize = 1; break; case WM_EXITSIZEMOVE: inMoveSize = 0; break; case WM_CREATE: { CREATESTRUCT *info = (CREATESTRUCT *) lParam; TkWinDrawable *twdPtr = (TkWinDrawable *)info->lpCreateParams; Tcl_HashEntry *hPtr; int new; /* * Add the window and handle to the window table. */ twdPtr->window.handle = hwnd; hPtr = Tcl_CreateHashEntry(&windowTable, (char *)hwnd, &new); if (!new) { panic("Duplicate window handle: %p", hwnd); } Tcl_SetHashValue(hPtr, twdPtr); /* * Store the pointer to the drawable structure passed into * CreateWindow in the user data slot of the window. */ SetWindowLong(hwnd, GWL_USERDATA, (DWORD)twdPtr); return 0; } case WM_DESTROY: DeleteWindow(hwnd); return 0; case WM_GETMINMAXINFO: TkWinWmSetLimits(hwnd, (MINMAXINFO *) lParam); return 0; case WM_PALETTECHANGED: return TkWinWmInstallColormaps(hwnd, WM_PALETTECHANGED, hwnd == (HWND)wParam); case WM_QUERYNEWPALETTE: return TkWinWmInstallColormaps(hwnd, WM_QUERYNEWPALETTE, TRUE); case WM_WINDOWPOSCHANGED: { WINDOWPOS *pos = (WINDOWPOS *) lParam; TkWinDrawable *twdPtr = (TkWinDrawable *) GetWindowLong(hwnd, GWL_USERDATA); TkWinWmConfigure(TkWinGetWinPtr(twdPtr), pos); return 0; } case WM_CLOSE: case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: case WM_MOUSEMOVE: case WM_CHAR: case WM_SYSCHAR: case WM_KEYDOWN: case WM_KEYUP: case WM_SETFOCUS: case WM_KILLFOCUS: TranslateEvent(hwnd, message, wParam, lParam); return 0; case WM_SYSKEYDOWN: case WM_SYSKEYUP: case WM_DESTROYCLIPBOARD: TranslateEvent(hwnd, message, wParam, lParam); /* * We need to pass these messages to the default window * procedure in order to get the system menu to work. */ break; } return DefWindowProc(hwnd, message, wParam, lParam); } /* *---------------------------------------------------------------------- * * TkWinChildProc -- * * Callback from Windows whenever an event occurs on a child * window. * * Results: * Standard Windows return value. * * Side effects: * Default window behavior. * *---------------------------------------------------------------------- */ LRESULT CALLBACK TkWinChildProc(hwnd, message, wParam, lParam) HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; { switch (message) { case WM_CREATE: { CREATESTRUCT *info = (CREATESTRUCT *) lParam; Tcl_HashEntry *hPtr; int new; /* * Add the window and handle to the window table. */ hPtr = Tcl_CreateHashEntry(&windowTable, (char *)hwnd, &new); if (!new) { panic("Duplicate window handle: %p", hwnd); } Tcl_SetHashValue(hPtr, info->lpCreateParams); /* * Store the pointer to the drawable structure passed into * CreateWindow in the user data slot of the window. Then set * the Z stacking order so the window appears on top. */ SetWindowLong(hwnd, GWL_USERDATA, (DWORD)info->lpCreateParams); SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); return 0; } case WM_DESTROY: DeleteWindow(hwnd); return 0; case WM_ERASEBKGND: case WM_WINDOWPOSCHANGED: return 0; case WM_RENDERFORMAT: { TkWinDrawable *twdPtr; twdPtr = (TkWinDrawable *) GetWindowLong(hwnd, GWL_USERDATA); TkWinClipboardRender(TkWinGetWinPtr(twdPtr), wParam); return 0; } case WM_DESTROYCLIPBOARD: case WM_PAINT: case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: case WM_MOUSEMOVE: case WM_CHAR: case WM_SYSCHAR: case WM_SYSKEYDOWN: case WM_SYSKEYUP: case WM_KEYDOWN: case WM_KEYUP: case WM_SETFOCUS: case WM_KILLFOCUS: TranslateEvent(hwnd, message, wParam, lParam); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); } /* *---------------------------------------------------------------------- * * TranslateEvent -- * * This function is called by the window procedures to handle * the translation from Win32 events to Tk events. * * Results: * None. * * Side effects: * Queues a new Tk event. * *---------------------------------------------------------------------- */ static void TranslateEvent(hwnd, message, wParam, lParam) HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; { TkWindow *winPtr; XEvent event; TkWinDrawable *twdPtr; /* * Retrieve the window information, and reset the hwnd pointer in * case the original window was a toplevel decorative frame. */ twdPtr = (TkWinDrawable *) GetWindowLong(hwnd, GWL_USERDATA); if (twdPtr == NULL) { return; } winPtr = TkWinGetWinPtr(twdPtr); /* * TranslateEvent may get called even after Tk has deleted the window. * So we must check for a dead window before proceeding. */ if (winPtr == NULL || winPtr->window == None) { return; } hwnd = TkWinGetHWND(winPtr->window); event.xany.serial = winPtr->display->request++; event.xany.send_event = False; event.xany.display = winPtr->display; event.xany.window = (Window) winPtr->window; switch (message) { case WM_PAINT: { PAINTSTRUCT ps; event.type = Expose; BeginPaint(hwnd, &ps); event.xexpose.x = ps.rcPaint.left; event.xexpose.y = ps.rcPaint.top; event.xexpose.width = ps.rcPaint.right - ps.rcPaint.left; event.xexpose.height = ps.rcPaint.bottom - ps.rcPaint.top; EndPaint(hwnd, &ps); event.xexpose.count = 0; break; } case WM_CLOSE: event.type = ClientMessage; event.xclient.message_type = Tk_InternAtom((Tk_Window) winPtr, "WM_PROTOCOLS"); event.xclient.format = 32; event.xclient.data.l[0] = Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW"); break; case WM_SETFOCUS: event.type = FocusIn; event.xfocus.mode = NotifyNormal; event.xfocus.detail = NotifyAncestor; break; case WM_KILLFOCUS: event.type = FocusOut; event.xfocus.mode = NotifyNormal; event.xfocus.detail = NotifyAncestor; break; case WM_DESTROYCLIPBOARD: event.type = SelectionClear; event.xselectionclear.selection = Tk_InternAtom((Tk_Window)winPtr, "CLIPBOARD"); event.xselectionclear.time = GetCurrentTime(); break; case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: case WM_MOUSEMOVE: case WM_CHAR: case WM_SYSCHAR: case WM_SYSKEYDOWN: case WM_SYSKEYUP: case WM_KEYDOWN: case WM_KEYUP: { unsigned int state = TkWinGetModifierState(message, wParam, lParam); Time time = GetCurrentTime(); POINT clientPoint; POINTS rootPoint; /* Note: POINT and POINTS are different */ DWORD msgPos; /* * Compute the screen and window coordinates of the event. */ msgPos = GetMessagePos(); rootPoint = MAKEPOINTS(msgPos); clientPoint.x = rootPoint.x; clientPoint.y = rootPoint.y; ScreenToClient(hwnd, &clientPoint); /* * Set up the common event fields. */ event.xbutton.root = RootWindow(winPtr->display, winPtr->screenNum); event.xbutton.subwindow = None; event.xbutton.x = clientPoint.x; event.xbutton.y = clientPoint.y; event.xbutton.x_root = rootPoint.x; event.xbutton.y_root = rootPoint.y; event.xbutton.state = state; event.xbutton.time = time; event.xbutton.same_screen = True; /* * Now set up event specific fields. */ switch (message) { case WM_LBUTTONDOWN: event.type = ButtonPress; event.xbutton.button = Button1; break; case WM_MBUTTONDOWN: event.type = ButtonPress; event.xbutton.button = Button2; break; case WM_RBUTTONDOWN: event.type = ButtonPress; event.xbutton.button = Button3; break; case WM_LBUTTONUP: event.type = ButtonRelease; event.xbutton.button = Button1; break; case WM_MBUTTONUP: event.type = ButtonRelease; event.xbutton.button = Button2; break; case WM_RBUTTONUP: event.type = ButtonRelease; event.xbutton.button = Button3; break; case WM_MOUSEMOVE: event.type = MotionNotify; event.xmotion.is_hint = NotifyNormal; break; case WM_SYSKEYDOWN: case WM_KEYDOWN: /* * Check for translated characters in the event queue. */ event.type = KeyPress; event.xkey.keycode = wParam; GetTranslatedKey(&event.xkey); break; case WM_SYSKEYUP: case WM_KEYUP: /* * We don't check for translated characters on keyup * because Tk won't know what to do with them. Instead, we * wait for the WM_CHAR messages which will follow. */ event.type = KeyRelease; event.xkey.keycode = wParam; event.xkey.nchars = 0; break; case WM_CHAR: case WM_SYSCHAR: /* * Synthesize both a KeyPress and a KeyRelease. */ event.type = KeyPress; event.xkey.keycode = 0; event.xkey.nchars = 1; event.xkey.trans_chars[0] = (char) wParam; Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); event.type = KeyRelease; break; } if ((event.type == MotionNotify) || (event.type == ButtonPress) || (event.type == ButtonRelease)) { TkWinPointerEvent(&event, winPtr); return; } break; } default: return; } Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); } /* *---------------------------------------------------------------------- * * TkWinGetModifierState -- * * This function constructs a state mask for the mouse buttons * and modifier keys. * * Results: * Returns a composite value of all the modifier and button state * flags that were set at the time the event occurred. * * Side effects: * None. * *---------------------------------------------------------------------- */ unsigned int TkWinGetModifierState(message, wParam, lParam) UINT message; /* Win32 message type */ WPARAM wParam; /* wParam of message, used if key message */ LPARAM lParam; /* lParam of message, used if key message */ { unsigned int state = 0; /* accumulated state flags */ int isKeyEvent; /* 1 if message is a key press or release */ int prevState; /* 1 if key was previously down */ /* * If the event is a key press or release, we check for autorepeat. */ if (message == WM_SYSKEYDOWN || message == WM_KEYDOWN || message == WM_SYSKEYUP || message == WM_KEYUP) { isKeyEvent = TRUE; prevState = HIWORD(lParam) & KF_REPEAT; } /* * If the key being pressed or released is a modifier key, then * we use its previous state, otherwise we look at the current state. */ if (isKeyEvent && (wParam == VK_SHIFT)) { state |= prevState ? ShiftMask : 0; } else { state |= (GetKeyState(VK_SHIFT) & 0x8000) ? ShiftMask : 0; } if (isKeyEvent && (wParam == VK_CONTROL)) { state |= prevState ? ControlMask : 0; } else { state |= (GetKeyState(VK_CONTROL) & 0x8000) ? ControlMask : 0; } if (isKeyEvent && (wParam == VK_MENU)) { state |= prevState ? Mod2Mask : 0; } else { state |= (GetKeyState(VK_MENU) & 0x8000) ? Mod2Mask : 0; } /* * For toggle keys, we have to check both the previous key state * and the current toggle state. The result is the state of the * toggle before the event. */ if ((wParam == VK_CAPITAL) && (message == WM_SYSKEYDOWN || message == WM_KEYDOWN)) { state = (prevState ^ (GetKeyState(VK_CAPITAL) & 0x0001)) ? 0 : LockMask; } else { state |= (GetKeyState(VK_CAPITAL) & 0x0001) ? LockMask : 0; } if ((wParam == VK_NUMLOCK) && (message == WM_SYSKEYDOWN || message == WM_KEYDOWN)) { state = (prevState ^ (GetKeyState(VK_NUMLOCK) & 0x0001)) ? 0 : Mod1Mask; } else { state |= (GetKeyState(VK_NUMLOCK) & 0x0001) ? Mod1Mask : 0; } if ((wParam == VK_SCROLL) && (message == WM_SYSKEYDOWN || message == WM_KEYDOWN)) { state = (prevState ^ (GetKeyState(VK_SCROLL) & 0x0001)) ? 0 : Mod3Mask; } else { state |= (GetKeyState(VK_SCROLL) & 0x0001) ? Mod3Mask : 0; } /* * If a mouse button is being pressed or released, we use the previous * state of the button. */ if (message == WM_LBUTTONUP || (message != WM_LBUTTONDOWN && GetKeyState(VK_LBUTTON) & 0x8000)) { state |= Button1Mask; } if (message == WM_MBUTTONUP || (message != WM_MBUTTONDOWN && GetKeyState(VK_MBUTTON) & 0x8000)) { state |= Button2Mask; } if (message == WM_RBUTTONUP || (message != WM_RBUTTONDOWN && GetKeyState(VK_RBUTTON) & 0x8000)) { state |= Button3Mask; } return state; } /* *---------------------------------------------------------------------- * * GetTranslatedKey -- * * Retrieves WM_CHAR messages that are placed on the system queue * by the TranslateMessage system call and places them in the * given KeyPress event. * * Results: * Sets the trans_chars and nchars member of the key event. * * Side effects: * Removes any WM_CHAR messages waiting on the top of the system * event queue. * *---------------------------------------------------------------------- */ static void GetTranslatedKey(xkey) XKeyEvent *xkey; { MSG msg; xkey->nchars = 0; while (xkey->nchars < XMaxTransChars && PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { if ((msg.message == WM_CHAR) || (msg.message == WM_SYSCHAR)) { xkey->trans_chars[xkey->nchars] = (char) msg.wParam; xkey->nchars++; GetMessage(&msg, NULL, 0, 0); if ((msg.message == WM_CHAR) && (msg.lParam & 0x20000000)) { xkey->state = 0; } } else { break; } } } /* *---------------------------------------------------------------------- * * TkWinGetDrawableFromHandle -- * * Find the drawable associated with the given window handle. * * Results: * Returns a drawable pointer if the window is managed by Tk. * Otherwise it returns NULL. * * Side effects: * None. * *---------------------------------------------------------------------- */ TkWinDrawable * TkWinGetDrawableFromHandle(hwnd) HWND hwnd; /* Win32 window handle */ { Tcl_HashEntry *hPtr; hPtr = Tcl_FindHashEntry(&windowTable, (char *)hwnd); if (hPtr) { return (TkWinDrawable *)Tcl_GetHashValue(hPtr); } return NULL; } /* *---------------------------------------------------------------------- * * DeleteWindow -- * * Remove a window from the window table, and free the resources * associated with the drawable. * * Results: * None. * * Side effects: * Frees the resources associated with a window handle. * *---------------------------------------------------------------------- */ static void DeleteWindow(hwnd) HWND hwnd; { TkWinDrawable *twdPtr; Tcl_HashEntry *hPtr; /* * Remove the window from the window table. */ hPtr = Tcl_FindHashEntry(&windowTable, (char *)hwnd); if (hPtr) { Tcl_DeleteHashEntry(hPtr); } /* * Free the drawable associated with this window, unless the drawable * is still in use by a TkWindow. This only happens in the case of * a top level window, since the window gets destroyed when the * decorative frame is destroyed. */ twdPtr = (TkWinDrawable *) GetWindowLong(hwnd, GWL_USERDATA); if (twdPtr) { if (twdPtr->window.winPtr == NULL) { ckfree((char *) twdPtr); } else if (!(twdPtr->window.winPtr->flags & TK_TOP_LEVEL)) { panic("Non-toplevel window destroyed before its drawable"); } else { twdPtr->window.handle = NULL; } } } /* *---------------------------------------------------------------------- * * Tk_FreeXId -- * * This inteface is not needed under Windows. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ void Tk_FreeXId(display, xid) Display *display; XID xid; { }