1/* 2 * tkMacOSXWindowEvent.c -- 3 * 4 * This file defines the routines for both creating and handling 5 * Window Manager class events for Tk. 6 * 7 * Copyright 2001, Apple Computer, Inc. 8 * Copyright (c) 2005-2007 Daniel A. Steffen <das@users.sourceforge.net> 9 * 10 * See the file "license.terms" for information on usage and redistribution of 11 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 12 * 13 * The following terms apply to all files originating from Apple 14 * Computer, Inc. ("Apple") and associated with the software 15 * unless explicitly disclaimed in individual files. 16 * 17 * 18 * Apple hereby grants permission to use, copy, modify, 19 * distribute, and license this software and its documentation 20 * for any purpose, provided that existing copyright notices are 21 * retained in all copies and that this notice is included 22 * verbatim in any distributions. No written agreement, license, 23 * or royalty fee is required for any of the authorized 24 * uses. Modifications to this software may be copyrighted by 25 * their authors and need not follow the licensing terms 26 * described here, provided that the new terms are clearly 27 * indicated on the first page of each file where they apply. 28 * 29 * 30 * IN NO EVENT SHALL APPLE, THE AUTHORS OR DISTRIBUTORS OF THE 31 * SOFTWARE BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, 32 * INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF 33 * THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, 34 * EVEN IF APPLE OR THE AUTHORS HAVE BEEN ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. APPLE, THE AUTHORS AND 36 * DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, 37 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, 38 * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS 39 * SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND APPLE,THE 40 * AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE 41 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 42 * 43 * GOVERNMENT USE: If you are acquiring this software on behalf 44 * of the U.S. government, the Government shall have only 45 * "Restricted Rights" in the software and related documentation 46 * as defined in the Federal Acquisition Regulations (FARs) in 47 * Clause 52.227.19 (c) (2). If you are acquiring the software 48 * on behalf of the Department of Defense, the software shall be 49 * classified as "Commercial Computer Software" and the 50 * Government shall have only "Restricted Rights" as defined in 51 * Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the 52 * foregoing, the authors grant the U.S. Government and others 53 * acting in its behalf permission to use and distribute the 54 * software in accordance with the terms specified in this 55 * license. 56 * 57 * RCS: @(#) $Id: tkMacOSXWindowEvent.c,v 1.3.2.26 2007/11/09 06:26:56 das Exp $ 58 */ 59 60#include "tkMacOSXPrivate.h" 61#include "tkMacOSXWm.h" 62#include "tkMacOSXEvent.h" 63#include "tkMacOSXDebug.h" 64 65/* 66#ifdef TK_MAC_DEBUG 67#define TK_MAC_DEBUG_CLIP_REGIONS 68#endif 69*/ 70 71/* 72 * Declaration of functions used only in this file 73 */ 74 75static int GenerateUpdateEvent(Window window); 76static int GenerateUpdates(HIMutableShapeRef updateRgn, CGRect *updateBounds, 77 TkWindow *winPtr); 78static int GenerateActivateEvents(Window window, int activeFlag); 79static void ClearPort(CGrafPtr port, HIShapeRef updateRgn); 80 81 82/* 83 *---------------------------------------------------------------------- 84 * 85 * TkMacOSXProcessApplicationEvent -- 86 * 87 * This processes Application level events, mainly activate 88 * and deactivate. 89 * 90 * Results: 91 * 0. 92 * 93 * Side effects: 94 * Hide or reveal floating windows. 95 * 96 *---------------------------------------------------------------------- 97 */ 98 99MODULE_SCOPE int 100TkMacOSXProcessApplicationEvent( 101 TkMacOSXEvent *eventPtr, 102 MacEventStatus *statusPtr) 103{ 104 Tcl_CmdInfo dummy; 105 106 /* 107 * This is a bit of a hack. We get "show" events both when we come back 108 * from being hidden, and whenever we are activated. I only want to run 109 * the "show" proc when we have been hidden already, not as a substitute 110 * for <Activate>. So I use this toggle... 111 */ 112 static int toggleHide = 0; 113 114 switch (eventPtr->eKind) { 115 case kEventAppActivated: 116 ShowFloatingWindows(); 117 break; 118 case kEventAppDeactivated: 119 TkSuspendClipboard(); 120 HideFloatingWindows(); 121 break; 122 case kEventAppQuit: 123 statusPtr->stopProcessing = 1; 124 break; 125 case kEventAppHidden: 126 if (toggleHide == 0) { 127 toggleHide = 1; 128 if (eventPtr->interp && Tcl_GetCommandInfo(eventPtr->interp, 129 "::tk::mac::OnHide", &dummy)) { 130 Tcl_GlobalEval(eventPtr->interp, "::tk::mac::OnHide"); 131 } 132 } 133 statusPtr->stopProcessing = 1; 134 break; 135 case kEventAppShown: 136 if (toggleHide == 1) { 137 toggleHide = 0; 138 if (eventPtr->interp && Tcl_GetCommandInfo(eventPtr->interp, 139 "::tk::mac::OnShow", &dummy)) { 140 Tcl_GlobalEval(eventPtr->interp, "::tk::mac::OnShow"); 141 } 142 } 143 statusPtr->stopProcessing = 1; 144 break; 145 case kEventAppAvailableWindowBoundsChanged: { 146 static UInt32 prevId = 0; 147 UInt32 id; 148 OSStatus err; 149 150 err = ChkErr(GetEventParameter, eventPtr->eventRef, 151 kEventParamTransactionID, typeUInt32, 152 NULL, sizeof(id), NULL, &id); 153 if (err != noErr || id != prevId) { 154 TkDisplay *dispPtr = TkGetDisplayList(); 155 156 prevId = id; 157 TkMacOSXDisplayChanged(dispPtr->display); 158 } 159 /* 160 * Should we call ::tk::mac::OnDisplayChanged? 161 */ 162 break; 163 } 164 default: 165 break; 166 } 167 return 0; 168} 169 170/* 171 *---------------------------------------------------------------------- 172 * 173 * TkMacOSXProcessAppearanceEvent -- 174 * 175 * This processes Appearance events. 176 * 177 * Results: 178 * 0. 179 * 180 * Side effects: 181 * None. 182 * 183 *---------------------------------------------------------------------- 184 */ 185 186MODULE_SCOPE int 187TkMacOSXProcessAppearanceEvent( 188 TkMacOSXEvent *eventPtr, 189 MacEventStatus *statusPtr) 190{ 191 switch (eventPtr->eKind) { 192 case kEventAppearanceScrollBarVariantChanged: 193 TkMacOSXInitScrollbarMetrics(); 194 break; 195 default: 196 break; 197 } 198 return 0; 199} 200 201/* 202 *---------------------------------------------------------------------- 203 * 204 * TkMacOSXProcessWindowEvent -- 205 * 206 * This processes Window level events, mainly activate 207 * and deactivate. 208 * 209 * Results: 210 * 0. 211 * 212 * Side effects: 213 * Cause Windows to be moved forward or backward in the 214 * window stack. 215 * 216 *---------------------------------------------------------------------- 217 */ 218 219MODULE_SCOPE int 220TkMacOSXProcessWindowEvent( 221 TkMacOSXEvent * eventPtr, 222 MacEventStatus * statusPtr) 223{ 224 OSStatus err; 225 WindowRef whichWindow; 226 Window window; 227 int eventFound = false; 228 TkDisplay *dispPtr; 229 TkWindow *winPtr; 230 231 switch (eventPtr->eKind) { 232 case kEventWindowActivated: 233 case kEventWindowDeactivated: 234 case kEventWindowUpdate: 235 case kEventWindowExpanding: 236 case kEventWindowBoundsChanged: 237 case kEventWindowDragStarted: 238 case kEventWindowDragCompleted: 239 case kEventWindowConstrain: 240 case kEventWindowGetRegion: 241 case kEventWindowDrawContent: 242 break; 243 default: 244 return 0; 245 break; 246 } 247 err = ChkErr(GetEventParameter, eventPtr->eventRef, 248 kEventParamDirectObject, typeWindowRef, NULL, sizeof(whichWindow), 249 NULL, &whichWindow); 250 if (err != noErr) { 251 return 0; 252 } 253 254 window = TkMacOSXGetXWindow(whichWindow); 255 dispPtr = TkGetDisplayList(); 256 winPtr = (TkWindow *)Tk_IdToWindow(dispPtr->display, window); 257 258 switch (eventPtr->eKind) { 259 case kEventWindowActivated: 260 case kEventWindowDeactivated: 261 if (window != None) { 262 int activate = (eventPtr->eKind == kEventWindowActivated); 263 264 eventFound |= GenerateActivateEvents(window, activate); 265 eventFound |= TkMacOSXGenerateFocusEvent(window, activate); 266 if (winPtr) { 267 TkMacOSXEnterExitFullscreen(winPtr, activate); 268 } 269 statusPtr->stopProcessing = 1; 270 } 271 break; 272 case kEventWindowUpdate: 273 if (window != None && GenerateUpdateEvent(window)) { 274 eventFound = true; 275 statusPtr->stopProcessing = 1; 276 } 277 break; 278 case kEventWindowExpanding: 279 if (winPtr) { 280 winPtr->wmInfoPtr->hints.initial_state = 281 TkMacOSXIsWindowZoomed(winPtr) ? ZoomState : 282 NormalState; 283 Tk_MapWindow((Tk_Window) winPtr); 284 /* 285 * Need to process all Tk events generated by Tk_MapWindow() 286 * before returning to ensure all children are mapped, as 287 * otherwise the Activate event that follows Expanding would 288 * not be processed by any unmapped children. 289 */ 290 while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {}; 291 while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {}; 292 } 293 break; 294 case kEventWindowBoundsChanged: 295 if (winPtr) { 296 WmInfo *wmPtr = winPtr->wmInfoPtr; 297 UInt32 attr; 298 Rect bounds; 299 int x = -1, y = -1, width = -1, height = -1, flags = 0; 300 301 ChkErr(GetEventParameter, eventPtr->eventRef, 302 kEventParamAttributes, typeUInt32, 303 NULL, sizeof(attr), NULL, &attr); 304 ChkErr(GetEventParameter, eventPtr->eventRef, 305 kEventParamCurrentBounds, typeQDRectangle, 306 NULL, sizeof(bounds), NULL, &bounds); 307 if (attr & kWindowBoundsChangeOriginChanged) { 308 x = bounds.left - wmPtr->xInParent; 309 y = bounds.top - wmPtr->yInParent; 310 flags |= TK_LOCATION_CHANGED; 311 } 312 if (attr & kWindowBoundsChangeSizeChanged) { 313 width = bounds.right - bounds.left; 314 height = bounds.bottom - bounds.top; 315 flags |= TK_SIZE_CHANGED; 316 } 317 TkMacOSXInvalClipRgns((Tk_Window) winPtr); 318 TkMacOSXInvalidateWindow((MacDrawable *) window, 319 TK_PARENT_WINDOW); 320 TkGenWMConfigureEvent((Tk_Window)winPtr, x, y, width, 321 height, flags); 322 if (attr & kWindowBoundsChangeUserResize || 323 attr & kWindowBoundsChangeUserDrag) { 324 TkMacOSXRunTclEventLoop(); 325 } 326 if (wmPtr->attributes & kWindowResizableAttribute) { 327 HIViewRef growBoxView; 328 329 err = HIViewFindByID(HIViewGetRoot(whichWindow), 330 kHIViewWindowGrowBoxID, &growBoxView); 331 if (err == noErr) { 332 ChkErr(HIViewSetNeedsDisplay, growBoxView, true); 333 } 334 } 335 } 336 break; 337 case kEventWindowDragStarted: 338 if (!(TkMacOSXModifierState() & cmdKey)) { 339 TkMacOSXBringWindowForward(whichWindow); 340 } 341 TkMacOSXTrackingLoop(1); 342 break; 343 case kEventWindowDragCompleted: { 344 Rect maxBounds, bounds, strWidths; 345 int h = 0, v = 0; 346 347 TkMacOSXTrackingLoop(0); 348 ChkErr(GetWindowGreatestAreaDevice, whichWindow, 349 kWindowDragRgn, NULL, &maxBounds); 350 ChkErr(GetWindowBounds, whichWindow, kWindowStructureRgn, 351 &bounds); 352 ChkErr(GetWindowStructureWidths, whichWindow, &strWidths); 353 if (bounds.left > maxBounds.right - strWidths.left) { 354 h = maxBounds.right 355 - (strWidths.left ? strWidths.left : 40) 356 - bounds.left; 357 } else if (bounds.right < maxBounds.left 358 + strWidths.right) { 359 h = maxBounds.left 360 + (strWidths.right ? strWidths.right : 40) 361 - bounds.right; 362 } 363 if (bounds.top > maxBounds.bottom - strWidths.top) { 364 v = maxBounds.bottom 365 - (strWidths.top ? strWidths.top : 40) 366 - bounds.top; 367 } else if (bounds.bottom < maxBounds.top 368 + strWidths.bottom) { 369 v = maxBounds.top 370 + (strWidths.bottom ? strWidths.bottom : 40) 371 - bounds.bottom; 372 } else if (strWidths.top && bounds.top < maxBounds.top) { 373 v = maxBounds.top - bounds.top; 374 } 375 if (h || v) { 376 OffsetRect(&bounds, h, v); 377 ChkErr(SetWindowBounds, whichWindow, 378 kWindowStructureRgn, &bounds); 379 } 380 break; 381 } 382 case kEventWindowConstrain: 383 if (winPtr && (winPtr->wmInfoPtr->flags & WM_FULLSCREEN) && 384 TkMacOSXMakeFullscreen(winPtr, whichWindow, 1, 385 NULL) == TCL_OK) { 386 statusPtr->stopProcessing = 1; 387 } 388 break; 389 case kEventWindowGetRegion: 390 if (winPtr && (winPtr->wmInfoPtr->flags & WM_TRANSPARENT)) { 391 WindowRegionCode code; 392 393 statusPtr->stopProcessing = (CallNextEventHandler( 394 eventPtr->callRef, eventPtr->eventRef) == noErr); 395 err = ChkErr(GetEventParameter, eventPtr->eventRef, 396 kEventParamWindowRegionCode, typeWindowRegionCode, 397 NULL, sizeof(code), NULL, &code); 398 if (err == noErr && code == kWindowOpaqueRgn) { 399 RgnHandle rgn; 400 401 err = ChkErr(GetEventParameter, eventPtr->eventRef, 402 kEventParamRgnHandle, typeQDRgnHandle, NULL, 403 sizeof(rgn), NULL, &rgn); 404 if (err == noErr) { 405 SetEmptyRgn(rgn); 406 statusPtr->stopProcessing = 1; 407 } 408 } 409 } 410 break; 411 case kEventWindowDrawContent: 412 if (winPtr && (winPtr->wmInfoPtr->flags & WM_TRANSPARENT)) { 413 CGrafPtr port; 414 415 GetPort(&port); 416 ClearPort(port, NULL); 417 } 418 break; 419 } 420 421 return eventFound; 422} 423 424/* 425 *---------------------------------------------------------------------- 426 * 427 * GenerateUpdateEvent -- 428 * 429 * Given a Macintosh window update event this function generates 430 * all the Expose XEvents needed by Tk. 431 * 432 * Results: 433 * True if event(s) are generated - false otherwise. 434 * 435 * Side effects: 436 * Additional events may be place on the Tk event queue. 437 * 438 *---------------------------------------------------------------------- 439 */ 440static int 441GenerateUpdateEvent(Window window) 442{ 443 WindowRef macWindow; 444 TkDisplay *dispPtr; 445 TkWindow *winPtr; 446 int result = 0; 447 CGRect updateBounds; 448 HIShapeRef rgn; 449 HIMutableShapeRef updateRgn; 450 int dx, dy; 451 452 dispPtr = TkGetDisplayList(); 453 winPtr = (TkWindow *)Tk_IdToWindow(dispPtr->display, window); 454 455 if (winPtr ==NULL ){ 456 return result; 457 } 458 macWindow = TkMacOSXDrawableWindow(window); 459 TK_IF_MAC_OS_X_API (5, HIWindowCopyShape, 460 ChkErr(HIWindowCopyShape, macWindow, kWindowUpdateRgn, 461 kHICoordSpaceWindow, &rgn); 462 dx = -winPtr->wmInfoPtr->xInParent; 463 dy = -winPtr->wmInfoPtr->yInParent; 464 ) TK_ELSE_MAC_OS_X (5, 465 Rect bounds; 466 467 TkMacOSXCheckTmpQdRgnEmpty(); 468 ChkErr(GetWindowRegion, macWindow, kWindowUpdateRgn, tkMacOSXtmpQdRgn); 469 rgn = HIShapeCreateWithQDRgn(tkMacOSXtmpQdRgn); 470 SetEmptyRgn(tkMacOSXtmpQdRgn); 471 ChkErr(GetWindowBounds, macWindow, kWindowContentRgn, &bounds); 472 dx = -bounds.left; 473 dy = -bounds.top; 474 ) TK_ENDIF 475 updateRgn = HIShapeCreateMutableCopy(rgn); 476 CFRelease(rgn); 477 ChkErr(HIShapeOffset, updateRgn, dx, dy); 478 HIShapeGetBounds(updateRgn, &updateBounds); 479#ifdef TK_MAC_DEBUG_CLIP_REGIONS 480 TkMacOSXDebugFlashRegion(window, updateRgn); 481#endif /* TK_MAC_DEBUG_CLIP_REGIONS */ 482 BeginUpdate(macWindow); 483 if (winPtr->wmInfoPtr->flags & WM_TRANSPARENT) { 484 ClearPort(TkMacOSXGetDrawablePort(window), updateRgn); 485 } 486 result = GenerateUpdates(updateRgn, &updateBounds, winPtr); 487 EndUpdate(macWindow); 488 CFRelease(updateRgn); 489 if (result) { 490 /* 491 * Ensure there are no pending idle-time redraws that could prevent 492 * the just posted Expose events from generating new redraws. 493 */ 494 495 Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT); 496 } 497 return result; 498 } 499 500/* 501 *---------------------------------------------------------------------- 502 * 503 * GenerateUpdates -- 504 * 505 * Given a Macintosh update region and a Tk window this function 506 * geneates a X Expose event for the window if it is within the 507 * update region. The function will then recursivly have each 508 * damaged window generate Expose events for its child windows. 509 * 510 * Results: 511 * True if event(s) are generated - false otherwise. 512 * 513 * Side effects: 514 * Additional events may be place on the Tk event queue. 515 * 516 *---------------------------------------------------------------------- 517 */ 518 519static int 520GenerateUpdates( 521 HIMutableShapeRef updateRgn, 522 CGRect *updateBounds, 523 TkWindow *winPtr) 524{ 525 TkWindow *childPtr; 526 XEvent event; 527 CGRect bounds, damageBounds; 528 HIShapeRef boundsRgn, damageRgn; 529 530 TkMacOSXWinCGBounds(winPtr, &bounds); 531 if (!CGRectIntersectsRect(bounds, *updateBounds)) { 532 return 0; 533 } 534 TK_IF_MAC_OS_X_API (4, HIShapeIntersectsRect, 535 if (!HIShapeIntersectsRect(updateRgn, &bounds)) { 536 return 0; 537 } 538 ) TK_ENDIF 539 540 /* 541 * Compute the bounding box of the area that the damage occured in. 542 */ 543 544 boundsRgn = HIShapeCreateWithRect(&bounds); 545 damageRgn = HIShapeCreateIntersection(updateRgn, boundsRgn); 546 if (HIShapeIsEmpty(damageRgn)) { 547 CFRelease(damageRgn); 548 CFRelease(boundsRgn); 549 return 0; 550 } 551 HIShapeGetBounds(damageRgn, &damageBounds); 552 ChkErr(TkMacOSHIShapeUnion, boundsRgn, updateRgn, updateRgn); 553 HIShapeGetBounds(updateRgn, updateBounds); 554 CFRelease(damageRgn); 555 CFRelease(boundsRgn); 556 557 event.xany.serial = Tk_Display(winPtr)->request; 558 event.xany.send_event = false; 559 event.xany.window = Tk_WindowId(winPtr); 560 event.xany.display = Tk_Display(winPtr); 561 event.type = Expose; 562 event.xexpose.x = damageBounds.origin.x - bounds.origin.x; 563 event.xexpose.y = damageBounds.origin.y - bounds.origin.y; 564 event.xexpose.width = damageBounds.size.width; 565 event.xexpose.height = damageBounds.size.height; 566 event.xexpose.count = 0; 567 Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); 568 569 /* 570 * Generate updates for the children of this window 571 */ 572 573 for (childPtr = winPtr->childList; childPtr != NULL; 574 childPtr = childPtr->nextPtr) { 575 if (!Tk_IsMapped(childPtr) || Tk_IsTopLevel(childPtr)) { 576 continue; 577 } 578 GenerateUpdates(updateRgn, updateBounds, childPtr); 579 } 580 581 /* 582 * Generate updates for any contained windows 583 */ 584 585 if (Tk_IsContainer(winPtr)) { 586 childPtr = TkpGetOtherWindow(winPtr); 587 if (childPtr != NULL && Tk_IsMapped(childPtr)) { 588 GenerateUpdates(updateRgn, updateBounds, childPtr); 589 } 590 591 /* 592 * TODO: Here we should handle out of process embedding. 593 */ 594 } 595 596 return 1; 597} 598 599/* 600 *---------------------------------------------------------------------- 601 * 602 * GenerateActivateEvents -- 603 * 604 * Given a Macintosh window activate event this function generates all the 605 * X Activate events needed by Tk. 606 * 607 * Results: 608 * True if event(s) are generated - false otherwise. 609 * 610 * Side effects: 611 * Additional events may be place on the Tk event queue. 612 * 613 *---------------------------------------------------------------------- 614 */ 615 616int 617GenerateActivateEvents( 618 Window window, /* Root X window for event. */ 619 int activeFlag ) 620{ 621 TkWindow *winPtr; 622 TkDisplay *dispPtr; 623 624 dispPtr = TkGetDisplayList(); 625 winPtr = (TkWindow *) Tk_IdToWindow(dispPtr->display, window); 626 if (winPtr == NULL || winPtr->window == None) { 627 return false; 628 } 629 630 TkGenerateActivateEvents(winPtr,activeFlag); 631 return true; 632} 633 634/* 635 *---------------------------------------------------------------------- 636 * 637 * TkMacOSXGenerateFocusEvent -- 638 * 639 * Given a Macintosh window activate event this function generates all the 640 * X Focus events needed by Tk. 641 * 642 * Results: 643 * True if event(s) are generated - false otherwise. 644 * 645 * Side effects: 646 * Additional events may be place on the Tk event queue. 647 * 648 *---------------------------------------------------------------------- 649 */ 650 651MODULE_SCOPE int 652TkMacOSXGenerateFocusEvent( 653 Window window, /* Root X window for event. */ 654 int activeFlag ) 655{ 656 XEvent event; 657 Tk_Window tkwin; 658 TkDisplay *dispPtr; 659 660 dispPtr = TkGetDisplayList(); 661 tkwin = Tk_IdToWindow(dispPtr->display, window); 662 if (tkwin == NULL) { 663 return false; 664 } 665 666 /* 667 * Don't send focus events to windows of class help or to 668 * windows with the kWindowNoActivatesAttribute. 669 */ 670 if (((TkWindow *)tkwin)->wmInfoPtr->macClass == kHelpWindowClass || 671 ((TkWindow *)tkwin)->wmInfoPtr->attributes & 672 kWindowNoActivatesAttribute) { 673 return false; 674 } 675 676 /* 677 * Generate FocusIn and FocusOut events. This event 678 * is only sent to the toplevel window. 679 */ 680 681 if (activeFlag) { 682 event.xany.type = FocusIn; 683 } else { 684 event.xany.type = FocusOut; 685 } 686 687 event.xany.serial = dispPtr->display->request; 688 event.xany.send_event = False; 689 event.xfocus.display = dispPtr->display; 690 event.xfocus.window = window; 691 event.xfocus.mode = NotifyNormal; 692 event.xfocus.detail = NotifyDetailNone; 693 694 Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); 695 return true; 696} 697 698/* 699 *---------------------------------------------------------------------- 700 * 701 * TkGenWMConfigureEvent -- 702 * 703 * Generate a ConfigureNotify event for Tk. Depending on the 704 * value of flag the values of width/height, x/y, or both may 705 * be changed. 706 * 707 * Results: 708 * None. 709 * 710 * Side effects: 711 * A ConfigureNotify event is sent to Tk. 712 * 713 *---------------------------------------------------------------------- 714 */ 715 716void 717TkGenWMConfigureEvent( 718 Tk_Window tkwin, 719 int x, 720 int y, 721 int width, 722 int height, 723 int flags) 724{ 725 XEvent event; 726 WmInfo *wmPtr; 727 TkWindow *winPtr = (TkWindow *) tkwin; 728 729 if (tkwin == NULL) { 730 return; 731 } 732 733 event.type = ConfigureNotify; 734 event.xconfigure.serial = Tk_Display(tkwin)->request; 735 event.xconfigure.send_event = False; 736 event.xconfigure.display = Tk_Display(tkwin); 737 event.xconfigure.event = Tk_WindowId(tkwin); 738 event.xconfigure.window = Tk_WindowId(tkwin); 739 event.xconfigure.border_width = winPtr->changes.border_width; 740 event.xconfigure.override_redirect = winPtr->atts.override_redirect; 741 if (winPtr->changes.stack_mode == Above) { 742 event.xconfigure.above = winPtr->changes.sibling; 743 } else { 744 event.xconfigure.above = None; 745 } 746 747 if (!(flags & TK_LOCATION_CHANGED)) { 748 x = Tk_X(tkwin); 749 y = Tk_Y(tkwin); 750 } 751 if (!(flags & TK_SIZE_CHANGED)) { 752 width = Tk_Width(tkwin); 753 height = Tk_Height(tkwin); 754 } 755 event.xconfigure.x = x; 756 event.xconfigure.y = y; 757 event.xconfigure.width = width; 758 event.xconfigure.height = height; 759 760 Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); 761 762 /* 763 * Update window manager information. 764 */ 765 if (Tk_IsTopLevel(winPtr)) { 766 wmPtr = winPtr->wmInfoPtr; 767 if (flags & TK_LOCATION_CHANGED) { 768 wmPtr->x = x; 769 wmPtr->y = y; 770 wmPtr->flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y); 771 } 772 if ((flags & TK_SIZE_CHANGED) && !(wmPtr->flags & WM_SYNC_PENDING) && 773 ((width != Tk_Width(tkwin)) || (height != Tk_Height(tkwin)))) { 774 if ((wmPtr->width == -1) && (width == winPtr->reqWidth)) { 775 /* 776 * Don't set external width, since the user didn't change it 777 * from what the widgets asked for. 778 */ 779 } else { 780 if (wmPtr->gridWin != NULL) { 781 wmPtr->width = wmPtr->reqGridWidth 782 + (width - winPtr->reqWidth)/wmPtr->widthInc; 783 if (wmPtr->width < 0) { 784 wmPtr->width = 0; 785 } 786 } else { 787 wmPtr->width = width; 788 } 789 } 790 if ((wmPtr->height == -1) && (height == winPtr->reqHeight)) { 791 /* 792 * Don't set external height, since the user didn't change it 793 * from what the widgets asked for. 794 */ 795 } else { 796 if (wmPtr->gridWin != NULL) { 797 wmPtr->height = wmPtr->reqGridHeight 798 + (height - winPtr->reqHeight)/wmPtr->heightInc; 799 if (wmPtr->height < 0) { 800 wmPtr->height = 0; 801 } 802 } else { 803 wmPtr->height = height; 804 } 805 } 806 wmPtr->configWidth = width; 807 wmPtr->configHeight = height; 808 } 809 } 810 811 /* 812 * Now set up the changes structure. Under X we wait for the 813 * ConfigureNotify to set these values. On the Mac we know imediatly that 814 * this is what we want - so we just set them. However, we need to 815 * make sure the windows clipping region is marked invalid so the 816 * change is visible to the subwindow. 817 */ 818 winPtr->changes.x = x; 819 winPtr->changes.y = y; 820 winPtr->changes.width = width; 821 winPtr->changes.height = height; 822 TkMacOSXInvalClipRgns(tkwin); 823} 824 825/* 826 *---------------------------------------------------------------------- 827 * 828 * TkGenWMDestroyEvent -- 829 * 830 * Generate a WM Destroy event for Tk. 831 * 832 * Results: 833 * None. 834 * 835 * Side effects: 836 * A WM_PROTOCOL/WM_DELETE_WINDOW event is sent to Tk. 837 * 838 *---------------------------------------------------------------------- 839 */ 840 841void 842TkGenWMDestroyEvent( 843 Tk_Window tkwin) 844{ 845 XEvent event; 846 847 event.xany.serial = Tk_Display(tkwin)->request; 848 event.xany.send_event = False; 849 event.xany.display = Tk_Display(tkwin); 850 851 event.xclient.window = Tk_WindowId(tkwin); 852 event.xclient.type = ClientMessage; 853 event.xclient.message_type = Tk_InternAtom(tkwin, "WM_PROTOCOLS"); 854 event.xclient.format = 32; 855 event.xclient.data.l[0] = Tk_InternAtom(tkwin, "WM_DELETE_WINDOW"); 856 Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); 857} 858 859/* 860 *---------------------------------------------------------------------- 861 * 862 * TkWmProtocolEventProc -- 863 * 864 * This procedure is called by the Tk_HandleEvent whenever a 865 * ClientMessage event arrives whose type is "WM_PROTOCOLS". 866 * This procedure handles the message from the window manager 867 * in an appropriate fashion. 868 * 869 * Results: 870 * None. 871 * 872 * Side effects: 873 * Depends on what sort of handler, if any, was set up for the 874 * protocol. 875 * 876 *---------------------------------------------------------------------- 877 */ 878 879void 880TkWmProtocolEventProc( 881 TkWindow *winPtr, /* Window to which the event was sent. */ 882 XEvent *eventPtr) /* X event. */ 883{ 884 WmInfo *wmPtr; 885 ProtocolHandler *protPtr; 886 Tcl_Interp *interp; 887 Atom protocol; 888 int result; 889 890 wmPtr = winPtr->wmInfoPtr; 891 if (wmPtr == NULL) { 892 return; 893 } 894 protocol = (Atom) eventPtr->xclient.data.l[0]; 895 for (protPtr = wmPtr->protPtr; protPtr != NULL; 896 protPtr = protPtr->nextPtr) { 897 if (protocol == protPtr->protocol) { 898 Tcl_Preserve((ClientData) protPtr); 899 interp = protPtr->interp; 900 Tcl_Preserve((ClientData) interp); 901 result = Tcl_GlobalEval(interp, protPtr->command); 902 if (result != TCL_OK) { 903 Tcl_AddErrorInfo(interp, "\n (command for \""); 904 Tcl_AddErrorInfo(interp, 905 Tk_GetAtomName((Tk_Window) winPtr, protocol)); 906 Tcl_AddErrorInfo(interp, "\" window manager protocol)"); 907 Tk_BackgroundError(interp); 908 } 909 Tcl_Release((ClientData) interp); 910 Tcl_Release((ClientData) protPtr); 911 return; 912 } 913 } 914 915 /* 916 * No handler was present for this protocol. If this is a 917 * WM_DELETE_WINDOW message then just destroy the window. 918 */ 919 920 if (protocol == Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW")) { 921 Tk_DestroyWindow((Tk_Window) winPtr); 922 } 923} 924 925/* 926 *---------------------------------------------------------------------- 927 * 928 * Tk_MacOSXIsAppInFront -- 929 * 930 * Returns 1 if this app is the foreground app. 931 * 932 * Results: 933 * 1 if app is in front, 0 otherwise. 934 * 935 * Side effects: 936 * None. 937 * 938 *---------------------------------------------------------------------- 939 */ 940 941int 942Tk_MacOSXIsAppInFront(void) 943{ 944 OSStatus err; 945 ProcessSerialNumber frontPsn, ourPsn = {0, kCurrentProcess}; 946 Boolean isFrontProcess = true; 947 948 err = ChkErr(GetFrontProcess, &frontPsn); 949 if (err == noErr) { 950 ChkErr(SameProcess, &frontPsn, &ourPsn, &isFrontProcess); 951 } 952 953 return (isFrontProcess == true); 954} 955 956/* 957 *---------------------------------------------------------------------- 958 * 959 * ClearPort -- 960 * 961 * Clear (i.e. fill with transparent color) the given port. 962 * 963 * Results: 964 * None. 965 * 966 * Side effects: 967 * None. 968 * 969 *---------------------------------------------------------------------- 970 */ 971static void 972ClearPort( 973 CGrafPtr port, 974 HIShapeRef updateRgn) 975{ 976 CGContextRef context; 977 Rect bounds; 978 CGRect rect; 979 980 GetPortBounds(port, &bounds); 981 QDBeginCGContext(port, &context); 982 SyncCGContextOriginWithPort(context, port); 983 CGContextConcatCTM(context, CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, 984 bounds.bottom - bounds.top)); 985 if (updateRgn) { 986 ChkErr(HIShapeReplacePathInCGContext, updateRgn, context); 987 CGContextEOClip(context); 988 } 989 rect = CGRectMake(0, 0, bounds.right, bounds.bottom); 990 CGContextClearRect(context, rect); 991 QDEndCGContext(port, &context); 992} 993