4 * Xlib emulation routines for Windows related to creating, displaying
5 * and destroying windows.
7 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
9 * See the file "license.terms" for information on usage and redistribution of
10 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
17 int initialized; /* 0 means table below needs initializing. */
18 Tcl_HashTable windowTable; /* The windowTable maps from HWND to Tk_Window
21 static Tcl_ThreadDataKey dataKey;
24 * Forward declarations for functions defined in this file:
27 static void NotifyVisibility(XEvent *eventPtr, TkWindow *winPtr);
30 *----------------------------------------------------------------------
34 * This function binds an HWND and a reflection function to the specified
38 * Returns an X Window that encapsulates the HWND.
41 * May allocate a new X Window. Also enters the HWND into the global
44 *----------------------------------------------------------------------
53 Tcl_HashEntry *entryPtr;
54 TkWinDrawable *twdPtr = (TkWinDrawable *) Tk_WindowId(tkwin);
55 ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
56 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
58 if (!tsdPtr->initialized) {
59 Tcl_InitHashTable(&tsdPtr->windowTable, TCL_ONE_WORD_KEYS);
60 tsdPtr->initialized = 1;
64 * Allocate a new drawable if necessary. Otherwise, remove the previous
65 * HWND from from the window table.
69 twdPtr = ckalloc(sizeof(TkWinDrawable));
70 twdPtr->type = TWD_WINDOW;
71 twdPtr->window.winPtr = (TkWindow *) tkwin;
72 } else if (twdPtr->window.handle != NULL) {
73 entryPtr = Tcl_FindHashEntry(&tsdPtr->windowTable,
74 (char *)twdPtr->window.handle);
75 Tcl_DeleteHashEntry(entryPtr);
79 * Insert the new HWND into the window table.
82 twdPtr->window.handle = hwnd;
83 entryPtr = Tcl_CreateHashEntry(&tsdPtr->windowTable, (char *)hwnd, &new);
84 Tcl_SetHashValue(entryPtr, tkwin);
86 return (Window)twdPtr;
90 *----------------------------------------------------------------------
94 * This function retrieves a Tk_Window from the window table given an
98 * Returns the matching Tk_Window.
103 *----------------------------------------------------------------------
110 Tcl_HashEntry *entryPtr;
111 ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
112 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
114 if (!tsdPtr->initialized) {
115 Tcl_InitHashTable(&tsdPtr->windowTable, TCL_ONE_WORD_KEYS);
116 tsdPtr->initialized = 1;
118 entryPtr = Tcl_FindHashEntry(&tsdPtr->windowTable, (char *) hwnd);
119 if (entryPtr != NULL) {
120 return (Tk_Window) Tcl_GetHashValue(entryPtr);
126 *----------------------------------------------------------------------
130 * This function extracts the HWND from an X Window.
133 * Returns the HWND associated with the Window.
138 *----------------------------------------------------------------------
145 return ((TkWinDrawable *) window)->window.handle;
149 *----------------------------------------------------------------------
151 * TkpPrintWindowId --
153 * This routine stores the string representation of the platform
154 * dependent window handle for an X Window in the given buffer.
157 * Returns the result in the specified buffer.
162 *----------------------------------------------------------------------
167 char *buf, /* Pointer to string large enough to hold the
168 * hex representation of a pointer. */
169 Window window) /* Window to be printed into buffer. */
171 HWND hwnd = (window) ? Tk_GetHWND(window) : 0;
173 sprintf(buf, "0x%" TCL_Z_MODIFIER "x", (size_t)hwnd);
177 *----------------------------------------------------------------------
181 * Given a string which represents the platform dependent window handle,
182 * produce the X Window id for the window.
185 * The return value is normally TCL_OK; in this case *idPtr will be set
186 * to the X Window id equivalent to string. If string is improperly
187 * formed then TCL_ERROR is returned and an error message will be left in
188 * the interp's result. If the number does not correspond to a Tk Window,
189 * then *idPtr will be set to None.
194 *----------------------------------------------------------------------
199 Tcl_Interp *interp, /* Interpreter to use for error reporting. */
200 const char *string, /* String containing a (possibly signed)
201 * integer in a form acceptable to strtol. */
202 Window *idPtr) /* Place to store converted result. */
211 * We want sscanf for the 64-bit check, but if that doesn't work, then
212 * Tcl_GetInt manages the error correctly.
217 (sscanf(string, "0x%p", &win.hwnd) != 1) &&
219 Tcl_GetInt(interp, string, &win.number) != TCL_OK) {
223 tkwin = Tk_HWNDToWindow(win.hwnd);
225 *idPtr = Tk_WindowId(tkwin);
233 *----------------------------------------------------------------------
237 * Creates a Windows window object based on the current attributes of the
238 * specified TkWindow.
241 * Returns a pointer to a new TkWinDrawable cast to a Window.
244 * Creates a new window.
246 *----------------------------------------------------------------------
258 if (parent != None) {
259 parentWin = Tk_GetHWND(parent);
260 style = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
263 style = WS_POPUP | WS_CLIPCHILDREN;
267 * Create the window, then ensure that it is at the top of the stacking
271 hwnd = CreateWindowExW(WS_EX_NOPARENTNOTIFY, TK_WIN_CHILD_CLASS_NAME, NULL,
272 (DWORD) style, Tk_X(winPtr), Tk_Y(winPtr), Tk_Width(winPtr),
273 Tk_Height(winPtr), parentWin, NULL, Tk_GetHINSTANCE(), NULL);
274 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
275 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
276 return Tk_AttachHWND((Tk_Window)winPtr, hwnd);
280 *----------------------------------------------------------------------
284 * Destroys the given window.
290 * Sends the WM_DESTROY message to the window and then destroys it the
291 * Win32 resources associated with the window.
293 *----------------------------------------------------------------------
301 Tcl_HashEntry *entryPtr;
302 TkWinDrawable *twdPtr = (TkWinDrawable *)w;
303 TkWindow *winPtr = TkWinGetWinPtr(w);
304 HWND hwnd = Tk_GetHWND(w);
305 ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
306 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
311 * Remove references to the window in the pointer module then release the
315 TkPointerDeadWindow(winPtr);
317 entryPtr = Tcl_FindHashEntry(&tsdPtr->windowTable, (char*)hwnd);
318 if (entryPtr != NULL) {
319 Tcl_DeleteHashEntry(entryPtr);
325 * Don't bother destroying the window if we are going to destroy the
329 if (hwnd != NULL && !(winPtr->flags & TK_DONT_DESTROY_WINDOW)) {
336 *----------------------------------------------------------------------
340 * Cause the given window to become visible.
346 * Causes the window state to change, and generates a MapNotify event.
348 *----------------------------------------------------------------------
358 TkWindow *winPtr = TkWinGetWinPtr(w);
362 ShowWindow(Tk_GetHWND(w), SW_SHOWNORMAL);
363 winPtr->flags |= TK_MAPPED;
366 * Check to see if this window is visible now. If all of the parent
367 * windows up to the first toplevel are mapped, then this window and its
368 * mapped children have just become visible.
371 if (!(winPtr->flags & TK_TOP_HIERARCHY)) {
372 for (parentPtr = winPtr->parentPtr; ;
373 parentPtr = parentPtr->parentPtr) {
374 if ((parentPtr == NULL) || !(parentPtr->flags & TK_MAPPED)) {
377 if (parentPtr->flags & TK_TOP_HIERARCHY) {
382 event.type = MapNotify;
383 event.xmap.serial = display->request;
384 event.xmap.send_event = False;
385 event.xmap.display = display;
386 event.xmap.event = winPtr->window;
387 event.xmap.window = winPtr->window;
388 event.xmap.override_redirect = winPtr->atts.override_redirect;
389 Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
393 * Generate VisibilityNotify events for this window and its mapped
397 event.type = VisibilityNotify;
398 event.xvisibility.serial = display->request;
399 event.xvisibility.send_event = False;
400 event.xvisibility.display = display;
401 event.xvisibility.window = winPtr->window;
402 event.xvisibility.state = VisibilityUnobscured;
403 NotifyVisibility(&event, winPtr);
408 *----------------------------------------------------------------------
410 * NotifyVisibility --
412 * This function recursively notifies the mapped children of the
413 * specified window of a change in visibility. Note that we don't
414 * properly report the visibility state, since Windows does not provide
415 * that info. The eventPtr argument must point to an event that has been
416 * completely initialized except for the window slot.
422 * Generates lots of events.
424 *----------------------------------------------------------------------
429 XEvent *eventPtr, /* Initialized VisibilityNotify event. */
430 TkWindow *winPtr) /* Window to notify. */
432 if (winPtr->atts.event_mask & VisibilityChangeMask) {
433 eventPtr->xvisibility.window = winPtr->window;
434 Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_TAIL);
436 for (winPtr = winPtr->childList; winPtr != NULL;
437 winPtr = winPtr->nextPtr) {
438 if (winPtr->flags & TK_MAPPED) {
439 NotifyVisibility(eventPtr, winPtr);
445 *----------------------------------------------------------------------
449 * Cause the given window to become invisible.
455 * Causes the window state to change, and generates an UnmapNotify event.
457 *----------------------------------------------------------------------
466 TkWindow *winPtr = TkWinGetWinPtr(w);
471 * Bug fix: Don't short circuit this routine based on TK_MAPPED because it
472 * will be cleared before XUnmapWindow is called.
475 ShowWindow(Tk_GetHWND(w), SW_HIDE);
476 winPtr->flags &= ~TK_MAPPED;
478 if (winPtr->flags & TK_WIN_MANAGED) {
479 event.type = UnmapNotify;
480 event.xunmap.serial = display->request;
481 event.xunmap.send_event = False;
482 event.xunmap.display = display;
483 event.xunmap.event = winPtr->window;
484 event.xunmap.window = winPtr->window;
485 event.xunmap.from_configure = False;
486 Tk_HandleEvent(&event);
492 *----------------------------------------------------------------------
494 * XMoveResizeWindow --
496 * Move and resize a window relative to its parent.
502 * Repositions and resizes the specified window.
504 *----------------------------------------------------------------------
511 int x, int y, /* Position relative to parent. */
512 unsigned int width, unsigned int height)
515 MoveWindow(Tk_GetHWND(w), x, y, (int) width, (int) height, TRUE);
520 *----------------------------------------------------------------------
524 * Move a window relative to its parent.
530 * Repositions the specified window.
532 *----------------------------------------------------------------------
539 int x, int y) /* Position relative to parent */
541 TkWindow *winPtr = TkWinGetWinPtr(w);
545 MoveWindow(Tk_GetHWND(w), x, y, winPtr->changes.width,
546 winPtr->changes.height, TRUE);
551 *----------------------------------------------------------------------
561 * Resizes the specified window.
563 *----------------------------------------------------------------------
570 unsigned int width, unsigned int height)
572 TkWindow *winPtr = TkWinGetWinPtr(w);
576 MoveWindow(Tk_GetHWND(w), winPtr->changes.x, winPtr->changes.y, (int)width,
582 *----------------------------------------------------------------------
584 * XRaiseWindow, XLowerWindow --
586 * Change the stacking order of a window.
592 * Changes the stacking order of the specified window.
594 *----------------------------------------------------------------------
602 HWND window = Tk_GetHWND(w);
605 SetWindowPos(window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
614 HWND window = Tk_GetHWND(w);
617 SetWindowPos(window, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
622 *----------------------------------------------------------------------
624 * XConfigureWindow --
626 * Change the size, position, stacking, or border of the specified
633 * Changes the attributes of the specified window. Note that we ignore
634 * the passed in values and use the values stored in the TkWindow data
637 *----------------------------------------------------------------------
644 unsigned int valueMask,
645 XWindowChanges *values)
647 TkWindow *winPtr = TkWinGetWinPtr(w);
648 HWND hwnd = Tk_GetHWND(w);
653 * Change the shape and/or position of the window.
656 if (valueMask & (CWX|CWY|CWWidth|CWHeight)) {
657 MoveWindow(hwnd, winPtr->changes.x, winPtr->changes.y,
658 winPtr->changes.width, winPtr->changes.height, TRUE);
662 * Change the stacking order of the window.
665 if (valueMask & CWStackMode) {
668 if ((valueMask & CWSibling) && (values->sibling != None)) {
669 sibling = Tk_GetHWND(values->sibling);
673 TkWinSetWindowPos(hwnd, sibling, values->stack_mode);
679 *----------------------------------------------------------------------
683 * Clears the entire window to the current background color.
689 * Erases the current contents of the window.
691 *----------------------------------------------------------------------
701 HPALETTE oldPalette, palette;
703 HWND hwnd = Tk_GetHWND(w);
704 HDC dc = GetDC(hwnd);
706 palette = TkWinGetPalette(display->screens[0].cmap);
707 oldPalette = SelectPalette(dc, palette, FALSE);
711 winPtr = TkWinGetWinPtr(w);
712 brush = CreateSolidBrush(winPtr->atts.background_pixel);
713 GetWindowRect(hwnd, &rc);
714 rc.right = rc.right - rc.left;
715 rc.bottom = rc.bottom - rc.top;
716 rc.left = rc.top = 0;
717 FillRect(dc, &rc, brush);
720 SelectPalette(dc, oldPalette, TRUE);
726 *----------------------------------------------------------------------
728 * XChangeWindowAttributes --
730 * This function is called when the attributes on a window are updated.
731 * Since Tk maintains all of the window state, the only relevant value is
738 * May cause the mouse position to be updated.
740 *----------------------------------------------------------------------
744 XChangeWindowAttributes(
747 unsigned long valueMask,
748 XSetWindowAttributes* attributes)
750 if (valueMask & CWCursor) {
751 XDefineCursor(display, w, attributes->cursor);
757 *----------------------------------------------------------------------
761 * TODO: currently placeholder to satisfy Xlib stubs.
769 *----------------------------------------------------------------------
784 *----------------------------------------------------------------------
786 * TkWinSetWindowPos --
788 * Adjust the stacking order of a window relative to a second window (or
795 * Moves the specified window in the stacking order.
797 *----------------------------------------------------------------------
802 HWND hwnd, /* Window to restack. */
803 HWND siblingHwnd, /* Sibling window. */
804 int pos) /* One of Above or Below. */
809 * Since Windows does not support Above mode, we place the specified
810 * window below the sibling and then swap them.
815 SetWindowPos(hwnd, siblingHwnd, 0, 0, 0, 0,
816 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
822 siblingHwnd = (pos == Above) ? HWND_TOP : HWND_BOTTOM;
825 SetWindowPos(hwnd, siblingHwnd, 0, 0, 0, 0,
826 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
830 *----------------------------------------------------------------------
832 * TkpShowBusyWindow --
834 * Makes a busy window "appear".
840 * Arranges for the busy window to start intercepting events and the
841 * cursor to change to the configured "hey, I'm busy!" setting.
843 *----------------------------------------------------------------------
850 Busy *busyPtr = (Busy *) busy;
856 if (busyPtr->tkBusy != NULL) {
857 Tk_MapWindow(busyPtr->tkBusy);
858 window = Tk_WindowId(busyPtr->tkBusy);
859 display = Tk_Display(busyPtr->tkBusy);
860 hWnd = Tk_GetHWND(window);
862 SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
866 * Under Win32, cursors aren't associated with windows. Tk fakes this by
867 * watching Motion events on its windows. So Tk will automatically change
868 * the cursor when the pointer enters the Busy window. But Windows does
869 * not immediately change the cursor; it waits for the cursor position to
870 * change or a system call. We need to change the cursor before the
871 * application starts processing, so set the cursor position redundantly
872 * back to the current position.
875 GetCursorPos(&point);
876 TkSetCursorPos(point.x, point.y);
880 *----------------------------------------------------------------------
882 * TkpHideBusyWindow --
884 * Makes a busy window "disappear".
890 * Arranges for the busy window to stop intercepting events, and the
891 * cursor to change back to its normal setting.
893 *----------------------------------------------------------------------
900 Busy *busyPtr = (Busy *) busy;
903 if (busyPtr->tkBusy != NULL) {
904 Tk_UnmapWindow(busyPtr->tkBusy);
908 * Under Win32, cursors aren't associated with windows. Tk fakes this by
909 * watching Motion events on its windows. So Tk will automatically change
910 * the cursor when the pointer enters the Busy window. But Windows does
911 * not immediately change the cursor: it waits for the cursor position to
912 * change or a system call. We need to change the cursor before the
913 * application starts processing, so set the cursor position redundantly
914 * back to the current position.
917 GetCursorPos(&point);
918 TkSetCursorPos(point.x, point.y);
922 *----------------------------------------------------------------------
924 * TkpMakeTransparentWindowExist --
926 * Construct the platform-specific resources for a transparent window.
932 * Moves the specified window in the stacking order.
934 *----------------------------------------------------------------------
938 TkpMakeTransparentWindowExist(
939 Tk_Window tkwin, /* Token for window. */
940 Window parent) /* Parent window. */
942 TkWindow *winPtr = (TkWindow *) tkwin;
943 HWND hParent = (HWND) parent, hWnd;
944 int style = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
945 DWORD exStyle = WS_EX_TRANSPARENT | WS_EX_TOPMOST;
947 hWnd = CreateWindowExW(exStyle, TK_WIN_CHILD_CLASS_NAME, NULL, style,
948 Tk_X(tkwin), Tk_Y(tkwin), Tk_Width(tkwin), Tk_Height(tkwin),
949 hParent, NULL, Tk_GetHINSTANCE(), NULL);
950 winPtr->window = Tk_AttachHWND(tkwin, hWnd);
954 *----------------------------------------------------------------------
958 * Construct the platform-specific parts of a busy window. Note that this
959 * postpones the actual creation of the window resource until later.
965 * Sets up part of the busy window structure.
967 *----------------------------------------------------------------------
978 Busy *busyPtr = (Busy *) busy;
980 if (winPtr->flags & TK_REPARENTED) {
982 * This works around a bug in the implementation of menubars for
983 * non-Macintosh window systems (Win32 and X11). Tk doesn't reset the
984 * pointers to the parent window when the menu is reparented
985 * (winPtr->parentPtr points to the wrong window). We get around this
986 * by determining the parent via the native API calls.
989 HWND hWnd = GetParent(Tk_GetHWND(Tk_WindowId(tkRef)));
992 if (GetWindowRect(hWnd, &rect)) {
993 busyPtr->width = rect.right - rect.left;
994 busyPtr->height = rect.bottom - rect.top;
997 *parentPtr = Tk_WindowId(tkParent);
998 *parentPtr = (Window) Tk_GetHWND(*parentPtr);