From 8bfe6e9b964fb48650e31fac01c29de41ba63d44 Mon Sep 17 00:00:00 2001 From: culler Date: Sat, 30 May 2020 23:13:52 +0000 Subject: A partly but not completely successful attempt to control how drawRect is run. --- generic/tkCanvas.c | 3 + macosx/tkMacOSXDraw.c | 40 ++++++------- macosx/tkMacOSXNotify.c | 115 ++++++++++++++++++++++++++++++++++--- macosx/tkMacOSXPrivate.h | 9 ++- macosx/tkMacOSXSubwindows.c | 73 ++++++++++++++++++------ macosx/tkMacOSXWindowEvent.c | 133 ++++++++++++++++++++----------------------- macosx/tkMacOSXWm.c | 5 +- 7 files changed, 256 insertions(+), 122 deletions(-) diff --git a/generic/tkCanvas.c b/generic/tkCanvas.c index 4d68ade..941e7d2 100644 --- a/generic/tkCanvas.c +++ b/generic/tkCanvas.c @@ -2577,6 +2577,9 @@ DisplayCanvas( #else canvasPtr->drawableXOrigin = canvasPtr->xOrigin; canvasPtr->drawableYOrigin = canvasPtr->yOrigin; +#if 0 + TkpAppCanDraw(tkwin); +#endif pixmap = Tk_WindowId(tkwin); TkpClipDrawableToRect(Tk_Display(tkwin), pixmap, screenX1 - canvasPtr->xOrigin, screenY1 - canvasPtr->yOrigin, diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index e56e666..d2d72e4 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -174,7 +174,9 @@ TkMacOSXBitmapRepFromDrawableRect( if (cg_image) { CGImageRelease(cg_image); } - } else if ((view = TkMacOSXDrawableView(mac_drawable)) != NULL) { + } else if (TkMacOSXDrawableView(mac_drawable) != NULL) { + TKContentView *tkview = (TKContentView *)view; + /* * Convert Tk top-left to NSView bottom-left coordinates. */ @@ -197,7 +199,8 @@ TkMacOSXBitmapRepFromDrawableRect( [view cacheDisplayInRect:view_rect toBitmapImageRep:bitmap_rep]; } else { TkMacOSXDbgMsg("No CGContext - cannot copy from screen to bitmap."); - [view setNeedsDisplay:YES]; + [tkview setTkNeedsDisplay:YES]; + [tkview setTkDirtyRect:[tkview bounds]]; return NULL; } } else { @@ -1622,7 +1625,7 @@ TkMacOSXSetupDrawingContext( if (dc.context) { dc.portBounds = clipBounds = CGContextGetClipBoundingBox(dc.context); } else if (win) { - NSView *view = TkMacOSXDrawableView(macDraw); + TKContentView *view = (TKContentView *)TkMacOSXDrawableView(macDraw); if (!view) { Tcl_Panic("TkMacOSXSetupDrawingContext(): " @@ -1636,31 +1639,22 @@ TkMacOSXSetupDrawingContext( * tells us whether we are being called from one of those methods. * * If the CGContext is not valid then we mark our view as needing - * display in the bounding rectangle of the clipping region and - * return failure. That rectangle should get drawn in a later call - * to drawRect. - * - * As an exception to the above, if mouse buttons are pressed at the - * moment when we fail to obtain a valid context we schedule the entire - * view for a redraw rather than just the clipping region. The purpose - * of this is to make sure that scrollbars get updated correctly. + * display. We could try to optimize by computing a smaller dirty rect + * here. */ if (![NSApp isDrawing] || view != [NSView focusView]) { - NSRect bounds = [view bounds]; - NSRect dirtyNS = bounds; - if ([NSEvent pressedMouseButtons]) { - [view setNeedsDisplay:YES]; - } else { + NSRect dirtyRect = [view bounds]; + if (dc.clipRgn) { + CGRect clipRect; CGAffineTransform t = { .a = 1, .b = 0, .c = 0, .d = -1, .tx = 0, - .ty = dirtyNS.size.height}; - if (dc.clipRgn) { - CGRect dirtyCG = NSRectToCGRect(dirtyNS); - HIShapeGetBounds(dc.clipRgn, &dirtyCG); - dirtyNS = NSRectToCGRect(CGRectApplyAffineTransform(dirtyCG, t)); - } - [view setNeedsDisplayInRect:dirtyNS]; + .ty = dirtyRect.size.height}; + HIShapeGetBounds(dc.clipRgn, &clipRect); + clipRect = CGRectApplyAffineTransform(clipRect, t); + dirtyRect = NSRectFromCGRect(clipRect); } + [view setTkNeedsDisplay:YES]; + [view setTkDirtyRect:NSUnionRect([view tkDirtyRect], dirtyRect)]; canDraw = false; goto end; } diff --git a/macosx/tkMacOSXNotify.c b/macosx/tkMacOSXNotify.c index 985d7bc..f8ac50d 100644 --- a/macosx/tkMacOSXNotify.c +++ b/macosx/tkMacOSXNotify.c @@ -299,6 +299,70 @@ TkMacOSXNotifyExitHandler( /* *---------------------------------------------------------------------- * + * TkMacOSXDrawAllViews -- + * + * This static function is meant to be run as an idle task. It attempts + * to redraw all views which have the tkNeedsDisplay property set to YES. + * This relies on a feature of [NSApp nextEventMatchingMask: ...] which + * is undocumented, namely that it sometimes blocks and calls drawRect + * for all views that need display before it returns. We call it with + * deQueue=NO so that it will not change anything on the AppKit event + * queue, because we only want the side effect that it runs + * drawRect. This function is the only place where NSViews get the + * needsDisplay property set to YES. + * + * The reason for running this as an idle task is to try to arrange that + * all widgets will be fully configured before they are drawn. Any idle + * tasks that might reconfigure them should be higher on the idle queue, + * so they should be run before the display procs are run by drawRect. + * + * If this is called directly with non-NULL clientData parameter then the + * int which it references will be set to the number of windows that need + * display, but the needsDisplay property of those windows will not be + * changed. + * + * Results: + * None. + * + * Side effects: + * Parts of windows my get redrawn. + * + *---------------------------------------------------------------------- + */ + +static void +TkMacOSXDrawAllViews( + ClientData clientData) +{ + int count = 0, *dirtyCount = (int *)clientData; + + for (NSWindow *window in [NSApp windows]) { + if ([[window contentView] isMemberOfClass:[TKContentView class]]) { + TKContentView *view = [window contentView]; + if ([view tkNeedsDisplay]) { + count++; + if (dirtyCount) { + continue; + } + [view setNeedsDisplayInRect:[view tkDirtyRect]]; + } + } else { + [window displayIfNeeded]; + } + } + if (dirtyCount) { + *dirtyCount = count; + } + + [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantPast] + inMode:GetRunLoopMode(TkMacOSXGetModalSession()) + dequeue:NO]; +} + +/* + *---------------------------------------------------------------------- + * * TkMacOSXEventsSetupProc -- * * This procedure implements the setup part of the MacOSX event source. It @@ -334,11 +398,13 @@ TkMacOSXEventsSetupProc( static const Tcl_Time zeroBlockTime = { 0, 0 }; [NSApp _resetAutoreleasePool]; - /* - * Call this with dequeue=NO -- just checking if the queue is empty. - */ + /* + * Call this with dequeue=NO to see if there are any events. If so, + * we set the block time to 0 and stop the heartbeat. Tcl_DoOneEvent + * will call the check proc to collect the events and translate them + * into XEvents. But also, drawRect may run. + */ - while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} NSEvent *currentEvent = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] @@ -353,7 +419,7 @@ TkMacOSXEventsSetupProc( } else if (!havePeriodicEvents){ /* - * When the user is not generating events we schedule a "hearbeat" + * When the user is not generating events we schedule a "heartbeat" * event to fire every 0.1 seconds. This helps to make the vwait * command more responsive when there is no user input, e.g. when * running the test suite. @@ -362,6 +428,12 @@ TkMacOSXEventsSetupProc( havePeriodicEvents = YES; [NSEvent startPeriodicEventsAfterDelay:0.0 withPeriod:0.1]; } + + /* + * Without this, new windows are sometimes not completely rendered. + */ + while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} + TkMacOSXDrawAllViews(NULL); } } @@ -388,6 +460,7 @@ TkMacOSXEventsCheckProc( int flags) { NSString *runloopMode = [[NSRunLoop currentRunLoop] currentMode]; + int eventsFound = 0; /* * runloopMode will be nil if we are in a Tcl event loop. @@ -408,7 +481,6 @@ TkMacOSXEventsCheckProc( [NSApp _lockAutoreleasePool]; do { - while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} modalSession = TkMacOSXGetModalSession(); testEvent = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] @@ -422,12 +494,12 @@ TkMacOSXEventsCheckProc( if (testEvent && [[testEvent window] inLiveResize]) { break; } - while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} currentEvent = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:GetRunLoopMode(modalSession) dequeue:YES]; if (currentEvent) { + /* * Generate Xevents. */ @@ -436,9 +508,12 @@ TkMacOSXEventsCheckProc( NSEvent *processedEvent = [NSApp tkProcessEvent:currentEvent]; Tcl_SetServiceMode(oldServiceMode); if (processedEvent) { + eventsFound++; + #ifdef TK_MAC_DEBUG_EVENTS TKLog(@" event: %@", currentEvent); #endif + if (modalSession) { [NSApp _modalSession:modalSession sendEvent:currentEvent]; } else { @@ -446,6 +521,7 @@ TkMacOSXEventsCheckProc( } } } else { + break; } } while (1); @@ -453,8 +529,31 @@ TkMacOSXEventsCheckProc( /* * Now we can unlock the pool. */ - [NSApp _unlockAutoreleasePool]; + + if (eventsFound == 0) { + + /* + * We found no events for Tcl in this iteration of the Tcl event + * loop, so it should proceed to servicing idle tasks. We add an + * idle task to the end of the idle queue which will redisplay all + * of our dirty windows. We want this to happen after all other + * idle tasks have run so that all widgets will be configured + * before they are displayed. The drawRect method "borrows" the + * idle queue while drawing views. That is, it sends expose events + * which cause display procs to be posted as idle tasks and then + * runs an inner event loop to processes those idle tasks. We are + * trying to arrange for the idle queue to be empty when it starts + * that process and empty when it finishes. + */ + + int dirtyCount = 0; + TkMacOSXDrawAllViews(&dirtyCount); + if (dirtyCount > 0) { + Tcl_CancelIdleCall(TkMacOSXDrawAllViews, NULL); + Tcl_DoWhenIdle(TkMacOSXDrawAllViews, NULL); + } + } } } diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h index be69fcd..fdd07ed 100644 --- a/macosx/tkMacOSXPrivate.h +++ b/macosx/tkMacOSXPrivate.h @@ -307,6 +307,8 @@ MODULE_SCOPE int TkMacOSXRegisterServiceWidgetObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); MODULE_SCOPE unsigned TkMacOSXAddVirtual(unsigned int keycode); +MODULE_SCOPE void TkMacOSXWinNSBounds(TkWindow *winPtr, NSView *view, + NSRect *bounds); #pragma mark Private Objective-C Classes @@ -418,8 +420,12 @@ VISIBILITY_HIDDEN @private NSString *privateWorkingText; Bool _needsRedisplay; + Bool _tkNeedsDisplay; + NSRect _tkDirtyRect; } @property Bool needsRedisplay; +@property Bool tkNeedsDisplay; +@property NSRect tkDirtyRect; @end @interface TKContentView(TKKeyEvent) @@ -428,7 +434,8 @@ VISIBILITY_HIDDEN @end @interface TKContentView(TKWindowEvent) -- (void) generateExposeEvents: (HIShapeRef) shape; + //(HIShapeRef) shape; +- (void) generateExposeEvents: (NSRect) rect; - (void) tkToolbarButton: (id) sender; @end diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index 6602564..53e5bf6 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -157,6 +157,7 @@ XMapWindow( winPtr->flags |= TK_MAPPED; if (Tk_IsTopLevel(winPtr)) { if (!Tk_IsEmbedded(winPtr)) { + TKContentView *view = [win contentView]; /* * We want to activate Tk when a toplevel is mapped but we must not @@ -169,7 +170,8 @@ XMapWindow( TkMacOSXApplyWindowAttributes(winPtr, win); [win setExcludedFromWindowsMenu:NO]; [NSApp activateIgnoringOtherApps:NO]; - [[win contentView] setNeedsDisplay:YES]; + [view setTkNeedsDisplay:YES]; + [view setTkDirtyRect: [view bounds]]; if ([win canBecomeKeyWindow]) { [win makeKeyAndOrderFront:NSApp]; } else { @@ -215,7 +217,9 @@ XMapWindow( if ([NSApp isDrawing]) { [[win contentView] setNeedsRedisplay:YES]; } else { - [[win contentView] setNeedsDisplay:YES]; + TKContentView *view = [win contentView]; + [view setTkNeedsDisplay:YES]; + [view setTkDirtyRect:[view bounds]]; } /* @@ -332,7 +336,9 @@ XUnmapWindow( if ([NSApp isDrawing]) { [[win contentView] setNeedsRedisplay:YES]; } else { - [[win contentView] setNeedsDisplay:YES]; + TKContentView *view = [win contentView]; + [view setTkNeedsDisplay:YES]; + [view setTkDirtyRect:[view bounds]]; } return Success; } @@ -986,8 +992,9 @@ InvalViewRect( void *ref) { static CGAffineTransform t; - NSView *view = ref; - + TKContentView *view = ref; + NSRect dirtyRect; + if (!view) { return paramErr; } @@ -997,8 +1004,9 @@ InvalViewRect( NSHeight([view bounds])); break; case kHIShapeEnumerateRect: - [view setNeedsDisplayInRect:NSRectFromCGRect( - CGRectApplyAffineTransform(*rect, t))]; + dirtyRect = NSRectFromCGRect(CGRectApplyAffineTransform(*rect, t)); + [view setTkNeedsDisplay:YES]; + [view setTkDirtyRect:NSUnionRect([view tkDirtyRect], dirtyRect)]; break; } return noErr; @@ -1275,16 +1283,15 @@ TkMacOSXInvalClipRgns( * * TkMacOSXWinBounds -- * - * Given a Tk window this function determines the windows bounds in - * relation to the Macintosh window's coordinate system. This is also the - * same coordinate system as the Tk toplevel window in which this window - * is contained. + * Given a Tk window this function determines the window's bounds in + * the coordinate system of the Tk toplevel window in which this window + * is contained. This fills in a Rect struct. * * Results: * None. * * Side effects: - * None. + * Fills in a Rect. * *---------------------------------------------------------------------- */ @@ -1307,16 +1314,15 @@ TkMacOSXWinBounds( * * TkMacOSXWinCGBounds -- * - * Given a Tk window this function determines the windows bounds in - * relation to the Macintosh window's coordinate system. This is also the - * same coordinate system as the Tk toplevel window in which this window - * is contained. + * Given a Tk window this function determines the window's bounds in + * the coordinate system of the Tk toplevel window in which this window + * is contained. This fills in a CGRect struct. * * Results: * None. * * Side effects: - * None. + * Fill in a CGRect. * *---------------------------------------------------------------------- */ @@ -1331,6 +1337,39 @@ TkMacOSXWinCGBounds( bounds->size.width = winPtr->changes.width; bounds->size.height = winPtr->changes.height; } +/* + *---------------------------------------------------------------------- + * + * TkMacOSXWinNSBounds -- + * + * Given a Tk window this function determines the window's bounds in + * the coordinate system of the TKContentView in which this Tk window + * is contained, which has the origin at the lower left corner. This + * fills in an NSRect struct and requires the TKContentView as a + * parameter + * + * Results: + * None. + * + * Side effects: + * Fills in an NSRect. + * + *---------------------------------------------------------------------- + */ + +void +TkMacOSXWinNSBounds( + TkWindow *winPtr, + NSView *view, + NSRect *bounds) +{ + bounds->size.width = winPtr->changes.width; + bounds->size.height = winPtr->changes.height; + bounds->origin.x = winPtr->privatePtr->xOff; + bounds->origin.y = ([view bounds].size.height - + bounds->size.height - + winPtr->privatePtr->yOff); +} /* *---------------------------------------------------------------------- diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 76b2b04..6e8f7c4 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -30,8 +30,8 @@ * Declaration of functions used only in this file */ -static int GenerateUpdates(HIShapeRef updateRgn, - CGRect *updateBounds, TkWindow *winPtr); +static int GenerateUpdates( + CGRect *updateBounds, TkWindow *winPtr); static int GenerateActivateEvents(TkWindow *winPtr, int activeFlag); static void DoWindowActivate(ClientData clientData); @@ -407,16 +407,29 @@ extern NSString *NSWindowDidOrderOffScreenNotification; int TkpAppCanDraw(Tk_Window tkwin) { - if (![NSApp isDrawing]) { - return 0; - } + int result; if (tkwin) { TkWindow *winPtr = (TkWindow *)tkwin; - NSView *view = TkMacOSXDrawableView(winPtr->privatePtr); - return (view == [NSView focusView]); + TKContentView *view = (TKContentView *) TkMacOSXDrawableView( + winPtr->privatePtr); + result = ([NSApp isDrawing] && view == [NSView focusView]); +#if 0 + printf("TkAppCanDraw: %s %d %d \n", Tk_PathName(tkwin), + [NSApp isDrawing], (view == [NSView focusView])); + if (!result) { + NSRect dirtyRect; + TkMacOSXWinNSBounds(winPtr, view, &dirtyRect); + printf("TkpAppCanDraw: dirtyRect for %s is %s\n", + Tk_PathName(tkwin), + NSStringFromRect(dirtyRect).UTF8String); + [view setTkNeedsDisplay:YES]; + [view setTkDirtyRect:dirtyRect]; + } +#endif } else { - return 1; + result = [NSApp isDrawing]; } + return result; } /* @@ -424,9 +437,9 @@ TkpAppCanDraw(Tk_Window tkwin) { * * GenerateUpdates -- * - * Given a Macintosh update region and a Tk window this function geneates + * Given an update rectangle and a Tk window, this function geneates * an X Expose event for the window if it meets the update region. The - * function will then recursivly have each damaged window generate Expose + * function will then recursively have each damaged window generate Expose * events for its child windows. * * Results: @@ -440,39 +453,23 @@ TkpAppCanDraw(Tk_Window tkwin) { static int GenerateUpdates( - HIShapeRef updateRgn, CGRect *updateBounds, TkWindow *winPtr) { TkWindow *childPtr; XEvent event; CGRect bounds, damageBounds; - HIShapeRef boundsRgn, damageRgn; TkMacOSXWinCGBounds(winPtr, &bounds); if (!CGRectIntersectsRect(bounds, *updateBounds)) { return 0; } - if (!HIShapeIntersectsRect(updateRgn, &bounds)) { - return 0; - } /* * Compute the bounding box of the area that the damage occured in. */ - boundsRgn = HIShapeCreateWithRect(&bounds); - damageRgn = HIShapeCreateIntersection(updateRgn, boundsRgn); - if (HIShapeIsEmpty(damageRgn)) { - CFRelease(damageRgn); - CFRelease(boundsRgn); - return 0; - } - HIShapeGetBounds(damageRgn, &damageBounds); - - CFRelease(damageRgn); - CFRelease(boundsRgn); - + damageBounds = CGRectIntersection(bounds, *updateBounds); event.xany.serial = LastKnownRequestProcessed(Tk_Display(winPtr)); event.xany.send_event = false; event.xany.window = Tk_WindowId(winPtr); @@ -486,7 +483,7 @@ GenerateUpdates( Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); #ifdef TK_MAC_DEBUG_DRAWING - TKLog(@"Expose %p {{%d, %d}, {%d, %d}}", event.xany.window, event.xexpose.x, + TKLog(@"Exposed %p {{%d, %d}, {%d, %d}}", event.xany.window, event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height); #endif @@ -499,7 +496,7 @@ GenerateUpdates( if (!Tk_IsMapped(childPtr) || Tk_IsTopLevel(childPtr)) { continue; } - GenerateUpdates(updateRgn, updateBounds, childPtr); + GenerateUpdates(updateBounds, childPtr); } /* @@ -509,7 +506,7 @@ GenerateUpdates( if (Tk_IsContainer(winPtr)) { childPtr = TkpGetOtherWindow(winPtr); if (childPtr != NULL && Tk_IsMapped(childPtr)) { - GenerateUpdates(updateRgn, updateBounds, childPtr); + GenerateUpdates(updateBounds, childPtr); } /* @@ -928,10 +925,10 @@ ConfigureRestrictProc( */ static void -RedisplayView( +RedisplayView( ClientData clientdata) { - NSView *view = (NSView *) clientdata; + TKContentView *view = (TKContentView *) clientdata; /* * Make sure that we are not trying to displaying a view that no longer @@ -941,7 +938,8 @@ RedisplayView( for (NSWindow *w in [NSApp windows]) { if ([w contentView] == view) { - [view setNeedsDisplay:YES]; + [view setTkNeedsDisplay:YES]; + [view setTkDirtyRect:[view bounds]]; break; } } @@ -951,13 +949,13 @@ RedisplayView( - (void) drawRect: (NSRect) rect { - const NSRect *rectsBeingDrawn; - NSInteger rectsBeingDrawnCount; #ifdef TK_MAC_DEBUG_DRAWING TkWindow *winPtr = TkMacOSXGetTkWindow([self window]); - if (winPtr) fprintf(stderr, "drawRect: drawing %s\n", - Tk_PathName(winPtr)); + if (winPtr) { + fprintf(stderr, "drawRect: drawing %s in %s\n", + Tk_PathName(winPtr), NSStringFromRect(rect).UTF8String); + } #endif /* @@ -974,23 +972,11 @@ RedisplayView( [NSApp setIsDrawing: YES]; - [self getRectsBeingDrawn:&rectsBeingDrawn count:&rectsBeingDrawnCount]; - CGFloat height = [self bounds].size.height; - HIMutableShapeRef drawShape = HIShapeCreateMutable(); - - while (rectsBeingDrawnCount--) { - CGRect r = NSRectToCGRect(*rectsBeingDrawn++); - #ifdef TK_MAC_DEBUG_DRAWING - fprintf(stderr, "drawRect: %dx%d@(%d,%d)\n", (int)r.size.width, - (int)r.size.height, (int)r.origin.x, (int)r.origin.y); + fprintf(stderr, "drawRect: %s\n", NSStringFromRect(rect).UTF8String; #endif - r.origin.y = height - (r.origin.y + r.size.height); - HIShapeUnionWithRect(drawShape, &r); - } - [self generateExposeEvents:(HIShapeRef)drawShape]; - CFRelease(drawShape); + [self generateExposeEvents:rect]; [NSApp setIsDrawing: NO]; if ([self needsRedisplay]) { @@ -998,6 +984,9 @@ RedisplayView( Tcl_DoWhenIdle(RedisplayView, self); } + [self setTkNeedsDisplay:NO]; + [self setTkDirtyRect:NSZeroRect]; + #ifdef TK_MAC_DEBUG_DRAWING fprintf(stderr, "drawRect: done.\n"); #endif @@ -1056,9 +1045,7 @@ RedisplayView( * Generate and process expose events to redraw the window. */ - HIRect bounds = NSRectToCGRect([self bounds]); - HIShapeRef shape = HIShapeCreateWithRect(&bounds); - [self generateExposeEvents: shape]; + [self generateExposeEvents: [self bounds]]; [w displayIfNeeded]; /* @@ -1076,11 +1063,11 @@ RedisplayView( * pending idle events are processed so the drawing will actually take place. */ -- (void) generateExposeEvents: (HIShapeRef) shape +- (void) generateExposeEvents: (NSRect) rect { unsigned long serial; - CGRect updateBounds; int updatesNeeded; + CGRect updateBounds; TkWindow *winPtr = TkMacOSXGetTkWindow([self window]); ClientData oldArg; Tk_RestrictProc *oldProc; @@ -1089,20 +1076,18 @@ RedisplayView( } /* - * Generate Tk Expose events. - */ - - HIShapeGetBounds(shape, &updateBounds); - - /* - * All of these events will share the same serial number. + * Generate Tk Expose events. All of these events will share the same + * serial number. */ - - serial = LastKnownRequestProcessed(Tk_Display(winPtr)); - updatesNeeded = GenerateUpdates(shape, &updateBounds, winPtr); - + + updateBounds = NSRectToCGRect(rect); + updateBounds.origin.y = ([self bounds].size.height - updateBounds.origin.y + - updateBounds.size.height); + updatesNeeded = GenerateUpdates(&updateBounds, winPtr); if (updatesNeeded) { + serial = LastKnownRequestProcessed(Tk_Display(winPtr)); + /* * First process all of the Expose events. */ @@ -1120,9 +1105,10 @@ RedisplayView( * effect.) * * Fortunately, Tk schedules all drawing to be done while Tcl is idle. - * So we can do the drawing by processing all of the idle events that - * were created when the expose events were processed. + * So we can do the drawing now by processing all of the idle events + * that were created when the expose events were processed. */ + while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} } } @@ -1202,10 +1188,15 @@ RedisplayView( - (BOOL) isOpaque { NSWindow *w = [self window]; - return (w && (([w styleMask] & NSTexturedBackgroundWindowMask) || - ![w isOpaque]) ? NO : YES); + return (w && (([w styleMask] & NSTexturedBackgroundWindowMask) || + ![w isOpaque]) ? NO : YES); } +/* + * On Catalina this is never called and drawRect clips to the rect that + * is passed to it by AppKit. + */ + - (BOOL) wantsDefaultClipping { return NO; diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index cab2b9a..1b51479 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -6201,13 +6201,14 @@ TkpRedrawWidget(Tk_Window tkwin) { } w = TkMacOSXDrawableWindow(winPtr->window); if (w) { - NSView *view = [w contentView]; + TKContentView *view = [w contentView]; TkMacOSXWinBounds(winPtr, &tkBounds); bounds = NSMakeRect(tkBounds.left, [view bounds].size.height - tkBounds.bottom, tkBounds.right - tkBounds.left, tkBounds.bottom - tkBounds.top); - [view setNeedsDisplayInRect:bounds]; + [view setTkNeedsDisplay:YES]; + [view setTkDirtyRect:bounds]; } } -- cgit v0.12