Tk Source Code

Check-in [2793972a]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:merge trunk, fix conflicts in tkTextDisp.c by propagating [0ffbdfe2] (the fix for [61cb40b01b]) to the revised text widget.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | revised_text | tip-466
Files: files | file ages | folders
SHA3-256: 2793972a39e72462b08837641cc44e6f6272c049becaeace52b2ee2bd205e9a8
User & Date: fvogel 2025-03-01 10:12:00.267
References
2025-03-01
10:18 Ticket [61cb40b0] Scrolling a canvas or text with embedded windows can cause artifacts with tcl/tk 9.0.1 Aqua status still Closed with 4 other changes artifact: b4e89b9c user: fvogel
Context
2025-03-09
17:27
merge trunk check-in: 8a6f1edf user: fvogel tags: revised_text, tip-466
2025-03-01
10:12
merge trunk, fix conflicts in tkTextDisp.c by propagating [0ffbdfe2] (the fix for [61cb40b01b]) to the revised text widget. check-in: 2793972a user: fvogel tags: revised_text, tip-466
09:55
merge trunk check-in: fa3abf01 user: fvogel tags: revised_text, tip-466
2025-02-28
15:39
Remove a couple of comments added as reminders. check-in: b1b79d93 user: culler tags: trunk, main
Changes
Unified Diff Ignore Whitespace Patch
Changes to generic/tkCanvas.c.
3176
3177
3178
3179
3180
3181
3182






















3183
3184
3185
3186
3187
3188
3189
#else
	canvasPtr->drawableXOrigin = canvasPtr->xOrigin;
	canvasPtr->drawableYOrigin = canvasPtr->yOrigin;
	pixmap = Tk_WindowId(tkwin);
	Tk_ClipDrawableToRect(Tk_Display(tkwin), pixmap,
		screenX1 - canvasPtr->xOrigin, screenY1 - canvasPtr->yOrigin,
		width, height);






















#endif /* TK_NO_DOUBLE_BUFFERING */

	/*
	 * Clear the area to be redrawn.
	 */

	XFillRectangle(Tk_Display(tkwin), pixmap, canvasPtr->pixmapGC,







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
#else
	canvasPtr->drawableXOrigin = canvasPtr->xOrigin;
	canvasPtr->drawableYOrigin = canvasPtr->yOrigin;
	pixmap = Tk_WindowId(tkwin);
	Tk_ClipDrawableToRect(Tk_Display(tkwin), pixmap,
		screenX1 - canvasPtr->xOrigin, screenY1 - canvasPtr->yOrigin,
		width, height);
	/*
	 * Call ItemDisplay for all window items.  This does not redraw the
	 * windows, but sets their position within the canvas, which ensures
	 * for macOS (the only platform which defines TK_NO_DOUBLE_BUFFERING)
	 * that the clipping region for the canvas gets updated before the
	 * background is painted by XFillRectangle.  Otherwise, when the
	 * background is filled the old locations of the window items will be
	 * clipped away, rather than the new locations, causing "ghost"
	 * windows to appear at the old locations.  Now that updateLayer is
	 * being used for macOS drawing it should be possible to stop
	 * maintaining clipping regions for all widgets.  When that happens
	 * this code can probably be removed.
	 */

	for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
		itemPtr = itemPtr->nextPtr) {
	    if (AlwaysRedraw(itemPtr)) {
		ItemDisplay(canvasPtr, itemPtr, pixmap,
			    screenX1, screenY1, width, height);
	    }
	}

#endif /* TK_NO_DOUBLE_BUFFERING */

	/*
	 * Clear the area to be redrawn.
	 */

	XFillRectangle(Tk_Display(tkwin), pixmap, canvasPtr->pixmapGC,
Changes to generic/tkTextDisp.c.
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
 *	between them, that are on the same line and use the same font and font
 *	size. Allocate the chars of all these chunks, the so-called "stretch",
 *	in a DString in the first chunk, the so-called "base chunk". Use the
 *	base chunk string for measuring and drawing, so that these actions are
 *	always performed with maximum context.
 *
 *	This is necessary for text rendering engines that provide ligatures
 *	and sub-pixel layout, like ATSU on Mac. If we don't do this, the
 *	measuring will change all the time, leading to an ugly "tremble and
 *	shiver" effect. This is because of the continuous splitting and
 *	re-merging of chunks that goes on in a text widget, when the cursor or
 *	the selection move.
 *
 * Side effects:
 *







|







96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
 *	between them, that are on the same line and use the same font and font
 *	size. Allocate the chars of all these chunks, the so-called "stretch",
 *	in a DString in the first chunk, the so-called "base chunk". Use the
 *	base chunk string for measuring and drawing, so that these actions are
 *	always performed with maximum context.
 *
 *	This is necessary for text rendering engines that provide ligatures
 *	and sub-pixel layout, like ATSU on macOS. If we don't do this, the
 *	measuring will change all the time, leading to an ugly "tremble and
 *	shiver" effect. This is because of the continuous splitting and
 *	re-merging of chunks that goes on in a text widget, when the cursor or
 *	the selection move.
 *
 * Side effects:
 *
8721
8722
8723
8724
8725
8726
8727


8728

8729
8730
8731
8732
8733
8734
8735
	 * Now scroll the lines. This may generate damage which we handle by
	 * calling TextInvalidateRegion to mark the display blocks as stale.
	 */

	damageRgn = TkCreateRegion();
	if (TkScrollWindow(textPtr->tkwin, dInfoPtr->scrollGC, MAX(0, dInfoPtr->x - extent1), oldY,
		dInfoPtr->maxX - dInfoPtr->x + extent1 + extent2, height, 0, y - oldY, damageRgn)) {


	    TextInvalidateRegion(textPtr, damageRgn);

	}
	DEBUG(stats.numCopies += 1);
	TkDestroyRegion(damageRgn);
    }

    /*
     * Clear the REDRAW_PENDING flag here. This is actually pretty tricky. We want to







>
>

>







8721
8722
8723
8724
8725
8726
8727
8728
8729
8730
8731
8732
8733
8734
8735
8736
8737
8738
	 * Now scroll the lines. This may generate damage which we handle by
	 * calling TextInvalidateRegion to mark the display blocks as stale.
	 */

	damageRgn = TkCreateRegion();
	if (TkScrollWindow(textPtr->tkwin, dInfoPtr->scrollGC, MAX(0, dInfoPtr->x - extent1), oldY,
		dInfoPtr->maxX - dInfoPtr->x + extent1 + extent2, height, 0, y - oldY, damageRgn)) {
#ifndef MACOSX_TK
	    /* No point in doing this on macOS. The DLines get redrawn anyway.*/
	    TextInvalidateRegion(textPtr, damageRgn);
#endif
	}
	DEBUG(stats.numCopies += 1);
	TkDestroyRegion(damageRgn);
    }

    /*
     * Clear the REDRAW_PENDING flag here. This is actually pretty tricky. We want to
8878
8879
8880
8881
8882
8883
8884
8885

8886
8887
8888
8889
8890
8891
8892
8893
8894
8895

8896
8897
8898
8899
8900
8901
8902
8903
8904
8905
8906
8907
8908
8909
8910
8911
8912
8913
8914
8915
8916
	    } else if (dInfoPtr->countWindows > 0 && dlPtr->chunkPtr) {
		/*
		 * On macOS we need to redisplay all embedded windows which
		 * were moved by the call to TkScrollWindows above.  This is
		 * not necessary on Unix or Windows because XScrollWindow will
		 * have included the bounding rectangles of all of these
		 * windows in the damage region.  The macosx implementation of
		 * TkScrollWindow does not do this.  It simply generates a

		 * damage region which is the scroll source rectangle minus
		 * the scroll destination rectangle.  This is because there is
		 * no efficient process available for iterating through the
		 * subwindows which meet the scrolled area.  (On Unix this is
		 * handled by GraphicsExpose events generated by XCopyArea and
		 * on Windows by ScrollWindowEx.  On macOS the low level
		 * scrolling is accomplished by calling [view scrollRect:by:].
		 * This method does not provide any damage information and, in
		 * any case, could not be aware of Tk windows which were not
		 * based on NSView objects.

		 *
		 * On the other hand, this loop is already iterating through
		 * all embedded windows which could possibly have been moved
		 * by the scrolling.  So it is as efficient to redisplay them
		 * here as it would have been if they had been redisplayed by
		 * the call to TextInvalidateRegion above.
		 */
#else
	    } else if (dInfoPtr->countWindows > 0
		    && dlPtr->chunkPtr
		    && (dlPtr->y < 0 || dlPtr->y + dlPtr->height > dInfoPtr->maxY)) {

		/*
		 * On platforms other than the Mac:
		 *
		 * It's the first or last DLine which are also overlapping the
		 * top or bottom of the window, but we decided above it wasn't
		 * necessary to display them (we were able to update them by
		 * scrolling). This is fine, except that if the lines contain
		 * any embedded windows, we must still call the display proc
		 * on them because they might need to be unmapped or they







|
>
|
|
<
<
|
|
<
<
|
<
>













|







8881
8882
8883
8884
8885
8886
8887
8888
8889
8890
8891


8892
8893


8894

8895
8896
8897
8898
8899
8900
8901
8902
8903
8904
8905
8906
8907
8908
8909
8910
8911
8912
8913
8914
8915
8916
	    } else if (dInfoPtr->countWindows > 0 && dlPtr->chunkPtr) {
		/*
		 * On macOS we need to redisplay all embedded windows which
		 * were moved by the call to TkScrollWindows above.  This is
		 * not necessary on Unix or Windows because XScrollWindow will
		 * have included the bounding rectangles of all of these
		 * windows in the damage region.  The macosx implementation of
		 * TkScrollWindow is not able to do this because no
		 * information about embedded windows is avaliable to it. So
		 * it simply generates a damage region which is the scroll
		 * source rectangle minus the scroll destination rectangle.


		 * (On Unix this is handled by GraphicsExpose events generated
		 * by XCopyArea and on Windows by ScrollWindowEx.  On macOS


		 * the embedded windows are not managed by the window manager,

		 * so there is no analogous technique available.)
		 *
		 * On the other hand, this loop is already iterating through
		 * all embedded windows which could possibly have been moved
		 * by the scrolling.  So it is as efficient to redisplay them
		 * here as it would have been if they had been redisplayed by
		 * the call to TextInvalidateRegion above.
		 */
#else
	    } else if (dInfoPtr->countWindows > 0
		    && dlPtr->chunkPtr
		    && (dlPtr->y < 0 || dlPtr->y + dlPtr->height > dInfoPtr->maxY)) {

		/*
		 * On platforms other than the macOS:
		 *
		 * It's the first or last DLine which are also overlapping the
		 * top or bottom of the window, but we decided above it wasn't
		 * necessary to display them (we were able to update them by
		 * scrolling). This is fine, except that if the lines contain
		 * any embedded windows, we must still call the display proc
		 * on them because they might need to be unmapped or they
8947
8948
8949
8950
8951
8952
8953








8954
8955
8956
8957
8958

8959
8960
8961
8962
8963
8964
8965
		    }
		    if (tkTextDebug) {
			char string[TK_POS_CHARS];

			TkrTextPrintIndex(textPtr, &dlPtr->index, string);
			LOG("tk_textEmbWinDisplay", string);
		    }








		    chunkPtr->layoutProcs->displayProc(textPtr, chunkPtr, x,
			    dlPtr->spaceAbove,
			    dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
			    dlPtr->baseline - dlPtr->spaceAbove, NULL,
			    (Drawable) None, dlPtr->y + dlPtr->spaceAbove);

		}
	    }
	}
#ifndef TK_NO_DOUBLE_BUFFERING
	Tk_FreePixmap(Tk_Display(textPtr->tkwin), pixmap);
#endif /* TK_NO_DOUBLE_BUFFERING */
    }







>
>
>
>
>
>
>
>





>







8947
8948
8949
8950
8951
8952
8953
8954
8955
8956
8957
8958
8959
8960
8961
8962
8963
8964
8965
8966
8967
8968
8969
8970
8971
8972
8973
8974
		    }
		    if (tkTextDebug) {
			char string[TK_POS_CHARS];

			TkrTextPrintIndex(textPtr, &dlPtr->index, string);
			LOG("tk_textEmbWinDisplay", string);
		    }
#ifdef MAC_OSX_TK
		    /* We need to redisplay the entire DLine so that the
		     * background of the line will not contain artifacts left
		     * by the scrolling.
		     */
		    
		    DisplayDLine(textPtr, dlPtr, NULL, pixmap);
#else		   
		    chunkPtr->layoutProcs->displayProc(textPtr, chunkPtr, x,
			    dlPtr->spaceAbove,
			    dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
			    dlPtr->baseline - dlPtr->spaceAbove, NULL,
			    (Drawable) None, dlPtr->y + dlPtr->spaceAbove);
#endif
		}
	    }
	}
#ifndef TK_NO_DOUBLE_BUFFERING
	Tk_FreePixmap(Tk_Display(textPtr->tkwin), pixmap);
#endif /* TK_NO_DOUBLE_BUFFERING */
    }
Changes to macosx/tkMacOSXWindowEvent.c.
956
957
958
959
960
961
962

963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981

982


983
984
985
986
987
988
989

- (BOOL) wantsUpdateLayer
{
    return YES;
}
- (void) updateLayer {
    CGContextRef context = self.tkLayerBitmapContext;

    if (context && ![NSApp tkWillExit]) {
	/*
	 * Create a CGImage by copying (probably using copy-on-write) the
	 * bitmap data of the CGBitmapContext that we have been using for
	 * drawing.  Then render that CGImage into the CALayer of this view by
	 * assigning a reference to the CGImage to the contents property of the
	 * layer. This will cause all drawing done since the last call to this
	 * function to become visible.
	 */

	CGImageRef newImg = CGBitmapContextCreateImage(context);
	self.layer.contents = (__bridge id) newImg;
	CGImageRelease(newImg); // will quickly leak memory if this is missing

	/*
	 * Run any pending widget display procs as part of the update.
	 * Without this there are black flashes when a window opens.
	 */


	while(Tcl_DoOneEvent(TCL_IDLE_EVENTS)){}


    }
}

- (void) viewDidChangeBackingProperties
{

    /*







>



















>
|
>
>







956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993

- (BOOL) wantsUpdateLayer
{
    return YES;
}
- (void) updateLayer {
    CGContextRef context = self.tkLayerBitmapContext;
    static bool initialized = NO;
    if (context && ![NSApp tkWillExit]) {
	/*
	 * Create a CGImage by copying (probably using copy-on-write) the
	 * bitmap data of the CGBitmapContext that we have been using for
	 * drawing.  Then render that CGImage into the CALayer of this view by
	 * assigning a reference to the CGImage to the contents property of the
	 * layer. This will cause all drawing done since the last call to this
	 * function to become visible.
	 */

	CGImageRef newImg = CGBitmapContextCreateImage(context);
	self.layer.contents = (__bridge id) newImg;
	CGImageRelease(newImg); // will quickly leak memory if this is missing

	/*
	 * Run any pending widget display procs as part of the update.
	 * Without this there are black flashes when a window opens.
	 */

	if (!initialized) {
	    while(Tcl_DoOneEvent(TCL_IDLE_EVENTS)){}
	    initialized = YES;
	}
    }
}

- (void) viewDidChangeBackingProperties
{

    /*
Changes to macosx/tkMacOSXWm.c.
7074
7075
7076
7077
7078
7079
7080
7081

7082
7083
7084
7085
7086
7087
7088
	Tk_MapWindow((Tk_Window)winPtr);
	[macWin deminiaturize:NSApp];
	[macWin orderFront:NSApp];
	TkMacOSXZoomToplevel(macWin, state == NormalState ? inZoomIn : inZoomOut);
    }

    /*
     * Make sure windows are updated after the state change too.

     */

    while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)){}
setStateEnd:
    return 1;
}








|
>







7074
7075
7076
7077
7078
7079
7080
7081
7082
7083
7084
7085
7086
7087
7088
7089
	Tk_MapWindow((Tk_Window)winPtr);
	[macWin deminiaturize:NSApp];
	[macWin orderFront:NSApp];
	TkMacOSXZoomToplevel(macWin, state == NormalState ? inZoomIn : inZoomOut);
    }

    /*
     * Make sure windows are updated after the state change too.  This is needed
     * in order for the event-9.11-20 tests to pass.
     */

    while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)){}
setStateEnd:
    return 1;
}