Tk Source Code

Artifact [224de9c9]
Login

Artifact 224de9c9f8b08de46c2cb9d72cfb24394abe9e6591767062becc13d5d87f0cc1:

Attachment "06d8246baf-postponedSNDIR.diff" to ticket [06d8246b] added by chrstphrchvz 2020-05-03 05:21:14.
diff --git macosx/tkMacOSXDraw.c macosx/tkMacOSXDraw.c
index e56e6665d..bad423261 100644
--- macosx/tkMacOSXDraw.c
+++ macosx/tkMacOSXDraw.c
@@ -1634,34 +1634,40 @@ TkMacOSXSetupDrawingContext(
 	 * and belongs to the view.  Validity can only be guaranteed inside of
 	 * a view's drawRect or setFrame methods.  The isDrawing attribute
 	 * tells us whether we are being called from one of those methods.
+	 * The region to be drawn into must also intersect with the area
+	 * for the current drawRect call.
 	 *
 	 * 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.
 	 */
 
-	if (![NSApp isDrawing] || view != [NSView focusView]) {
-	    NSRect bounds = [view bounds];
-	    NSRect dirtyNS = bounds;
-	    if ([NSEvent pressedMouseButtons]) {
-		[view setNeedsDisplay:YES];
-	    } else {
-		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];
-	    }
+	NSRect bounds = [view bounds];
+	NSRect dirtyNS = bounds;
+	if (dc.clipRgn) {
+	    // Don't bother initializing; will be overwritten with dc.clipRgn
+	    //CGRect dirtyCG = NSRectToCGRect(dirtyNS);
+	    CGRect dirtyCG;
+	    HIShapeGetBounds(dc.clipRgn, &dirtyCG);
+	    CGAffineTransform t = {
+		.a = 1, .b = 0, .c = 0, .d = -1, .tx = 0,
+		.ty = dirtyNS.size.height
+	    };
+	    dirtyNS = NSRectToCGRect(CGRectApplyAffineTransform(dirtyCG, t));
+	}
+	if (![NSApp isDrawing]) {
+	    [view setNeedsDisplayInRect:dirtyNS];
 	    canDraw = false;
+	} else if (view != [NSView focusView] || ![view needsToDrawRect:dirtyNS]) {
+	    // Try calling setNeedsDisplayInRect: later when not in drawRect.
+	    TkMacOSXPostponedSNDIR *holder = ckalloc(sizeof(TkMacOSXPostponedSNDIR));
+	    holder->view = view;
+	    holder->dirtyRect = dirtyNS;
+	    Tcl_CreateTimerHandler(0, TkMacOSXDoPostponedSNDIR, holder);
+	    canDraw = false;
+	}
+	if (!canDraw) {
 	    goto end;
 	}
 
@@ -1823,6 +1829,41 @@ TkMacOSXRestoreDrawingContext(
 #endif /* TK_MAC_DEBUG */
 }
 
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkMacOSXDoPostponedSNDIR --
+ *
+ *	Check whether currently in drawRect:, and if not, do the call to
+ *	[view setNeedsDisplayInRect:dirtyRect] which was postponed either in
+ *	TkMacOSXSetupDrawingContext, or by an earlier TkMacOSXDoPostponedSNDIR
+ *	attempt, due to already being in drawRect:. Otherwise if in drawRect:,
+ *	postpone again.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Either setNeedsDisplayInRect: is called and the holder for the call's
+ *	arguments is freed, or it is postponed again with a new timer handler.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkMacOSXDoPostponedSNDIR(
+    ClientData clientData)
+{
+    TkMacOSXPostponedSNDIR *holder = clientData;
+    if (![NSApp isDrawing]) {
+	[holder->view setNeedsDisplayInRect:holder->dirtyRect];
+	ckfree(holder);
+    } else {
+	// Postpone again
+	Tcl_CreateTimerHandler(0, TkMacOSXDoPostponedSNDIR, holder);
+    }
+}
+
 /*
  *----------------------------------------------------------------------
  *
diff --git macosx/tkMacOSXInt.h macosx/tkMacOSXInt.h
index 9cb75d2ba..a91cb57f2 100644
--- macosx/tkMacOSXInt.h
+++ macosx/tkMacOSXInt.h
@@ -186,6 +186,12 @@ MODULE_SCOPE void TkpFreeGCCache(GC gc);
 #define TK_LAYOUT_WITH_BASE_CHUNKS	1
 #define TK_DRAW_IN_CONTEXT		1
 
+// Structure to hold arguments for postponed setNeedsDisplayInRect: calls
+typedef struct {
+    NSView *view;
+    NSRect dirtyRect;
+} TkMacOSXPostponedSNDIR;
+
 /*
  * Prototypes of internal procs not in the stubs table.
  */
@@ -204,6 +210,7 @@ MODULE_SCOPE Bool TkpAppIsDrawing(void);
 MODULE_SCOPE void TkpDisplayWindow(Tk_Window tkwin);
 MODULE_SCOPE Bool TkTestLogDisplay(void);
 MODULE_SCOPE Bool TkMacOSXInDarkMode(Tk_Window tkwin);
+MODULE_SCOPE void TkMacOSXDoPostponedSNDIR(ClientData clientData);
 
 /*
  * Include the stubbed internal platform-specific API.