1/* 2 * tkWinWindow.c -- 3 * 4 * Xlib emulation routines for Windows related to creating, 5 * displaying and destroying windows. 6 * 7 * Copyright (c) 1995-1997 Sun Microsystems, Inc. 8 * 9 * See the file "license.terms" for information on usage and redistribution 10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 11 * 12 * RCS: @(#) $Id: tkWinWindow.c,v 1.10 2002/06/14 22:25:12 jenglish Exp $ 13 */ 14 15#include "tkWinInt.h" 16 17typedef struct ThreadSpecificData { 18 int initialized; /* 0 means table below needs initializing. */ 19 Tcl_HashTable windowTable; /* The windowTable maps from HWND to 20 * Tk_Window handles. */ 21} ThreadSpecificData; 22static Tcl_ThreadDataKey dataKey; 23 24/* 25 * Forward declarations for procedures defined in this file: 26 */ 27 28static void NotifyVisibility _ANSI_ARGS_((XEvent *eventPtr, 29 TkWindow *winPtr)); 30 31/* 32 *---------------------------------------------------------------------- 33 * 34 * Tk_AttachHWND -- 35 * 36 * This function binds an HWND and a reflection procedure to 37 * the specified Tk_Window. 38 * 39 * Results: 40 * Returns an X Window that encapsulates the HWND. 41 * 42 * Side effects: 43 * May allocate a new X Window. Also enters the HWND into the 44 * global window table. 45 * 46 *---------------------------------------------------------------------- 47 */ 48 49Window 50Tk_AttachHWND(tkwin, hwnd) 51 Tk_Window tkwin; 52 HWND hwnd; 53{ 54 int new; 55 Tcl_HashEntry *entryPtr; 56 TkWinDrawable *twdPtr = (TkWinDrawable *) Tk_WindowId(tkwin); 57 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 58 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 59 60 if (!tsdPtr->initialized) { 61 Tcl_InitHashTable(&tsdPtr->windowTable, TCL_ONE_WORD_KEYS); 62 tsdPtr->initialized = 1; 63 } 64 65 /* 66 * Allocate a new drawable if necessary. Otherwise, remove the 67 * previous HWND from from the window table. 68 */ 69 70 if (twdPtr == NULL) { 71 twdPtr = (TkWinDrawable*) ckalloc(sizeof(TkWinDrawable)); 72 twdPtr->type = TWD_WINDOW; 73 twdPtr->window.winPtr = (TkWindow *) tkwin; 74 } else if (twdPtr->window.handle != NULL) { 75 entryPtr = Tcl_FindHashEntry(&tsdPtr->windowTable, 76 (char *)twdPtr->window.handle); 77 Tcl_DeleteHashEntry(entryPtr); 78 } 79 80 /* 81 * Insert the new HWND into the window table. 82 */ 83 84 twdPtr->window.handle = hwnd; 85 entryPtr = Tcl_CreateHashEntry(&tsdPtr->windowTable, (char *)hwnd, &new); 86 Tcl_SetHashValue(entryPtr, (ClientData)tkwin); 87 88 return (Window)twdPtr; 89} 90 91/* 92 *---------------------------------------------------------------------- 93 * 94 * Tk_HWNDToWindow -- 95 * 96 * This function retrieves a Tk_Window from the window table 97 * given an HWND. 98 * 99 * Results: 100 * Returns the matching Tk_Window. 101 * 102 * Side effects: 103 * None. 104 * 105 *---------------------------------------------------------------------- 106 */ 107 108Tk_Window 109Tk_HWNDToWindow(hwnd) 110 HWND hwnd; 111{ 112 Tcl_HashEntry *entryPtr; 113 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 114 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 115 116 if (!tsdPtr->initialized) { 117 Tcl_InitHashTable(&tsdPtr->windowTable, TCL_ONE_WORD_KEYS); 118 tsdPtr->initialized = 1; 119 } 120 entryPtr = Tcl_FindHashEntry(&tsdPtr->windowTable, (char*)hwnd); 121 if (entryPtr != NULL) { 122 return (Tk_Window) Tcl_GetHashValue(entryPtr); 123 } 124 return NULL; 125} 126 127/* 128 *---------------------------------------------------------------------- 129 * 130 * Tk_GetHWND -- 131 * 132 * This function extracts the HWND from an X Window. 133 * 134 * Results: 135 * Returns the HWND associated with the Window. 136 * 137 * Side effects: 138 * None. 139 * 140 *---------------------------------------------------------------------- 141 */ 142 143HWND 144Tk_GetHWND(window) 145 Window window; 146{ 147 return ((TkWinDrawable *) window)->window.handle; 148} 149 150/* 151 *---------------------------------------------------------------------- 152 * 153 * TkpPrintWindowId -- 154 * 155 * This routine stores the string representation of the 156 * platform dependent window handle for an X Window in the 157 * given buffer. 158 * 159 * Results: 160 * Returns the result in the specified buffer. 161 * 162 * Side effects: 163 * None. 164 * 165 *---------------------------------------------------------------------- 166 */ 167 168void 169TkpPrintWindowId(buf, window) 170 char *buf; /* Pointer to string large enough to hold 171 * the hex representation of a pointer. */ 172 Window window; /* Window to be printed into buffer. */ 173{ 174 HWND hwnd = (window) ? Tk_GetHWND(window) : 0; 175 /* 176 * Use pointer representation, because Win64 is P64 (*not* LP64). 177 * Windows doesn't print the 0x for %p, so we do it. 178 */ 179 sprintf(buf, "0x%p", hwnd); 180} 181 182/* 183 *---------------------------------------------------------------------- 184 * 185 * TkpScanWindowId -- 186 * 187 * Given a string which represents the platform dependent window 188 * handle, produce the X Window id for the window. 189 * 190 * Results: 191 * The return value is normally TCL_OK; in this case *idPtr 192 * will be set to the X Window id equivalent to string. If 193 * string is improperly formed then TCL_ERROR is returned and 194 * an error message will be left in the interp's result. If the 195 * number does not correspond to a Tk Window, then *idPtr will 196 * be set to None. 197 * 198 * Side effects: 199 * None. 200 * 201 *---------------------------------------------------------------------- 202 */ 203 204int 205TkpScanWindowId(interp, string, idPtr) 206 Tcl_Interp *interp; /* Interpreter to use for error reporting. */ 207 CONST char *string; /* String containing a (possibly signed) 208 * integer in a form acceptable to strtol. */ 209 Window *idPtr; /* Place to store converted result. */ 210{ 211 Tk_Window tkwin; 212 Window number; 213 214 /* 215 * We want sscanf for the 64-bit check, but if that doesn't work, 216 * then Tcl_GetInt manages the error correctly. 217 */ 218 if ( 219#ifdef _WIN64 220 (sscanf(string, "0x%p", &number) != 1) && 221#endif 222 Tcl_GetInt(interp, string, (int *)&number) != TCL_OK) { 223 return TCL_ERROR; 224 } 225 226 tkwin = Tk_HWNDToWindow((HWND)number); 227 if (tkwin) { 228 *idPtr = Tk_WindowId(tkwin); 229 } else { 230 *idPtr = None; 231 } 232 return TCL_OK; 233} 234 235/* 236 *---------------------------------------------------------------------- 237 * 238 * TkpMakeWindow -- 239 * 240 * Creates a Windows window object based on the current attributes 241 * of the specified TkWindow. 242 * 243 * Results: 244 * Returns a pointer to a new TkWinDrawable cast to a Window. 245 * 246 * Side effects: 247 * Creates a new window. 248 * 249 *---------------------------------------------------------------------- 250 */ 251 252Window 253TkpMakeWindow(winPtr, parent) 254 TkWindow *winPtr; 255 Window parent; 256{ 257 HWND parentWin; 258 int style; 259 HWND hwnd; 260 261 if (parent != None) { 262 parentWin = Tk_GetHWND(parent); 263 style = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; 264 } else { 265 parentWin = NULL; 266 style = WS_POPUP | WS_CLIPCHILDREN; 267 } 268 269 /* 270 * Create the window, then ensure that it is at the top of the 271 * stacking order. 272 */ 273 274 hwnd = CreateWindowEx(WS_EX_NOPARENTNOTIFY, TK_WIN_CHILD_CLASS_NAME, NULL, 275 style, Tk_X(winPtr), Tk_Y(winPtr), Tk_Width(winPtr), 276 Tk_Height(winPtr), parentWin, NULL, Tk_GetHINSTANCE(), NULL); 277 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, 278 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); 279 return Tk_AttachHWND((Tk_Window)winPtr, hwnd); 280} 281 282/* 283 *---------------------------------------------------------------------- 284 * 285 * XDestroyWindow -- 286 * 287 * Destroys the given window. 288 * 289 * Results: 290 * None. 291 * 292 * Side effects: 293 * Sends the WM_DESTROY message to the window and then destroys 294 * it the Win32 resources associated with the window. 295 * 296 *---------------------------------------------------------------------- 297 */ 298 299void 300XDestroyWindow(display, w) 301 Display* display; 302 Window w; 303{ 304 Tcl_HashEntry *entryPtr; 305 TkWinDrawable *twdPtr = (TkWinDrawable *)w; 306 TkWindow *winPtr = TkWinGetWinPtr(w); 307 HWND hwnd = Tk_GetHWND(w); 308 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 309 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 310 311 display->request++; 312 313 /* 314 * Remove references to the window in the pointer module then 315 * release the drawable. 316 */ 317 318 TkPointerDeadWindow(winPtr); 319 320 entryPtr = Tcl_FindHashEntry(&tsdPtr->windowTable, (char*)hwnd); 321 if (entryPtr != NULL) { 322 Tcl_DeleteHashEntry(entryPtr); 323 } 324 325 ckfree((char *)twdPtr); 326 327 /* 328 * Don't bother destroying the window if we are going to destroy 329 * the parent later. 330 */ 331 332 if (hwnd != NULL && !(winPtr->flags & TK_DONT_DESTROY_WINDOW)) { 333 DestroyWindow(hwnd); 334 } 335} 336 337/* 338 *---------------------------------------------------------------------- 339 * 340 * XMapWindow -- 341 * 342 * Cause the given window to become visible. 343 * 344 * Results: 345 * None 346 * 347 * Side effects: 348 * Causes the window state to change, and generates a MapNotify 349 * event. 350 * 351 *---------------------------------------------------------------------- 352 */ 353 354void 355XMapWindow(display, w) 356 Display* display; 357 Window w; 358{ 359 XEvent event; 360 TkWindow *parentPtr; 361 TkWindow *winPtr = TkWinGetWinPtr(w); 362 363 display->request++; 364 365 ShowWindow(Tk_GetHWND(w), SW_SHOWNORMAL); 366 winPtr->flags |= TK_MAPPED; 367 368 /* 369 * Check to see if this window is visible now. If all of the parent 370 * windows up to the first toplevel are mapped, then this window and 371 * its mapped children have just become visible. 372 */ 373 374 if (!(winPtr->flags & TK_TOP_HIERARCHY)) { 375 for (parentPtr = winPtr->parentPtr; ; 376 parentPtr = parentPtr->parentPtr) { 377 if ((parentPtr == NULL) || !(parentPtr->flags & TK_MAPPED)) { 378 return; 379 } 380 if (parentPtr->flags & TK_TOP_HIERARCHY) { 381 break; 382 } 383 } 384 } else { 385 event.type = MapNotify; 386 event.xmap.serial = display->request; 387 event.xmap.send_event = False; 388 event.xmap.display = display; 389 event.xmap.event = winPtr->window; 390 event.xmap.window = winPtr->window; 391 event.xmap.override_redirect = winPtr->atts.override_redirect; 392 Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); 393 } 394 395 /* 396 * Generate VisibilityNotify events for this window and its mapped 397 * children. 398 */ 399 400 event.type = VisibilityNotify; 401 event.xvisibility.serial = display->request; 402 event.xvisibility.send_event = False; 403 event.xvisibility.display = display; 404 event.xvisibility.window = winPtr->window; 405 event.xvisibility.state = VisibilityUnobscured; 406 NotifyVisibility(&event, winPtr); 407} 408 409/* 410 *---------------------------------------------------------------------- 411 * 412 * NotifyVisibility -- 413 * 414 * This function recursively notifies the mapped children of the 415 * specified window of a change in visibility. Note that we don't 416 * properly report the visibility state, since Windows does not 417 * provide that info. The eventPtr argument must point to an event 418 * that has been completely initialized except for the window slot. 419 * 420 * Results: 421 * None. 422 * 423 * Side effects: 424 * Generates lots of events. 425 * 426 *---------------------------------------------------------------------- 427 */ 428 429static void 430NotifyVisibility(eventPtr, winPtr) 431 XEvent *eventPtr; /* Initialized VisibilityNotify event. */ 432 TkWindow *winPtr; /* Window to notify. */ 433{ 434 if (winPtr->atts.event_mask & VisibilityChangeMask) { 435 eventPtr->xvisibility.window = winPtr->window; 436 Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_TAIL); 437 } 438 for (winPtr = winPtr->childList; winPtr != NULL; 439 winPtr = winPtr->nextPtr) { 440 if (winPtr->flags & TK_MAPPED) { 441 NotifyVisibility(eventPtr, winPtr); 442 } 443 } 444} 445 446/* 447 *---------------------------------------------------------------------- 448 * 449 * XUnmapWindow -- 450 * 451 * Cause the given window to become invisible. 452 * 453 * Results: 454 * None 455 * 456 * Side effects: 457 * Causes the window state to change, and generates an UnmapNotify 458 * event. 459 * 460 *---------------------------------------------------------------------- 461 */ 462 463void 464XUnmapWindow(display, w) 465 Display* display; 466 Window w; 467{ 468 XEvent event; 469 TkWindow *winPtr = TkWinGetWinPtr(w); 470 471 display->request++; 472 473 /* 474 * Bug fix: Don't short circuit this routine based on TK_MAPPED because 475 * it will be cleared before XUnmapWindow is called. 476 */ 477 478 ShowWindow(Tk_GetHWND(w), SW_HIDE); 479 winPtr->flags &= ~TK_MAPPED; 480 481 if (winPtr->flags & TK_WIN_MANAGED) { 482 event.type = UnmapNotify; 483 event.xunmap.serial = display->request; 484 event.xunmap.send_event = False; 485 event.xunmap.display = display; 486 event.xunmap.event = winPtr->window; 487 event.xunmap.window = winPtr->window; 488 event.xunmap.from_configure = False; 489 Tk_HandleEvent(&event); 490 } 491} 492 493/* 494 *---------------------------------------------------------------------- 495 * 496 * XMoveResizeWindow -- 497 * 498 * Move and resize a window relative to its parent. 499 * 500 * Results: 501 * None. 502 * 503 * Side effects: 504 * Repositions and resizes the specified window. 505 * 506 *---------------------------------------------------------------------- 507 */ 508 509void 510XMoveResizeWindow(display, w, x, y, width, height) 511 Display* display; 512 Window w; 513 int x; /* Position relative to parent. */ 514 int y; 515 unsigned int width; 516 unsigned int height; 517{ 518 display->request++; 519 MoveWindow(Tk_GetHWND(w), x, y, width, height, TRUE); 520} 521 522/* 523 *---------------------------------------------------------------------- 524 * 525 * XMoveWindow -- 526 * 527 * Move a window relative to its parent. 528 * 529 * Results: 530 * None. 531 * 532 * Side effects: 533 * Repositions the specified window. 534 * 535 *---------------------------------------------------------------------- 536 */ 537 538void 539XMoveWindow(display, w, x, y) 540 Display* display; 541 Window w; 542 int x; 543 int y; 544{ 545 TkWindow *winPtr = TkWinGetWinPtr(w); 546 547 display->request++; 548 549 MoveWindow(Tk_GetHWND(w), x, y, winPtr->changes.width, 550 winPtr->changes.height, TRUE); 551} 552 553/* 554 *---------------------------------------------------------------------- 555 * 556 * XResizeWindow -- 557 * 558 * Resize a window. 559 * 560 * Results: 561 * None. 562 * 563 * Side effects: 564 * Resizes the specified window. 565 * 566 *---------------------------------------------------------------------- 567 */ 568 569void 570XResizeWindow(display, w, width, height) 571 Display* display; 572 Window w; 573 unsigned int width; 574 unsigned int height; 575{ 576 TkWindow *winPtr = TkWinGetWinPtr(w); 577 578 display->request++; 579 580 MoveWindow(Tk_GetHWND(w), winPtr->changes.x, winPtr->changes.y, width, 581 height, TRUE); 582} 583 584/* 585 *---------------------------------------------------------------------- 586 * 587 * XRaiseWindow -- 588 * 589 * Change the stacking order of a window. 590 * 591 * Results: 592 * None. 593 * 594 * Side effects: 595 * Changes the stacking order of the specified window. 596 * 597 *---------------------------------------------------------------------- 598 */ 599 600void 601XRaiseWindow(display, w) 602 Display* display; 603 Window w; 604{ 605 HWND window = Tk_GetHWND(w); 606 607 display->request++; 608 SetWindowPos(window, HWND_TOPMOST, 0, 0, 0, 0, 609 SWP_NOMOVE | SWP_NOSIZE); 610} 611 612/* 613 *---------------------------------------------------------------------- 614 * 615 * XConfigureWindow -- 616 * 617 * Change the size, position, stacking, or border of the specified 618 * window. 619 * 620 * Results: 621 * None. 622 * 623 * Side effects: 624 * Changes the attributes of the specified window. Note that we 625 * ignore the passed in values and use the values stored in the 626 * TkWindow data structure. 627 * 628 *---------------------------------------------------------------------- 629 */ 630 631void 632XConfigureWindow(display, w, value_mask, values) 633 Display* display; 634 Window w; 635 unsigned int value_mask; 636 XWindowChanges* values; 637{ 638 TkWindow *winPtr = TkWinGetWinPtr(w); 639 HWND hwnd = Tk_GetHWND(w); 640 641 display->request++; 642 643 /* 644 * Change the shape and/or position of the window. 645 */ 646 647 if (value_mask & (CWX|CWY|CWWidth|CWHeight)) { 648 MoveWindow(hwnd, winPtr->changes.x, winPtr->changes.y, 649 winPtr->changes.width, winPtr->changes.height, TRUE); 650 } 651 652 /* 653 * Change the stacking order of the window. 654 */ 655 656 if (value_mask & CWStackMode) { 657 HWND sibling; 658 if ((value_mask & CWSibling) && (values->sibling != None)) { 659 sibling = Tk_GetHWND(values->sibling); 660 } else { 661 sibling = NULL; 662 } 663 TkWinSetWindowPos(hwnd, sibling, values->stack_mode); 664 } 665} 666 667/* 668 *---------------------------------------------------------------------- 669 * 670 * XClearWindow -- 671 * 672 * Clears the entire window to the current background color. 673 * 674 * Results: 675 * None. 676 * 677 * Side effects: 678 * Erases the current contents of the window. 679 * 680 *---------------------------------------------------------------------- 681 */ 682 683void 684XClearWindow(display, w) 685 Display* display; 686 Window w; 687{ 688 RECT rc; 689 HBRUSH brush; 690 HPALETTE oldPalette, palette; 691 TkWindow *winPtr; 692 HWND hwnd = Tk_GetHWND(w); 693 HDC dc = GetDC(hwnd); 694 695 palette = TkWinGetPalette(display->screens[0].cmap); 696 oldPalette = SelectPalette(dc, palette, FALSE); 697 698 display->request++; 699 700 winPtr = TkWinGetWinPtr(w); 701 brush = CreateSolidBrush(winPtr->atts.background_pixel); 702 GetWindowRect(hwnd, &rc); 703 rc.right = rc.right - rc.left; 704 rc.bottom = rc.bottom - rc.top; 705 rc.left = rc.top = 0; 706 FillRect(dc, &rc, brush); 707 708 DeleteObject(brush); 709 SelectPalette(dc, oldPalette, TRUE); 710 ReleaseDC(hwnd, dc); 711} 712 713/* 714 *---------------------------------------------------------------------- 715 * 716 * XChangeWindowAttributes -- 717 * 718 * This function is called when the attributes on a window are 719 * updated. Since Tk maintains all of the window state, the only 720 * relevant value is the cursor. 721 * 722 * Results: 723 * None. 724 * 725 * Side effects: 726 * May cause the mouse position to be updated. 727 * 728 *---------------------------------------------------------------------- 729 */ 730 731void 732XChangeWindowAttributes(display, w, valueMask, attributes) 733 Display* display; 734 Window w; 735 unsigned long valueMask; 736 XSetWindowAttributes* attributes; 737{ 738 if (valueMask & CWCursor) { 739 XDefineCursor(display, w, attributes->cursor); 740 } 741} 742 743/* 744 *---------------------------------------------------------------------- 745 * 746 * TkWinSetWindowPos -- 747 * 748 * Adjust the stacking order of a window relative to a second 749 * window (or NULL). 750 * 751 * Results: 752 * None. 753 * 754 * Side effects: 755 * Moves the specified window in the stacking order. 756 * 757 *---------------------------------------------------------------------- 758 */ 759 760void 761TkWinSetWindowPos(hwnd, siblingHwnd, pos) 762 HWND hwnd; /* Window to restack. */ 763 HWND siblingHwnd; /* Sibling window. */ 764 int pos; /* One of Above or Below. */ 765{ 766 HWND temp; 767 768 /* 769 * Since Windows does not support Above mode, we place the 770 * specified window below the sibling and then swap them. 771 */ 772 773 if (siblingHwnd) { 774 if (pos == Above) { 775 SetWindowPos(hwnd, siblingHwnd, 0, 0, 0, 0, 776 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); 777 temp = hwnd; 778 hwnd = siblingHwnd; 779 siblingHwnd = temp; 780 } 781 } else { 782 siblingHwnd = (pos == Above) ? HWND_TOP : HWND_BOTTOM; 783 } 784 785 SetWindowPos(hwnd, siblingHwnd, 0, 0, 0, 0, 786 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); 787} 788 789/* 790 *---------------------------------------------------------------------- 791 * 792 * TkpWindowWasRecentlyDeleted -- 793 * 794 * Determines whether we know if the window given as argument was 795 * recently deleted. Called by the generic code error handler to 796 * handle BadWindow events. 797 * 798 * Results: 799 * Always 0. We do not keep this information on Windows. 800 * 801 * Side effects: 802 * None. 803 * 804 *---------------------------------------------------------------------- 805 */ 806 807int 808TkpWindowWasRecentlyDeleted(win, dispPtr) 809 Window win; 810 TkDisplay *dispPtr; 811{ 812 return 0; 813} 814