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