1/* 2 * tkMacOSXWindowEvent.c -- 3 * 4 * This file defines the routines for both creating and handling Window 5 * Manager class events for Tk. 6 * 7 * Copyright 2001-2009, Apple Inc. 8 * Copyright (c) 2005-2009 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 * RCS: @(#) $Id$ 14 */ 15 16#include "tkMacOSXPrivate.h" 17#include "tkMacOSXWm.h" 18#include "tkMacOSXEvent.h" 19#include "tkMacOSXDebug.h" 20 21/* 22#ifdef TK_MAC_DEBUG 23#define TK_MAC_DEBUG_EVENTS 24#define TK_MAC_DEBUG_DRAWING 25#endif 26*/ 27 28/* 29 * Declaration of functions used only in this file 30 */ 31 32static int GenerateUpdates(HIMutableShapeRef updateRgn, 33 CGRect *updateBounds, TkWindow *winPtr); 34static int GenerateActivateEvents(TkWindow *winPtr, 35 int activeFlag); 36static void DoWindowActivate(ClientData clientData); 37 38#pragma mark TKApplication(TKWindowEvent) 39 40#ifdef TK_MAC_DEBUG_NOTIFICATIONS 41extern NSString *NSWindowWillOrderOnScreenNotification; 42extern NSString *NSWindowDidOrderOnScreenNotification; 43extern NSString *NSWindowDidOrderOffScreenNotification; 44 45#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 46#define NSWindowWillStartLiveResizeNotification @"NSWindowWillStartLiveResizeNotification" 47#define NSWindowDidEndLiveResizeNotification @"NSWindowDidEndLiveResizeNotification" 48#endif 49#endif 50 51@implementation TKApplication(TKWindowEvent) 52- (void)windowActivation:(NSNotification *)notification { 53#ifdef TK_MAC_DEBUG_NOTIFICATIONS 54 TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); 55#endif 56 BOOL activate = [[notification name] isEqualToString:NSWindowDidBecomeKeyNotification]; 57 NSWindow *w = [notification object]; 58 TkWindow *winPtr = TkMacOSXGetTkWindow(w); 59 60 if (winPtr && Tk_IsMapped(winPtr)) { 61 GenerateActivateEvents(winPtr, activate); 62 } 63} 64- (void)windowBoundsChanged:(NSNotification *)notification { 65#ifdef TK_MAC_DEBUG_NOTIFICATIONS 66 TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); 67#endif 68 BOOL movedOnly = [[notification name] isEqualToString:NSWindowDidMoveNotification]; 69 if (movedOnly) { 70 /* constraining to screen after move not needed with AppKit */ 71 } 72 NSWindow *w = [notification object]; 73 TkWindow *winPtr = TkMacOSXGetTkWindow(w); 74 75 if (winPtr) { 76 WmInfo *wmPtr = winPtr->wmInfoPtr; 77 NSRect bounds = [w frame]; 78 int x, y, width = -1, height = -1, flags = 0; 79 80 x = bounds.origin.x; 81 y = tkMacOSXZeroScreenHeight - (bounds.origin.y + bounds.size.height); 82 if (winPtr->changes.x != x || winPtr->changes.y != y){ 83 flags |= TK_LOCATION_CHANGED; 84 } else { 85 x = y = -1; 86 } 87 if (!movedOnly && (winPtr->changes.width != bounds.size.width || 88 winPtr->changes.height != bounds.size.height)) { 89 width = bounds.size.width - wmPtr->xInParent; 90 height = bounds.size.height - wmPtr->yInParent; 91 flags |= TK_SIZE_CHANGED; 92 } 93 if (Tcl_GetServiceMode() != TCL_SERVICE_NONE) { 94 /* propagate geometry changes immediately */ 95 flags |= TK_MACOSX_HANDLE_EVENT_IMMEDIATELY; 96 } 97 TkGenWMConfigureEvent((Tk_Window) winPtr, x, y, width, height, flags); 98 } 99} 100- (void)windowExpanded:(NSNotification *)notification { 101#ifdef TK_MAC_DEBUG_NOTIFICATIONS 102 TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); 103#endif 104 NSWindow *w = [notification object]; 105 TkWindow *winPtr = TkMacOSXGetTkWindow(w); 106 107 if (winPtr) { 108 winPtr->wmInfoPtr->hints.initial_state = 109 TkMacOSXIsWindowZoomed(winPtr) ? ZoomState : NormalState; 110 Tk_MapWindow((Tk_Window) winPtr); 111 if (Tcl_GetServiceMode() != TCL_SERVICE_NONE) { 112 /* Process all Tk events generated by Tk_MapWindow() */ 113 while (Tcl_ServiceEvent(0)) {} 114 while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} 115 116 /* 117 * NSWindowDidDeminiaturizeNotification is received after 118 * NSWindowDidBecomeKeyNotification, so activate manually 119 */ 120 121 GenerateActivateEvents(winPtr, 1); 122 } else { 123 Tcl_DoWhenIdle(DoWindowActivate, winPtr); 124 } 125 } 126} 127- (void)windowCollapsed:(NSNotification *)notification { 128#ifdef TK_MAC_DEBUG_NOTIFICATIONS 129 TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); 130#endif 131 NSWindow *w = [notification object]; 132 TkWindow *winPtr = TkMacOSXGetTkWindow(w); 133 134 if (winPtr) { 135 Tk_UnmapWindow((Tk_Window) winPtr); 136 } 137} 138- (BOOL)windowShouldClose:(NSWindow *)w { 139#ifdef TK_MAC_DEBUG_NOTIFICATIONS 140 TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, w); 141#endif 142 TkWindow *winPtr = TkMacOSXGetTkWindow(w); 143 144 if (winPtr) { 145 TkGenWMDestroyEvent((Tk_Window) winPtr); 146 } 147 148 /* 149 * If necessary, TkGenWMDestroyEvent() handles [close]ing the window, 150 * so can always return NO from -windowShouldClose: for a Tk window. 151 */ 152 153 return (winPtr ? NO : YES); 154} 155#ifdef TK_MAC_DEBUG_NOTIFICATIONS 156- (void)windowDragStart:(NSNotification *)notification { 157 TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); 158} 159- (void)windowLiveResize:(NSNotification *)notification { 160 TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); 161 //BOOL start = [[notification name] isEqualToString:NSWindowWillStartLiveResizeNotification]; 162} 163- (void)windowMapped:(NSNotification *)notification { 164 TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); 165 NSWindow *w = [notification object]; 166 TkWindow *winPtr = TkMacOSXGetTkWindow(w); 167 168 if (winPtr) { 169 //Tk_MapWindow((Tk_Window) winPtr); 170 } 171} 172- (void)windowBecameVisible:(NSNotification *)notification { 173#ifdef TK_MAC_DEBUG_NOTIFICATIONS 174 TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); 175#endif 176} 177- (void)windowUnmapped:(NSNotification *)notification { 178 TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); 179 NSWindow *w = [notification object]; 180 TkWindow *winPtr = TkMacOSXGetTkWindow(w); 181 182 if (winPtr) { 183 //Tk_UnmapWindow((Tk_Window) winPtr); 184 } 185} 186#endif 187 188- (void)_setupWindowNotifications { 189 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; 190#define observe(n, s) [nc addObserver:self selector:@selector(s) name:(n) object:nil] 191 observe(NSWindowDidBecomeKeyNotification, windowActivation:); 192 observe(NSWindowDidResignKeyNotification, windowActivation:); 193 observe(NSWindowDidMoveNotification, windowBoundsChanged:); 194 observe(NSWindowDidResizeNotification, windowBoundsChanged:); 195 observe(NSWindowDidDeminiaturizeNotification, windowExpanded:); 196 observe(NSWindowDidMiniaturizeNotification, windowCollapsed:); 197#ifdef TK_MAC_DEBUG_NOTIFICATIONS 198 observe(NSWindowWillMoveNotification, windowDragStart:); 199 observe(NSWindowWillStartLiveResizeNotification, windowLiveResize:); 200 observe(NSWindowDidEndLiveResizeNotification, windowLiveResize:); 201 observe(NSWindowWillOrderOnScreenNotification, windowMapped:); 202 observe(NSWindowDidOrderOnScreenNotification, windowBecameVisible:); 203 observe(NSWindowDidOrderOffScreenNotification, windowUnmapped:); 204#endif 205#undef observe 206} 207@end 208 209#pragma mark TKApplication(TKApplicationEvent) 210 211@implementation TKApplication(TKApplicationEvent) 212- (void)applicationActivate:(NSNotification *)notification { 213#ifdef TK_MAC_DEBUG_NOTIFICATIONS 214 TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); 215#endif 216 [NSApp tkCheckPasteboard]; 217} 218- (void)applicationDeactivate:(NSNotification *)notification { 219#ifdef TK_MAC_DEBUG_NOTIFICATIONS 220 TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); 221#endif 222 TkSuspendClipboard(); 223} 224- (void)applicationShowHide:(NSNotification *)notification { 225#ifdef TK_MAC_DEBUG_NOTIFICATIONS 226 TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); 227#endif 228 const char *cmd = ([[notification name] isEqualToString: 229 NSApplicationDidUnhideNotification] ? 230 "::tk::mac::OnShow" : "::tk::mac::OnHide"); 231 Tcl_CmdInfo dummy; 232 233 if (_eventInterp && Tcl_GetCommandInfo(_eventInterp, cmd, &dummy)) { 234 int code = Tcl_EvalEx(_eventInterp, cmd, -1, TCL_EVAL_GLOBAL); 235 if (code != TCL_OK) { 236 Tcl_BackgroundError(_eventInterp); 237 } 238 Tcl_ResetResult(_eventInterp); 239 } 240} 241- (void)displayChanged:(NSNotification *)notification { 242#ifdef TK_MAC_DEBUG_NOTIFICATIONS 243 TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); 244#endif 245 TkDisplay *dispPtr = TkGetDisplayList(); 246 247 if (dispPtr) { 248 TkMacOSXDisplayChanged(dispPtr->display); 249 } 250} 251@end 252 253#pragma mark - 254 255/* 256 *---------------------------------------------------------------------- 257 * 258 * GenerateUpdates -- 259 * 260 * Given a Macintosh update region and a Tk window this function geneates 261 * a X Expose event for the window if it is within the update region. The 262 * function will then recursivly have each damaged window generate Expose 263 * events for its child windows. 264 * 265 * Results: 266 * True if event(s) are generated - false otherwise. 267 * 268 * Side effects: 269 * Additional events may be place on the Tk event queue. 270 * 271 *---------------------------------------------------------------------- 272 */ 273 274static int 275GenerateUpdates( 276 HIMutableShapeRef updateRgn, 277 CGRect *updateBounds, 278 TkWindow *winPtr) 279{ 280 TkWindow *childPtr; 281 XEvent event; 282 CGRect bounds, damageBounds; 283 HIShapeRef boundsRgn, damageRgn; 284 285 TkMacOSXWinCGBounds(winPtr, &bounds); 286 if (!CGRectIntersectsRect(bounds, *updateBounds)) { 287 return 0; 288 } 289 if (!HIShapeIntersectsRect(updateRgn, &bounds)) { 290 return 0; 291 } 292 293 /* 294 * Compute the bounding box of the area that the damage occured in. 295 */ 296 297 boundsRgn = HIShapeCreateWithRect(&bounds); 298 damageRgn = HIShapeCreateIntersection(updateRgn, boundsRgn); 299 if (HIShapeIsEmpty(damageRgn)) { 300 CFRelease(damageRgn); 301 CFRelease(boundsRgn); 302 return 0; 303 } 304 HIShapeGetBounds(damageRgn, &damageBounds); 305 if (!Tk_IsTopLevel(winPtr)) { 306 ChkErr(TkMacOSHIShapeUnion, boundsRgn, updateRgn, updateRgn); 307 HIShapeGetBounds(updateRgn, updateBounds); 308 } 309 CFRelease(damageRgn); 310 CFRelease(boundsRgn); 311 312 event.xany.serial = LastKnownRequestProcessed(Tk_Display(winPtr)); 313 event.xany.send_event = false; 314 event.xany.window = Tk_WindowId(winPtr); 315 event.xany.display = Tk_Display(winPtr); 316 event.type = Expose; 317 event.xexpose.x = damageBounds.origin.x - bounds.origin.x; 318 event.xexpose.y = damageBounds.origin.y - bounds.origin.y; 319 event.xexpose.width = damageBounds.size.width; 320 event.xexpose.height = damageBounds.size.height; 321 event.xexpose.count = 0; 322 Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); 323#ifdef TK_MAC_DEBUG_DRAWING 324 TKLog(@"Expose %p {{%d, %d}, {%d, %d}}", event.xany.window, event.xexpose.x, 325 event.xexpose.y, event.xexpose.width, event.xexpose.height); 326#endif 327 328 /* 329 * Generate updates for the children of this window 330 */ 331 332 for (childPtr = winPtr->childList; childPtr != NULL; 333 childPtr = childPtr->nextPtr) { 334 if (!Tk_IsMapped(childPtr) || Tk_IsTopLevel(childPtr)) { 335 continue; 336 } 337 GenerateUpdates(updateRgn, updateBounds, childPtr); 338 } 339 340 /* 341 * Generate updates for any contained windows 342 */ 343 344 if (Tk_IsContainer(winPtr)) { 345 childPtr = TkpGetOtherWindow(winPtr); 346 if (childPtr != NULL && Tk_IsMapped(childPtr)) { 347 GenerateUpdates(updateRgn, updateBounds, childPtr); 348 } 349 350 /* 351 * TODO: Here we should handle out of process embedding. 352 */ 353 } 354 355 return 1; 356} 357 358/* 359 *---------------------------------------------------------------------- 360 * 361 * GenerateActivateEvents -- 362 * 363 * Given a Macintosh window activate event this function generates all 364 * the X Activate events needed by Tk. 365 * 366 * Results: 367 * True if event(s) are generated - false otherwise. 368 * 369 * Side effects: 370 * Additional events may be place on the Tk event queue. 371 * 372 *---------------------------------------------------------------------- 373 */ 374 375int 376GenerateActivateEvents( 377 TkWindow *winPtr, 378 int activeFlag) 379{ 380 TkGenerateActivateEvents(winPtr, activeFlag); 381 TkMacOSXGenerateFocusEvent(winPtr, activeFlag); 382 TkMacOSXEnterExitFullscreen(winPtr, activeFlag); 383 return true; 384} 385 386/* 387 *---------------------------------------------------------------------- 388 * 389 * DoWindowActivate -- 390 * 391 * Idle handler that calls GenerateActivateEvents(). 392 * 393 * Results: 394 * None. 395 * 396 * Side effects: 397 * Additional events may be place on the Tk event queue. 398 * 399 *---------------------------------------------------------------------- 400 */ 401 402void 403DoWindowActivate( 404 ClientData clientData) 405{ 406 GenerateActivateEvents(clientData, 1); 407} 408 409/* 410 *---------------------------------------------------------------------- 411 * 412 * TkMacOSXGenerateFocusEvent -- 413 * 414 * Given a Macintosh window activate event this function generates all 415 * the X Focus events needed by Tk. 416 * 417 * Results: 418 * True if event(s) are generated - false otherwise. 419 * 420 * Side effects: 421 * Additional events may be place on the Tk event queue. 422 * 423 *---------------------------------------------------------------------- 424 */ 425 426MODULE_SCOPE int 427TkMacOSXGenerateFocusEvent( 428 TkWindow *winPtr, /* Root X window for event. */ 429 int activeFlag) 430{ 431 XEvent event; 432 433 /* 434 * Don't send focus events to windows of class help or to windows with the 435 * kWindowNoActivatesAttribute. 436 */ 437 438 if (winPtr->wmInfoPtr && (winPtr->wmInfoPtr->macClass == kHelpWindowClass || 439 winPtr->wmInfoPtr->attributes & kWindowNoActivatesAttribute)) { 440 return false; 441 } 442 443 /* 444 * Generate FocusIn and FocusOut events. This event is only sent to the 445 * toplevel window. 446 */ 447 448 if (activeFlag) { 449 event.xany.type = FocusIn; 450 } else { 451 event.xany.type = FocusOut; 452 } 453 454 event.xany.serial = LastKnownRequestProcessed(Tk_Display(winPtr)); 455 event.xany.send_event = False; 456 event.xfocus.display = Tk_Display(winPtr); 457 event.xfocus.window = winPtr->window; 458 event.xfocus.mode = NotifyNormal; 459 event.xfocus.detail = NotifyDetailNone; 460 461 Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); 462 return true; 463} 464 465/* 466 *---------------------------------------------------------------------- 467 * 468 * TkGenWMConfigureEvent -- 469 * 470 * Generate a ConfigureNotify event for Tk. Depending on the value of 471 * flag the values of width/height, x/y, or both may be changed. 472 * 473 * Results: 474 * None. 475 * 476 * Side effects: 477 * A ConfigureNotify event is sent to Tk. 478 * 479 *---------------------------------------------------------------------- 480 */ 481 482void 483TkGenWMConfigureEvent( 484 Tk_Window tkwin, 485 int x, 486 int y, 487 int width, 488 int height, 489 int flags) 490{ 491 XEvent event; 492 WmInfo *wmPtr; 493 TkWindow *winPtr = (TkWindow *) tkwin; 494 495 if (tkwin == NULL) { 496 return; 497 } 498 499 event.type = ConfigureNotify; 500 event.xconfigure.serial = LastKnownRequestProcessed(Tk_Display(tkwin)); 501 event.xconfigure.send_event = False; 502 event.xconfigure.display = Tk_Display(tkwin); 503 event.xconfigure.event = Tk_WindowId(tkwin); 504 event.xconfigure.window = Tk_WindowId(tkwin); 505 event.xconfigure.border_width = winPtr->changes.border_width; 506 event.xconfigure.override_redirect = winPtr->atts.override_redirect; 507 if (winPtr->changes.stack_mode == Above) { 508 event.xconfigure.above = winPtr->changes.sibling; 509 } else { 510 event.xconfigure.above = None; 511 } 512 513 if (!(flags & TK_LOCATION_CHANGED)) { 514 x = Tk_X(tkwin); 515 y = Tk_Y(tkwin); 516 } 517 if (!(flags & TK_SIZE_CHANGED)) { 518 width = Tk_Width(tkwin); 519 height = Tk_Height(tkwin); 520 } 521 event.xconfigure.x = x; 522 event.xconfigure.y = y; 523 event.xconfigure.width = width; 524 event.xconfigure.height = height; 525 526 if (flags & TK_MACOSX_HANDLE_EVENT_IMMEDIATELY) { 527 Tk_HandleEvent(&event); 528 } else { 529 Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); 530 } 531 532 /* 533 * Update window manager information. 534 */ 535 536 if (Tk_IsTopLevel(winPtr)) { 537 wmPtr = winPtr->wmInfoPtr; 538 if (flags & TK_LOCATION_CHANGED) { 539 wmPtr->x = x; 540 wmPtr->y = y; 541 wmPtr->flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y); 542 } 543 if ((flags & TK_SIZE_CHANGED) && !(wmPtr->flags & WM_SYNC_PENDING) && 544 ((width != Tk_Width(tkwin)) || (height != Tk_Height(tkwin)))) { 545 if ((wmPtr->width == -1) && (width == winPtr->reqWidth)) { 546 /* 547 * Don't set external width, since the user didn't change it 548 * from what the widgets asked for. 549 */ 550 } else { 551 if (wmPtr->gridWin != NULL) { 552 wmPtr->width = wmPtr->reqGridWidth 553 + (width - winPtr->reqWidth)/wmPtr->widthInc; 554 if (wmPtr->width < 0) { 555 wmPtr->width = 0; 556 } 557 } else { 558 wmPtr->width = width; 559 } 560 } 561 if ((wmPtr->height == -1) && (height == winPtr->reqHeight)) { 562 /* 563 * Don't set external height, since the user didn't change it 564 * from what the widgets asked for. 565 */ 566 } else { 567 if (wmPtr->gridWin != NULL) { 568 wmPtr->height = wmPtr->reqGridHeight 569 + (height - winPtr->reqHeight)/wmPtr->heightInc; 570 if (wmPtr->height < 0) { 571 wmPtr->height = 0; 572 } 573 } else { 574 wmPtr->height = height; 575 } 576 } 577 wmPtr->configWidth = width; 578 wmPtr->configHeight = height; 579 } 580 } 581 582 /* 583 * Now set up the changes structure. Under X we wait for the 584 * ConfigureNotify to set these values. On the Mac we know imediatly that 585 * this is what we want - so we just set them. However, we need to make 586 * sure the windows clipping region is marked invalid so the change is 587 * visible to the subwindow. 588 */ 589 590 winPtr->changes.x = x; 591 winPtr->changes.y = y; 592 winPtr->changes.width = width; 593 winPtr->changes.height = height; 594 TkMacOSXInvalClipRgns(tkwin); 595} 596 597/* 598 *---------------------------------------------------------------------- 599 * 600 * TkGenWMDestroyEvent -- 601 * 602 * Generate a WM Destroy event for Tk. 603 * 604 * Results: 605 * None. 606 * 607 * Side effects: 608 * A WM_PROTOCOL/WM_DELETE_WINDOW event is sent to Tk. 609 * 610 *---------------------------------------------------------------------- 611 */ 612 613void 614TkGenWMDestroyEvent( 615 Tk_Window tkwin) 616{ 617 XEvent event; 618 619 event.xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin)); 620 event.xany.send_event = False; 621 event.xany.display = Tk_Display(tkwin); 622 623 event.xclient.window = Tk_WindowId(tkwin); 624 event.xclient.type = ClientMessage; 625 event.xclient.message_type = Tk_InternAtom(tkwin, "WM_PROTOCOLS"); 626 event.xclient.format = 32; 627 event.xclient.data.l[0] = Tk_InternAtom(tkwin, "WM_DELETE_WINDOW"); 628 Tk_HandleEvent(&event); 629} 630 631/* 632 *---------------------------------------------------------------------- 633 * 634 * TkWmProtocolEventProc -- 635 * 636 * This procedure is called by the Tk_HandleEvent whenever a 637 * ClientMessage event arrives whose type is "WM_PROTOCOLS". This 638 * procedure handles the message from the window manager in an 639 * appropriate fashion. 640 * 641 * Results: 642 * None. 643 * 644 * Side effects: 645 * Depends on what sort of handler, if any, was set up for the protocol. 646 * 647 *---------------------------------------------------------------------- 648 */ 649 650void 651TkWmProtocolEventProc( 652 TkWindow *winPtr, /* Window to which the event was sent. */ 653 XEvent *eventPtr) /* X event. */ 654{ 655 WmInfo *wmPtr; 656 ProtocolHandler *protPtr; 657 Tcl_Interp *interp; 658 Atom protocol; 659 int result; 660 661 wmPtr = winPtr->wmInfoPtr; 662 if (wmPtr == NULL) { 663 return; 664 } 665 protocol = (Atom) eventPtr->xclient.data.l[0]; 666 for (protPtr = wmPtr->protPtr; protPtr != NULL; 667 protPtr = protPtr->nextPtr) { 668 if (protocol == protPtr->protocol) { 669 Tcl_Preserve(protPtr); 670 interp = protPtr->interp; 671 Tcl_Preserve(interp); 672 result = Tcl_GlobalEval(interp, protPtr->command); 673 if (result != TCL_OK) { 674 Tcl_AddErrorInfo(interp, "\n (command for \""); 675 Tcl_AddErrorInfo(interp, 676 Tk_GetAtomName((Tk_Window) winPtr, protocol)); 677 Tcl_AddErrorInfo(interp, "\" window manager protocol)"); 678 Tk_BackgroundError(interp); 679 } 680 Tcl_Release(interp); 681 Tcl_Release(protPtr); 682 return; 683 } 684 } 685 686 /* 687 * No handler was present for this protocol. If this is a WM_DELETE_WINDOW 688 * message then just destroy the window. 689 */ 690 691 if (protocol == Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW")) { 692 Tk_DestroyWindow((Tk_Window) winPtr); 693 } 694} 695 696/* 697 *---------------------------------------------------------------------- 698 * 699 * Tk_MacOSXIsAppInFront -- 700 * 701 * Returns 1 if this app is the foreground app. 702 * 703 * Results: 704 * 1 if app is in front, 0 otherwise. 705 * 706 * Side effects: 707 * None. 708 * 709 *---------------------------------------------------------------------- 710 */ 711 712int 713Tk_MacOSXIsAppInFront(void) 714{ 715 OSStatus err; 716 ProcessSerialNumber frontPsn, ourPsn = {0, kCurrentProcess}; 717 Boolean isFrontProcess = true; 718 719 err = ChkErr(GetFrontProcess, &frontPsn); 720 if (err == noErr) { 721 ChkErr(SameProcess, &frontPsn, &ourPsn, &isFrontProcess); 722 } 723 724 return (isFrontProcess == true); 725} 726 727#pragma mark TKContentView 728 729#import <ApplicationServices/ApplicationServices.h> 730 731/* 732 * Custom content view for Tk NSWindows, containing standard NSView subviews. 733 * The goal is to emulate X11-style drawing in response to Expose events: 734 * during the normal AppKit drawing cycle, we supress drawing of all subviews 735 * (using a technique adapted from WebKit's WebHTMLView) and instead send 736 * Expose events about the subviews that would be redrawn. 737 * Tk Expose event handling and drawing handlers then draw the subviews 738 * manually via their -displayRectIgnoringOpacity: 739 */ 740 741@interface TKContentView(TKWindowEvent) 742- (void)drawRect:(NSRect)rect; 743- (void)generateExposeEvents:(HIMutableShapeRef)shape; 744- (BOOL)isOpaque; 745- (BOOL)wantsDefaultClipping; 746- (BOOL)acceptsFirstResponder; 747- (void)keyDown:(NSEvent *)theEvent; 748@end 749 750@implementation TKContentView 751@end 752 753static Tk_RestrictAction ExposeRestrictProc(ClientData arg, XEvent *eventPtr) 754{ 755 return (eventPtr->type == Expose && eventPtr->xany.serial == PTR2UINT(arg) ? 756 TK_PROCESS_EVENT : TK_DEFER_EVENT); 757} 758 759@implementation TKContentView(TKWindowEvent) 760 761- (void)drawRect:(NSRect)rect { 762 const NSRect *rectsBeingDrawn; 763 NSInteger rectsBeingDrawnCount; 764 [self getRectsBeingDrawn:&rectsBeingDrawn count:&rectsBeingDrawnCount]; 765#ifdef TK_MAC_DEBUG_DRAWING 766 TKLog(@"-[%@(%p) %s%@]", [self class], self, _cmd, NSStringFromRect(rect)); 767 [[NSColor colorWithDeviceRed:0.0 green:1.0 blue:0.0 alpha:.1] setFill]; 768 NSRectFillListUsingOperation(rectsBeingDrawn, rectsBeingDrawnCount, 769 NSCompositeSourceOver); 770#endif 771 NSWindow *w = [self window]; 772 if ([self isOpaque] && [w showsResizeIndicator]) { 773 NSRect bounds = [self convertRect:[w _growBoxRect] fromView:nil]; 774 if ([self needsToDrawRect:bounds]) { 775 NSEraseRect(bounds); 776 } 777 } 778 CGFloat height = [self bounds].size.height; 779 HIMutableShapeRef drawShape = HIShapeCreateMutable(); 780 while (rectsBeingDrawnCount--) { 781 CGRect r = NSRectToCGRect(*rectsBeingDrawn++); 782 r.origin.y = height - (r.origin.y + r.size.height); 783 HIShapeUnionWithRect(drawShape, &r); 784 } 785 if (CFRunLoopGetMain() == CFRunLoopGetCurrent()) { 786 [self generateExposeEvents:drawShape]; 787 } else { 788 [self performSelectorOnMainThread:@selector(generateExposeEvents:) 789 withObject:(id)drawShape waitUntilDone:NO 790 modes:[NSArray arrayWithObjects:NSRunLoopCommonModes, 791 NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, 792 nil]]; 793 } 794 CFRelease(drawShape); 795} 796 797- (void)generateExposeEvents:(HIMutableShapeRef)shape { 798 TkWindow *winPtr = TkMacOSXGetTkWindow([self window]); 799 unsigned long serial; 800 CGRect updateBounds; 801 802 if (!winPtr) { 803 return; 804 } 805 HIShapeGetBounds(shape, &updateBounds); 806 serial = LastKnownRequestProcessed(Tk_Display(winPtr)); 807 if (GenerateUpdates(shape, &updateBounds, winPtr) && 808 ![[NSRunLoop currentRunLoop] currentMode] && 809 Tcl_GetServiceMode() != TCL_SERVICE_NONE) { 810 /* 811 * Ensure there are no pending idle-time redraws that could prevent 812 * the just posted Expose events from generating new redraws. 813 */ 814 815 while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} 816 817 /* 818 * For smoother drawing, process Expose events and resulting redraws 819 * immediately instead of at idle time. 820 */ 821 822 ClientData oldArg; 823 Tk_RestrictProc *oldProc = Tk_RestrictEvents(ExposeRestrictProc, 824 UINT2PTR(serial), &oldArg); 825 while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {} 826 Tk_RestrictEvents(oldProc, oldArg, &oldArg); 827 while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} 828 } 829} 830 831- (void)tkToolbarButton:(id)sender { 832#ifdef TK_MAC_DEBUG_EVENTS 833 TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd); 834#endif 835 XVirtualEvent event; 836 int x, y; 837 TkWindow *winPtr = TkMacOSXGetTkWindow([self window]); 838 Tk_Window tkwin = (Tk_Window) winPtr; 839 840 bzero(&event, sizeof(XVirtualEvent)); 841 event.type = VirtualEvent; 842 event.serial = LastKnownRequestProcessed(Tk_Display(tkwin)); 843 event.send_event = false; 844 event.display = Tk_Display(tkwin); 845 event.event = Tk_WindowId(tkwin); 846 event.root = XRootWindow(Tk_Display(tkwin), 0); 847 event.subwindow = None; 848 event.time = TkpGetMS(); 849 XQueryPointer(NULL, winPtr->window, NULL, NULL, 850 &event.x_root, &event.y_root, &x, &y, &event.state); 851 Tk_TopCoordsToWindow(tkwin, x, y, &event.x, &event.y); 852 event.same_screen = true; 853 event.name = Tk_GetUid("ToolbarButton"); 854 Tk_QueueWindowEvent((XEvent *) &event, TCL_QUEUE_TAIL); 855} 856 857#ifdef TK_MAC_DEBUG_DRAWING 858- (void)setFrameSize:(NSSize)newSize { 859 TKLog(@"-[%@(%p) %s%@]", [self class], self, _cmd, NSStringFromSize(newSize)); 860 [super setFrameSize:newSize]; 861} 862- (void)setNeedsDisplayInRect:(NSRect)invalidRect { 863 TKLog(@"-[%@(%p) %s%@]", [self class], self, _cmd, 864 NSStringFromRect(invalidRect)); 865 [super setNeedsDisplayInRect:invalidRect]; 866} 867#endif 868- (BOOL)isOpaque { 869 NSWindow *w = [self window]; 870 871 return (w && (([w styleMask] & NSTexturedBackgroundWindowMask) || 872 ![w isOpaque]) ? NO : YES); 873} 874 875- (BOOL)wantsDefaultClipping { 876 return NO; 877} 878 879- (BOOL)acceptsFirstResponder { 880 return YES; 881} 882 883- (void)keyDown:(NSEvent *)theEvent { 884#ifdef TK_MAC_DEBUG_EVENTS 885 TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, theEvent); 886#endif 887} 888 889@end 890 891#pragma mark TKContentViewPrivate 892 893/* 894 * Technique adapted from WebKit/WebKit/mac/WebView/WebHTMLView.mm to supress 895 * normal AppKit subview drawing and make all drawing go through us. 896 * Overrides NSView internals. 897 */ 898 899@interface TKContentView(TKContentViewPrivate) 900- (id)initWithFrame:(NSRect)frame; 901- (void)_setAsideSubviews; 902- (void)_restoreSubviews; 903@end 904 905@interface NSView(TKContentViewPrivate) 906- (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView; 907- (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect; 908- (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView; 909- (void)_lightWeightRecursiveDisplayInRect:(NSRect)visRect; 910- (BOOL)_drawRectIfEmpty; 911- (void)_drawRect:(NSRect)inRect clip:(BOOL)clip; 912- (void)_setDrawsOwnDescendants:(BOOL)drawsOwnDescendants; 913@end 914 915 916@implementation TKContentView(TKContentViewPrivate) 917 918- (id)initWithFrame:(NSRect)frame { 919 self = [super initWithFrame:frame]; 920 if (self) { 921 _savedSubviews = nil; 922 _subviewsSetAside = NO; 923 [self _setDrawsOwnDescendants:YES]; 924 } 925 return self; 926} 927 928- (void)_setAsideSubviews 929{ 930#ifdef TK_MAC_DEBUG 931 if (_subviewsSetAside || _savedSubviews) { 932 Tcl_Panic("TKContentView _setAsideSubviews called incorrectly"); 933 } 934#endif 935 _savedSubviews = _subviews; 936 _subviews = nil; 937 _subviewsSetAside = YES; 938 } 939 940 - (void)_restoreSubviews 941 { 942#ifdef TK_MAC_DEBUG 943 if (!_subviewsSetAside || _subviews) { 944 Tcl_Panic("TKContentView _restoreSubviews called incorrectly"); 945 } 946#endif 947 _subviews = _savedSubviews; 948 _savedSubviews = nil; 949 _subviewsSetAside = NO; 950} 951 952- (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView 953{ 954 [self _setAsideSubviews]; 955 [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect rectIsVisibleRectForView:visibleView topView:topView]; 956 [self _restoreSubviews]; 957} 958 959- (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect 960{ 961 BOOL needToSetAsideSubviews = !_subviewsSetAside; 962 if (needToSetAsideSubviews) { 963 [self _setAsideSubviews]; 964 } 965 [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect]; 966 if (needToSetAsideSubviews) { 967 [self _restoreSubviews]; 968 } 969} 970 971- (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView 972{ 973 [self _setAsideSubviews]; 974 [super _recursive:recurse displayRectIgnoringOpacity:displayRect inContext:context topView:topView]; 975 [self _restoreSubviews]; 976} 977 978- (void)_lightWeightRecursiveDisplayInRect:(NSRect)visRect { 979 BOOL needToSetAsideSubviews = !_subviewsSetAside; 980 if (needToSetAsideSubviews) { 981 [self _setAsideSubviews]; 982 } 983 [super _lightWeightRecursiveDisplayInRect:visRect]; 984 if (needToSetAsideSubviews) { 985 [self _restoreSubviews]; 986 } 987} 988 989- (BOOL)_drawRectIfEmpty { 990 /* 991 * Our -drawRect manages subview drawing directly, so it needs to be called 992 * even if the area to be redrawn is completely obscured by subviews. 993 */ 994 return YES; 995} 996 997- (void)_drawRect:(NSRect)inRect clip:(BOOL)clip { 998#ifdef TK_MAC_DEBUG_DRAWING 999 TKLog(@"-[%@(%p) %s%@]", [self class], self, _cmd, NSStringFromRect(inRect)); 1000#endif 1001 BOOL subviewsWereSetAside = _subviewsSetAside; 1002 if (subviewsWereSetAside) { 1003 [self _restoreSubviews]; 1004 } 1005 [super _drawRect:inRect clip:clip]; 1006 if (subviewsWereSetAside) { 1007 [self _setAsideSubviews]; 1008 } 1009} 1010 1011@end 1012 1013/* 1014 * Local Variables: 1015 * mode: c 1016 * c-basic-offset: 4 1017 * fill-column: 79 1018 * coding: utf-8 1019 * End: 1020 */ 1021