From f7df301427ff284aa23c342c5405e7eda1aa4e2d Mon Sep 17 00:00:00 2001 From: marc_culler Date: Mon, 15 Aug 2022 13:34:03 +0000 Subject: [PATCH 01/96] Christopher Chavez's CGImage-backed views. --- macosx/tkMacOSXDraw.c | 67 +++++++++++++------------ macosx/tkMacOSXImage.c | 94 ++++++++++++++++++++++++++---------- macosx/tkMacOSXInit.c | 12 +++++ macosx/tkMacOSXInt.h | 15 ++++++ macosx/tkMacOSXMenu.c | 7 ++- macosx/tkMacOSXNotify.c | 9 ++++ macosx/tkMacOSXPrivate.h | 17 ++++++- macosx/tkMacOSXRegion.c | 4 +- macosx/tkMacOSXSubwindows.c | 29 +++++++++-- macosx/tkMacOSXTest.c | 1 + macosx/tkMacOSXWindowEvent.c | 75 +++++++++++++++++++++++++++- 11 files changed, 259 insertions(+), 71 deletions(-) diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index aa3ed16196..4e8f57fccb 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -248,7 +248,6 @@ TkMacOSXGetCGContextForDrawable( if (macDraw && (macDraw->flags & TK_IS_PIXMAP) && !macDraw->context) { const size_t bitsPerComponent = 8; - size_t bitsPerPixel, bytesPerRow, len; CGColorSpaceRef colorspace = NULL; CGBitmapInfo bitmapInfo = #ifdef __LITTLE_ENDIAN__ @@ -256,25 +255,17 @@ TkMacOSXGetCGContextForDrawable( #else kCGBitmapByteOrderDefault; #endif - char *data; CGRect bounds = CGRectMake(0, 0, macDraw->size.width, macDraw->size.height); if (macDraw->flags & TK_IS_BW_PIXMAP) { - bitsPerPixel = 8; bitmapInfo = (CGBitmapInfo)kCGImageAlphaOnly; } else { colorspace = CGColorSpaceCreateDeviceRGB(); - bitsPerPixel = 32; bitmapInfo |= kCGImageAlphaPremultipliedFirst; } - bytesPerRow = ((size_t) - macDraw->size.width * bitsPerPixel + 127) >> 3 & ~15; - len = macDraw->size.height * bytesPerRow; - data = (char *)ckalloc(len); - bzero(data, len); - macDraw->context = CGBitmapContextCreate(data, macDraw->size.width, - macDraw->size.height, bitsPerComponent, bytesPerRow, + macDraw->context = CGBitmapContextCreate(NULL, macDraw->size.width, + macDraw->size.height, bitsPerComponent, 0, colorspace, bitmapInfo); if (macDraw->context) { CGContextClearRect(macDraw->context, bounds); @@ -292,7 +283,8 @@ TkMacOSXGetCGContextForDrawable( * * TkMacOSXDrawCGImage -- * - * Draw CG image into drawable. + * Draw CG image into drawable. The entire image is used, and will + * be rescaled if its dimensions do not equal dstBounds.size. * * Results: * None. @@ -311,25 +303,11 @@ TkMacOSXDrawCGImage( CGImageRef image, unsigned long imageForeground, unsigned long imageBackground, - CGRect imageBounds, - CGRect srcBounds, CGRect dstBounds) { MacDrawable *macDraw = (MacDrawable *)d; if (macDraw && context && image) { - CGImageRef subImage = NULL; - - if (!CGRectEqualToRect(imageBounds, srcBounds)) { - if (!CGRectContainsRect(imageBounds, srcBounds)) { - TkMacOSXDbgMsg("Mismatch of sub CGImage bounds"); - } - subImage = CGImageCreateWithImageInRect(image, CGRectOffset( - srcBounds, -imageBounds.origin.x, -imageBounds.origin.y)); - if (subImage) { - image = subImage; - } - } dstBounds = CGRectOffset(dstBounds, macDraw->xOff, macDraw->yOff); if (CGImageIsMask(image)) { if (macDraw->flags & TK_IS_BW_PIXMAP) { @@ -378,9 +356,6 @@ TkMacOSXDrawCGImage( CGContextDrawImage(context, dstBounds, image); CGContextRestoreGState(context); #endif /* TK_MAC_DEBUG_IMAGE_DRAWING */ - if (subImage) { - CFRelease(subImage); - } } else { TkMacOSXDbgMsg("Drawing of empty CGImage requested"); } @@ -514,7 +489,7 @@ XDrawSegments( * * XFillPolygon -- * - * Draws a filled polygon using the even-odd fill algorithm, + * Draws a filled polygon. * * Results: * None. @@ -562,7 +537,9 @@ XFillPolygon( CGContextAddLineToPoint(dc.context, prevx, prevy); } } - CGContextEOFillPath(dc.context); + (gc->fill_rule == EvenOddRule) + ? CGContextEOFillPath(dc.context) + : CGContextFillPath(dc.context); } TkMacOSXRestoreDrawingContext(&dc); return Success; @@ -1133,7 +1110,11 @@ XMaxRequestSize( int TkScrollWindow( Tk_Window tkwin, /* The window to be scrolled. */ +#if TK_MAC_SYNCHRONOUS_DRAWING + GC gc, /* GC for window to be scrolled. */ +#else TCL_UNUSED(GC), /* GC for window to be scrolled. */ +#endif int x, int y, /* Position rectangle to be scrolled. */ int width, int height, int dx, int dy, /* Distance rectangle should be moved. */ @@ -1147,7 +1128,13 @@ TkScrollWindow( NSRect bounds, viewSrcRect, srcRect, dstRect; int result = 0; +#if TK_MAC_SYNCHRONOUS_DRAWING + // Should behave more like TkScrollWindow on other platforms + if (XCopyArea(Tk_Display(tkwin), drawable, drawable, gc, x, y, + (unsigned)width, (unsigned)height, x+dx, y+dy) == Success) { +#else if (view) { +#endif /* * Get the scroll area in NSView coordinates (origin at bottom left). @@ -1158,11 +1145,15 @@ TkScrollWindow( bounds.size.height - height - (macDraw->yOff + y), width, height); +#if TK_MAC_SYNCHRONOUS_DRAWING + // Already scrolled using XCopyArea() +#else /* * Scroll the rectangle. */ [view scrollRect:viewSrcRect by:NSMakeSize(dx, -dy)]; +#endif /* * Compute the damage region, using Tk coordinates (origin at top left). @@ -1286,7 +1277,11 @@ TkMacOSXSetupDrawingContext( if (!dc.context) { NSRect drawingBounds, currentBounds; dc.view = view; +#if TK_MAC_CGIMAGE_DRAWING + dc.context = view.tkLayerBitmapContext; +#else dc.context = GET_CGCONTEXT; +#endif if (dc.clipRgn) { CGRect clipBounds; CGAffineTransform t = { .a = 1, .b = 0, .c = 0, .d = -1, .tx = 0, @@ -1298,6 +1293,11 @@ TkMacOSXSetupDrawingContext( drawingBounds = [view bounds]; } +#if TK_MAC_SYNCHRONOUS_DRAWING + // It seems this should be the only place to use addTkDirtyRect: + // and that it should not be used elsewhere as a proxy to generate Expose events, which will not work. + [view addTkDirtyRect:drawingBounds]; +#else /* * We can only draw into the NSView which is the current focusView. * When the current [NSView focusView] is nil, the CGContext for @@ -1332,6 +1332,7 @@ TkMacOSXSetupDrawingContext( if (!NSContainsRect(currentBounds, drawingBounds)) { [view addTkDirtyRect:drawingBounds]; } +#endif } /* @@ -1469,6 +1470,10 @@ TkMacOSXSetupDrawingContext( dc.clipRgn = NULL; } *dcPtr = dc; +#if TK_MAC_SYNCHRONOUS_DRAWING + // The goal is to allow immediate drawing; canDraw == 0 should happen far less often. + if (0) fprintf(stderr, "tkmacosxsdc canDraw %d\n", canDraw); +#endif return canDraw; } diff --git a/macosx/tkMacOSXImage.c b/macosx/tkMacOSXImage.c index fc0ffdf42e..939a6663c3 100644 --- a/macosx/tkMacOSXImage.c +++ b/macosx/tkMacOSXImage.c @@ -18,6 +18,9 @@ static CGImageRef CreateCGImageFromPixmap(Drawable pixmap); static CGImageRef CreateCGImageFromDrawableRect( Drawable drawable, +#if TK_MAC_SYNCHRONOUS_DRAWING + int force_1x_scale, +#endif int x, int y, unsigned int width, unsigned int height); /* Pixel formats @@ -508,8 +511,16 @@ TkMacOSXPutImage( return BadDrawable; } if (dc.context) { - CGRect bounds, srcRect, dstRect; + CGRect dstRect, srcRect = CGRectMake(src_x, src_y, width, height); + /* + * Whole image is copied before cropping. For performance, + * consider revising TkMacOSXCreateCGImageWithXImage() to accept + * source x/y/w/h and copy only the needed portion instead. + */ CGImageRef img = TkMacOSXCreateCGImageWithXImage(image, pixelFormat); + CGImageRef cropped = CGImageCreateWithImageInRect(img, srcRect); + CGImageRelease(img); + img = cropped; /* * The CGContext for a pixmap is RGB only, with A = 0. @@ -519,12 +530,9 @@ TkMacOSXPutImage( CGContextSetBlendMode(dc.context, kCGBlendModeSourceAtop); } if (img) { - bounds = CGRectMake(0, 0, image->width, image->height); - srcRect = CGRectMake(src_x, src_y, width, height); dstRect = CGRectMake(dest_x, dest_y, width, height); - TkMacOSXDrawCGImage(drawable, gc, dc.context, - img, gc->foreground, gc->background, - bounds, srcRect, dstRect); + TkMacOSXDrawCGImage(drawable, gc, dc.context, img, + gc->foreground, gc->background, dstRect); CFRelease(img); } else { TkMacOSXDbgMsg("Invalid source drawable"); @@ -635,6 +643,11 @@ int TkpPutRGBAImage( * with origin at the top left, as used by XImage and CGImage, not bottom * left as used by NSView. * + * If force_1x_scale is true, then the returned CGImage will be downscaled + * if necessary to have the requested width and height. Othewise, for + * windows on Retina displays, the width and height of the returned CGImage + * will be twice the requested width and height. + * * Side effects: * None * @@ -644,6 +657,9 @@ int TkpPutRGBAImage( static CGImageRef CreateCGImageFromDrawableRect( Drawable drawable, +#if TK_MAC_SYNCHRONOUS_DRAWING + int force_1x_scale, +#endif int x, int y, unsigned int width, @@ -652,6 +668,9 @@ CreateCGImageFromDrawableRect( MacDrawable *mac_drawable = (MacDrawable *)drawable; CGContextRef cg_context = NULL; CGImageRef cg_image = NULL, result = NULL; +#if TK_MAC_SYNCHRONOUS_DRAWING + CGFloat scaleFactor = 1.0; +#endif if (mac_drawable->flags & TK_IS_PIXMAP) { cg_context = TkMacOSXGetCGContextForDrawable(drawable); CGContextRetain(cg_context); @@ -661,6 +680,13 @@ CreateCGImageFromDrawableRect( TkMacOSXDbgMsg("Invalid source drawable"); return NULL; } +#if TK_MAC_SYNCHRONOUS_DRAWING + scaleFactor = view.layer.contentsScale; +#endif +#if TK_MAC_CGIMAGE_DRAWING + cg_context = ((TKContentView *)view).tkLayerBitmapContext; + CGContextRetain(cg_context); +#else NSSize size = view.frame.size; NSUInteger view_width = size.width, view_height = size.height; NSUInteger bytesPerPixel = 4, @@ -673,6 +699,7 @@ CreateCGImageFromDrawableRect( kCGBitmapByteOrder32Big); CFRelease(colorSpace); [view.layer renderInContext:cg_context]; +#endif } if (cg_context) { cg_image = CGBitmapContextCreateImage(cg_context); @@ -681,8 +708,34 @@ CreateCGImageFromDrawableRect( if (cg_image) { CGRect rect = CGRectMake(x + mac_drawable->xOff, y + mac_drawable->yOff, width, height); +#if TK_MAC_SYNCHRONOUS_DRAWING + rect = CGRectApplyAffineTransform(rect, CGAffineTransformMakeScale(scaleFactor, scaleFactor)); +#endif result = CGImageCreateWithImageInRect(cg_image, rect); CGImageRelease(cg_image); +#if TK_MAC_SYNCHRONOUS_DRAWING + if (force_1x_scale && (scaleFactor != 1.0)) { + // See https://web.archive.org/web/20200219030756/http://blog.foundry376.com/2008/07/scaling-a-cgimage/#comment-200 + // create context, keeping original image properties + CGColorSpaceRef colorspace = CGImageGetColorSpace(cg_image); + cg_context = CGBitmapContextCreate(NULL, width, height, + CGImageGetBitsPerComponent(cg_image), + //CGImageGetBytesPerRow(cg_image), // wastes space? + CGImageGetBitsPerPixel(cg_image) * width / 8, + colorspace, + CGImageGetAlphaInfo(cg_image)); + CGColorSpaceRelease(colorspace); + if (cg_context) { + // draw image to context (resizing it) + CGContextDrawImage(cg_context, CGRectMake(0, 0, width, height), + cg_image); + // extract resulting image from context + result = CGBitmapContextCreateImage(cg_context); + CGContextRelease(cg_context); + } + CGImageRelease(cg_image); + } +#endif } return result; } @@ -763,7 +816,8 @@ XGetImage( return NULL; } - cgImage = CreateCGImageFromDrawableRect(drawable, x, y, width, height); + // Request 1x-scale image for compatibility + cgImage = CreateCGImageFromDrawableRect(drawable, 1, x, y, width, height); if (cgImage) { bitmapRep = [NSBitmapImageRep alloc]; [bitmapRep initWithCGImage:cgImage]; @@ -775,7 +829,6 @@ XGetImage( bitmap_fmt = [bitmapRep bitmapFormat]; size = [bitmapRep bytesPerPlane]; bytes_per_row = [bitmapRep bytesPerRow]; - bitmap = (char *)ckalloc(size); if ((bitmap_fmt != 0 && bitmap_fmt != NSAlphaFirstBitmapFormat) || [bitmapRep samplesPerPixel] != 4 || [bitmapRep isPlanar] != 0 @@ -785,6 +838,7 @@ XGetImage( [bitmapRep release]; return NULL; } + bitmap = (char *)ckalloc(size); memcpy(bitmap, (char *)[bitmapRep bitmapData], size); [bitmapRep release]; @@ -854,9 +908,8 @@ XCopyArea( int dest_y) { TkMacOSXDrawingContext dc; - MacDrawable *srcDraw = (MacDrawable *)src; CGImageRef img = NULL; - CGRect bounds, srcRect, dstRect; + CGRect dstRect; display->request++; if (!width || !height) { @@ -873,20 +926,13 @@ XCopyArea( return BadDrawable; } - if (srcDraw->flags & TK_IS_PIXMAP) { - img = CreateCGImageFromPixmap(src); - } else if (TkMacOSXGetNSWindowForDrawable(src)) { - img = CreateCGImageFromDrawableRect(src, src_x, src_y, width, height); - } else { - TkMacOSXDbgMsg("Invalid source drawable - neither window nor pixmap."); - } + // Use unscaled source (TkMacOSXDrawCGImage() will implicitly downscale) + img = CreateCGImageFromDrawableRect(src, 0, src_x, src_y, width, height); if (img) { - bounds = CGRectMake(0, 0, srcDraw->size.width, srcDraw->size.height); - srcRect = CGRectMake(src_x, src_y, width, height); dstRect = CGRectMake(dest_x, dest_y, width, height); TkMacOSXDrawCGImage(dst, gc, dc.context, img, - gc->foreground, gc->background, bounds, srcRect, dstRect); + gc->foreground, gc->background, dstRect); CFRelease(img); } else { TkMacOSXDbgMsg("Failed to construct CGImage."); @@ -931,7 +977,7 @@ XCopyPlane( TkMacOSXDrawingContext dc; MacDrawable *srcDraw = (MacDrawable *)src; MacDrawable *dstDraw = (MacDrawable *)dst; - CGRect bounds, srcRect, dstRect; + CGRect srcRect, dstRect; display->request++; if (!width || !height) { /* TkMacOSXDbgMsg("Drawing of empty area requested"); */ @@ -997,13 +1043,9 @@ XCopyPlane( CGImageRelease(submask); CGImageRelease(subimage); } else { - bounds = CGRectMake(0, 0, - srcDraw->size.width, srcDraw->size.height); - srcRect = CGRectMake(src_x, src_y, width, height); dstRect = CGRectMake(dest_x, dest_y, width, height); TkMacOSXDrawCGImage(dst, gc, dc.context, img, - gc->foreground, imageBackground, bounds, - srcRect, dstRect); + gc->foreground, imageBackground, dstRect); CGImageRelease(img); } } else { diff --git a/macosx/tkMacOSXInit.c b/macosx/tkMacOSXInit.c index d9f3d123d3..6ef52a3595 100644 --- a/macosx/tkMacOSXInit.c +++ b/macosx/tkMacOSXInit.c @@ -41,7 +41,19 @@ static int TkMacOSXGetAppPathCmd(ClientData cd, Tcl_Interp *ip, @implementation TKApplication @synthesize poolLock = _poolLock; @synthesize macOSVersion = _macOSVersion; +#if TK_MAC_CGIMAGE_DRAWING +// There should be no need for isDrawing since drawRect: is not used. +- (void) setIsDrawing: (Bool)b +{ + return; +} +- (Bool) isDrawing +{ + return YES; +} +#else @synthesize isDrawing = _isDrawing; +#endif @synthesize needsToDraw = _needsToDraw; @synthesize tkLiveResizeEnded = _tkLiveResizeEnded; @synthesize tkPointerWindow = _tkPointerWindow; diff --git a/macosx/tkMacOSXInt.h b/macosx/tkMacOSXInt.h index 61771a0ee0..8fa0467a04 100644 --- a/macosx/tkMacOSXInt.h +++ b/macosx/tkMacOSXInt.h @@ -171,6 +171,21 @@ MODULE_SCOPE TkMacOSXEmbedHandler *tkMacOSXEmbedHandler; #define TK_LAYOUT_WITH_BASE_CHUNKS 1 #define TK_DRAW_IN_CONTEXT 1 +/* +* Experimental CGBitmapContext → CGImage → layer-backed NSView drawing +* (FIXME: should this go somewhere else?) +*/ +#define TK_MAC_CGIMAGE_DRAWING 1 + +/* + * Using an approach to allow Tk drawing routines to draw "synchronously" + * (rather than only when in drawRect:--although actually displaying results + * of drawing may still be done asynchronously) + */ +#if TK_MAC_CGIMAGE_DRAWING +#define TK_MAC_SYNCHRONOUS_DRAWING 1 +#endif + /* * Prototypes of internal procs not in the stubs table. */ diff --git a/macosx/tkMacOSXMenu.c b/macosx/tkMacOSXMenu.c index cc3ab7f234..e00e488b3c 100644 --- a/macosx/tkMacOSXMenu.c +++ b/macosx/tkMacOSXMenu.c @@ -935,8 +935,6 @@ TkpPostMenu( } realWin = Tk_Parent(realWin); } - NSWindow *win = [realWinView window]; - NSView *view = [win contentView]; NSMenu *menu = (NSMenu *) menuPtr->platformData; NSInteger itemIndex = index; NSInteger numItems = [menu numberOfItems]; @@ -966,8 +964,9 @@ TkpPostMenu( } [menu popUpMenuPositioningItem:item - atLocation:[win tkConvertPointFromScreen:location] - inView:view]; + atLocation:location + inView:nil + appearance:realWinView.effectiveAppearance]; inPostMenu = 0; return TCL_OK; } diff --git a/macosx/tkMacOSXNotify.c b/macosx/tkMacOSXNotify.c index 208d846512..6c1fa4d167 100644 --- a/macosx/tkMacOSXNotify.c +++ b/macosx/tkMacOSXNotify.c @@ -378,7 +378,11 @@ TkMacOSXDrawAllViews( if (dirtyCount) { continue; } +#if TK_MAC_CGIMAGE_DRAWING + // Layer-backed view: let NSView schedule updates +#else [[view layer] setNeedsDisplayInRect:[view tkDirtyRect]]; +#endif [view setNeedsDisplay:YES]; } } else { @@ -403,7 +407,12 @@ TkMacOSXDrawAllViews( */ if ([view needsDisplay]) { +#if TK_MAC_CGIMAGE_DRAWING + // Should no longer ever need to setNeedsDisplay:NO + if (0) fprintf(stderr, "nD still set %p\n", view); +#else [view setNeedsDisplay: NO]; +#endif } } } diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h index 3b031392d3..34005d9e4e 100644 --- a/macosx/tkMacOSXPrivate.h +++ b/macosx/tkMacOSXPrivate.h @@ -250,8 +250,7 @@ MODULE_SCOPE int TkMacOSXIsWindowZoomed(TkWindow *winPtr); MODULE_SCOPE int TkGenerateButtonEventForXPointer(Window window); MODULE_SCOPE void TkMacOSXDrawCGImage(Drawable d, GC gc, CGContextRef context, CGImageRef image, unsigned long imageForeground, - unsigned long imageBackground, CGRect imageBounds, - CGRect srcBounds, CGRect dstBounds); + unsigned long imageBackground, CGRect dstBounds); MODULE_SCOPE int TkMacOSXSetupDrawingContext(Drawable d, GC gc, TkMacOSXDrawingContext *dcPtr); MODULE_SCOPE void TkMacOSXRestoreDrawingContext( @@ -433,6 +432,9 @@ VISIBILITY_HIDDEN } @property Bool tkNeedsDisplay; @property NSRect tkDirtyRect; +#if TK_MAC_CGIMAGE_DRAWING +@property CGContextRef tkLayerBitmapContext; +#endif @end @interface TKContentView(TKKeyEvent) @@ -445,6 +447,9 @@ VISIBILITY_HIDDEN - (void) clearTkDirtyRect; - (void) generateExposeEvents: (NSRect) rect; - (void) tkToolbarButton: (id) sender; +#if TK_MAC_CGIMAGE_DRAWING +- (void) resetTkLayerBitmapContext; +#endif @end @interface NSWindow(TKWm) @@ -498,6 +503,14 @@ VISIBILITY_HIDDEN - (NSMenuItem *)itemInSupermenu; @end +// Need undocumented appearance: argument +@interface NSMenu(TKMenu) +- (BOOL)popUpMenuPositioningItem:(NSMenuItem *)item + atLocation:(NSPoint)location + inView:(NSView *)view + appearance:(NSAppearance *)appearance; +@end + @interface NSMenuItem(TKUtils) + (id)itemWithSubmenu:(NSMenu *)submenu; + (id)itemWithTitle:(NSString *)title submenu:(NSMenu *)submenu; diff --git a/macosx/tkMacOSXRegion.c b/macosx/tkMacOSXRegion.c index fbb41cb953..4e185079a0 100644 --- a/macosx/tkMacOSXRegion.c +++ b/macosx/tkMacOSXRegion.c @@ -45,8 +45,8 @@ TkRegion TkCreateRegion(void) { TkRegion region = (TkRegion) HIShapeCreateMutable(); - DebugLog("Created region: total regions = %d\n", ++totalRegions); - RetainRegion(region); + DebugLog("Created region: total regions = %d, total count is %d\n", + ++totalRegions, ++totalRegionRetainCount); return region; } diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index dbae21ef46..e58874f41b 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -179,6 +179,7 @@ XMapWindow( TkMacOSXApplyWindowAttributes(winPtr, win); [win setExcludedFromWindowsMenu:NO]; [NSApp activateIgnoringOtherApps:initialized]; + // Not sure this does anything useful for TK_MAC_SYNCHRONOUS_DRAWING [view addTkDirtyRect: [view bounds]]; if (initialized) { if ([win canBecomeKeyWindow]) { @@ -225,9 +226,19 @@ XMapWindow( */ TKContentView *view = [win contentView]; +#if TK_MAC_SYNCHRONOUS_DRAWING + /* + * Do not rely on addTkDirtyRect: to generate Expose events + * (though I’m not sure if this is the place to generate events; + * or if using generateExposeEvents: is the best way; + * what does XMapWindow() do on other platforms?) + */ + [view generateExposeEvents:[view bounds]]; +#else if (view != [NSView focusView]) { [view addTkDirtyRect:[view bounds]]; } +#endif /* * Generate VisibilityNotify events for window and all mapped children. @@ -367,10 +378,18 @@ XUnmapWindow( TkMacOSXInvalClipRgns((Tk_Window)parentPtr); TkMacOSXUpdateClipRgn(parentPtr); } +#if TK_MAC_SYNCHRONOUS_DRAWING + /* + * Anything need to be done here instead? + * (Not yet aware of reasoning behind [78a3bdc4454f] + * i.e. why the existing approach uses addTkDirtyRect: here) + */ +#else TKContentView *view = [win contentView]; if (view != [NSView focusView]) { [view addTkDirtyRect:[view bounds]]; } +#endif Tk_UpdatePointer(NULL, x, y, state); return Success; } @@ -1040,7 +1059,12 @@ InvalViewRect( break; case kHIShapeEnumerateRect: dirtyRect = NSRectFromCGRect(CGRectApplyAffineTransform(*rect, t)); +#if TK_MAC_SYNCHRONOUS_DRAWING + // Cannot rely on addTkDirtyRect: to force redrawing. + [view generateExposeEvents:dirtyRect]; +#else [view addTkDirtyRect:dirtyRect]; +#endif break; } return noErr; @@ -1512,11 +1536,6 @@ Tk_FreePixmap( display->request++; if (macPix->context) { - char *data = (char *)CGBitmapContextGetData(macPix->context); - - if (data) { - ckfree(data); - } CFRelease(macPix->context); } ckfree(macPix); diff --git a/macosx/tkMacOSXTest.c b/macosx/tkMacOSXTest.c index ec19ec5926..f6dd03e0d3 100644 --- a/macosx/tkMacOSXTest.c +++ b/macosx/tkMacOSXTest.c @@ -153,6 +153,7 @@ TkTestLogDisplay( Drawable drawable) { MacDrawable *macWin = (MacDrawable *)drawable; + if (TK_MAC_SYNCHRONOUS_DRAWING) return True; // No checking focusView. NSWindow *win = nil; if (macWin->toplevel && macWin->toplevel->winPtr && macWin->toplevel->winPtr->wmInfoPtr && diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 5769369c84..710f4ecfa8 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -477,6 +477,7 @@ static void RefocusGrabWindow(void *data) { int TkpWillDrawWidget(Tk_Window tkwin) { int result; + if (TK_MAC_SYNCHRONOUS_DRAWING) return 0; // not in drawRect if (tkwin) { TkWindow *winPtr = (TkWindow *)tkwin; TKContentView *view = (TKContentView *)TkMacOSXGetNSViewForDrawable( @@ -970,22 +971,30 @@ ConfigureRestrictProc( { self = [super initWithFrame:frame]; if (self) { +#if TK_MAC_CGIMAGE_DRAWING + // Want layer-backed view, not layer-hosting +#else /* * The layer must exist before we set wantsLayer to YES. */ self.layer = [CALayer layer]; +#endif self.wantsLayer = YES; self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay; self.layer.contentsGravity = self.layer.contentsAreFlipped ? kCAGravityTopLeft : kCAGravityBottomLeft; +#if TK_MAC_CGIMAGE_DRAWING + // Want layer-backed view, not layer-hosting +#else /* * Nothing gets drawn at all if the layer does not have a delegate. * Currently, we do not implement any methods of the delegate, however. */ self.layer.delegate = (id) self; +#endif trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:(NSTrackingMouseEnteredAndExited | @@ -1000,6 +1009,23 @@ ConfigureRestrictProc( return self; } +#if TK_MAC_CGIMAGE_DRAWING +- (BOOL) wantsUpdateLayer +{ + return YES; +} +- (void) updateLayer { + if (0) fprintf(stderr, "updateLayer\n"); + CGContextRef ctx = self.tkLayerBitmapContext; + + if (ctx) { + CGImageRef newImg = CGBitmapContextCreateImage(ctx); + self.layer.contents = (__bridge id)newImg; + CGImageRelease(newImg); // will quickly leak memory if this is missing + [self clearTkDirtyRect]; + } +} +#else /* * We will just use drawRect. */ @@ -1008,6 +1034,7 @@ ConfigureRestrictProc( { return NO; } +#endif - (void) viewDidChangeBackingProperties { @@ -1020,6 +1047,11 @@ ConfigureRestrictProc( */ self.layer.contentsScale = self.window.screen.backingScaleFactor; +#if TK_MAC_CGIMAGE_DRAWING + [self resetTkLayerBitmapContext]; + // need to redraw + [self generateExposeEvents: [self bounds]]; +#endif } - (void) addTkDirtyRect: (NSRect) rect @@ -1028,7 +1060,11 @@ ConfigureRestrictProc( _tkDirtyRect = NSUnionRect(_tkDirtyRect, rect); [NSApp setNeedsToDraw:YES]; [self setNeedsDisplay:YES]; +#if TK_MAC_CGIMAGE_DRAWING + // Layer-backed: want the NSView to control when to draw +#else [[self layer] setNeedsDisplay]; +#endif } - (void) clearTkDirtyRect @@ -1038,6 +1074,9 @@ ConfigureRestrictProc( [NSApp setNeedsToDraw:NO]; } +#if TK_MAC_CGIMAGE_DRAWING +// Remove drawRect: just to make sure it isn’t used +#else - (void) drawRect: (NSRect) rect { (void)rect; @@ -1071,10 +1110,14 @@ ConfigureRestrictProc( fprintf(stderr, "drawRect: done.\n"); #endif } +#endif -(void) setFrameSize: (NSSize)newsize { [super setFrameSize: newsize]; +#if TK_MAC_CGIMAGE_DRAWING + [self resetTkLayerBitmapContext]; +#endif NSWindow *w = [self window]; TkWindow *winPtr = TkMacOSXGetTkWindow(w); Tk_Window tkwin = (Tk_Window)winPtr; @@ -1167,7 +1210,7 @@ ConfigureRestrictProc( updateBounds.origin.y = ([self bounds].size.height - updateBounds.origin.y - updateBounds.size.height); updatesNeeded = GenerateUpdates(&updateBounds, winPtr); - if (updatesNeeded) { + if (!TK_MAC_SYNCHRONOUS_DRAWING && updatesNeeded) { serial = LastKnownRequestProcessed(Tk_Display(winPtr)); @@ -1369,6 +1412,36 @@ static const char *const accentNames[] = { return [super validRequestorForSendType:sendType returnType:returnType]; } +#if TK_MAC_CGIMAGE_DRAWING +-(void) resetTkLayerBitmapContext { + CGColorSpaceRef colorspace = NULL; + + // Try using display colorspace to avoid performance hit due to colorspace conversion + // (allow disabling in case color accuracy is needed) + if (!getenv("TK_NODISPLAYCOLORSPACE")) { + colorspace = CGDisplayCopyColorSpace(CGMainDisplayID()); + } + + // fallback (uses sRGB on macOS 10.8 and later) + if (!colorspace) { + colorspace = CGColorSpaceCreateDeviceRGB(); + } + + CGContextRef newCtx = CGBitmapContextCreate( + NULL, self.layer.contentsScale * self.frame.size.width, + self.layer.contentsScale * self.frame.size.height, 8, 0, colorspace, + kCGBitmapByteOrder32Big | kCGImageAlphaNoneSkipLast // will also need to specify this when capturing + ); + CGContextScaleCTM(newCtx, self.layer.contentsScale, self.layer.contentsScale); + if (0) fprintf(stderr, "rTkLBC %.1f %s %p %p %ld\n", (float)self.layer.contentsScale, + NSStringFromSize(self.frame.size).UTF8String, colorspace, newCtx, self.tkLayerBitmapContext ? (long)CFGetRetainCount(self.tkLayerBitmapContext) : INT_MIN); + if (0) fprintf(stderr, "rTkLBC %p %ld\n", self.tkLayerBitmapContext, (long)(self.tkLayerBitmapContext ? CFGetRetainCount(self.tkLayerBitmapContext) : LONG_MIN)); + CGContextRelease(self.tkLayerBitmapContext); // will also need this in a destructor somewhere + self.tkLayerBitmapContext = newCtx; + CGColorSpaceRelease(colorspace); +} +#endif + @end /* From 37b53c9f257c1dd882d7291353b939648c34b378 Mon Sep 17 00:00:00 2001 From: marc_culler Date: Tue, 16 Aug 2022 14:18:19 +0000 Subject: [PATCH 02/96] Re-enable liveResize. --- macosx/tkMacOSXWindowEvent.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 710f4ecfa8..f5e7d5b49b 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -1170,7 +1170,7 @@ ConfigureRestrictProc( * ticket [1fa8c3ed8d]. */ - if([NSApp isDrawing] || [self inLiveResize]) { + if ([NSApp isDrawing] || [self inLiveResize]) { [self generateExposeEvents: [self bounds]]; } @@ -1210,7 +1210,8 @@ ConfigureRestrictProc( updateBounds.origin.y = ([self bounds].size.height - updateBounds.origin.y - updateBounds.size.height); updatesNeeded = GenerateUpdates(&updateBounds, winPtr); - if (!TK_MAC_SYNCHRONOUS_DRAWING && updatesNeeded) { + //if (!TK_MAC_SYNCHRONOUS_DRAWING && updatesNeeded) { + if ([self inLiveResize] && updatesNeeded) { serial = LastKnownRequestProcessed(Tk_Display(winPtr)); From 230f963a35e575129f24862283fe03d9aeb88d50 Mon Sep 17 00:00:00 2001 From: marc_culler Date: Tue, 6 Sep 2022 23:49:18 +0000 Subject: [PATCH 03/96] Add Christopher's patches to make dark mode work. --- macosx/tkMacOSXColor.c | 54 ++++++++++++++++++++---------------- macosx/tkMacOSXDraw.c | 13 +++++++++ macosx/tkMacOSXPrivate.h | 3 ++ macosx/tkMacOSXWindowEvent.c | 1 + 4 files changed, 47 insertions(+), 24 deletions(-) diff --git a/macosx/tkMacOSXColor.c b/macosx/tkMacOSXColor.c index 3951683bc0..2b18b17691 100644 --- a/macosx/tkMacOSXColor.c +++ b/macosx/tkMacOSXColor.c @@ -629,6 +629,7 @@ TkpGetColor( XColor color; Colormap colormap = tkwin ? Tk_Colormap(tkwin) : noColormap; NSView *view = nil; + Bool haveValidXColor = False; static Bool initialized = NO; static NSColorSpace* sRGB = nil; @@ -658,33 +659,26 @@ TkpGetColor( p.pixel.colortype = entry->type; p.pixel.value = entry->index; color.pixel = p.ulong; - if (entry->type == semantic) { - CGFloat rgba[4]; + #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 - if (@available(macOS 10.14, *)) { - NSAppearance *savedAppearance = [NSAppearance currentAppearance]; - NSAppearance *windowAppearance = savedAppearance; - if (view) { - windowAppearance = [view effectiveAppearance]; - } - if ([windowAppearance name] == NSAppearanceNameDarkAqua) { - colormap = darkColormap; - } else { - colormap = lightColormap; - } - [NSAppearance setCurrentAppearance:windowAppearance]; - GetRGBA(entry, p.ulong, rgba); - [NSAppearance setCurrentAppearance:savedAppearance]; - } else { - GetRGBA(entry, p.ulong, rgba); + NSAppearance *savedAppearance, *windowAppearance; + if (@available(macOS 10.14, *)) { + savedAppearance = [NSAppearance currentAppearance]; + windowAppearance = savedAppearance; + if (view) { + windowAppearance = [view effectiveAppearance]; } -#else - GetRGBA(entry, p.ulong, rgba); + [NSAppearance setCurrentAppearance:windowAppearance]; + } #endif + + if (entry->type == semantic) { + CGFloat rgba[4]; + GetRGBA(entry, p.ulong, rgba); color.red = rgba[0] * 65535.0; color.green = rgba[1] * 65535.0; color.blue = rgba[2] * 65535.0; - goto validXColor; + haveValidXColor = True; } else if (SetCGColorComponents(entry, 0, &c)) { const size_t n = CGColorGetNumberOfComponents(c); const CGFloat *rgba = CGColorGetComponents(c); @@ -702,15 +696,27 @@ TkpGetColor( Tcl_Panic("CGColor with %d components", (int) n); } CGColorRelease(c); - goto validXColor; + haveValidXColor = True; + } +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 + if (@available(macOS 10.14, *)) { + // Not sure whether colormap should also be set for non-semantic color + if (haveValidXColor && entry->type == semantic) { + if ([windowAppearance name] == NSAppearanceNameDarkAqua) { + colormap = darkColormap; + } else { + colormap = lightColormap; + } + } + [NSAppearance setCurrentAppearance:savedAppearance]; } +#endif } } - if (TkParseColor(display, colormap, name, &color) == 0) { + if (!haveValidXColor && TkParseColor(display, colormap, name, &color) == 0) { return NULL; } -validXColor: tkColPtr = (TkColor *)ckalloc(sizeof(TkColor)); tkColPtr->colormap = colormap; tkColPtr->color = color; diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index 4e8f57fccb..cf4293c782 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -1401,6 +1401,14 @@ TkMacOSXSetupDrawingContext( CGContextClipToRect(dc.context, r); } } +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 + if (@available(macOS 10.14, *)) { + dc.savedAppearance = NSAppearance.currentAppearance; + if (view) { + NSAppearance.currentAppearance = view.effectiveAppearance; + } + } +#endif if (gc) { static const CGLineCap cgCap[] = { [CapNotLast] = kCGLineCapButt, @@ -1510,6 +1518,11 @@ TkMacOSXRestoreDrawingContext( CFRelease(dcPtr->clipRgn); dcPtr->clipRgn = NULL; } +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 + if (@available(macOS 10.14, *)) { + NSAppearance.currentAppearance = dcPtr->savedAppearance; + } +#endif #ifdef TK_MAC_DEBUG bzero(dcPtr, sizeof(TkMacOSXDrawingContext)); diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h index 34005d9e4e..0e1bfdfa8c 100644 --- a/macosx/tkMacOSXPrivate.h +++ b/macosx/tkMacOSXPrivate.h @@ -204,6 +204,9 @@ typedef struct TkMacOSXDrawingContext { CGContextRef context; NSView *view; HIShapeRef clipRgn; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 + NSAppearance *savedAppearance; +#endif } TkMacOSXDrawingContext; /* diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index f5e7d5b49b..e737f5e2f9 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -1314,6 +1314,7 @@ static const char *const accentNames[] = { effectiveAppearanceName.UTF8String, accentName, highlightName); TkSendVirtualEvent(tkwin, "AppearanceChanged", Tcl_NewStringObj(data, -1)); + [self generateExposeEvents:self.bounds]; } - (void)observeValueForKeyPath:(NSString *)keyPath From 0c5b941e3458589bd9b56b34ac1915da7c09f451 Mon Sep 17 00:00:00 2001 From: marc_culler Date: Sat, 17 Sep 2022 20:26:52 +0000 Subject: [PATCH 04/96] Fix the ttk background issue with 1 deprecated call. --- macosx/tkMacOSXColor.c | 8 ++-- macosx/tkMacOSXDraw.c | 79 ++++++++++++++++++++++++++++++---------- macosx/tkMacOSXPrivate.h | 3 -- macosx/tkMacOSXRegion.c | 25 ------------- 4 files changed, 62 insertions(+), 53 deletions(-) diff --git a/macosx/tkMacOSXColor.c b/macosx/tkMacOSXColor.c index 2c0d642ce0..9f941e0921 100644 --- a/macosx/tkMacOSXColor.c +++ b/macosx/tkMacOSXColor.c @@ -661,14 +661,13 @@ TkpGetColor( color.pixel = p.ulong; #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 - NSAppearance *savedAppearance, *windowAppearance; + NSAppearance *windowAppearance; if (@available(macOS 10.14, *)) { - savedAppearance = [NSAppearance currentAppearance]; - windowAppearance = savedAppearance; if (view) { windowAppearance = [view effectiveAppearance]; + } else { + windowAppearance = [NSApp effectiveAppearance]; } - [NSAppearance setCurrentAppearance:windowAppearance]; } #endif @@ -739,7 +738,6 @@ TkpGetColor( colormap = lightColormap; } } - [NSAppearance setCurrentAppearance:savedAppearance]; } #endif } diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index cf4293c782..5c9c0e834a 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -1121,11 +1121,9 @@ TkScrollWindow( TkRegion damageRgn) /* Region to accumulate damage in. */ { Drawable drawable = Tk_WindowId(tkwin); - MacDrawable *macDraw = (MacDrawable *)drawable; - TKContentView *view = (TKContentView *)TkMacOSXGetNSViewForDrawable(macDraw); HIShapeRef srcRgn, dstRgn; HIMutableShapeRef dmgRgn = HIShapeCreateMutable(); - NSRect bounds, viewSrcRect, srcRect, dstRect; + NSRect srcRect, dstRect; int result = 0; #if TK_MAC_SYNCHRONOUS_DRAWING @@ -1133,21 +1131,21 @@ TkScrollWindow( if (XCopyArea(Tk_Display(tkwin), drawable, drawable, gc, x, y, (unsigned)width, (unsigned)height, x+dx, y+dy) == Success) { #else + TKContentView *view = (TKContentView *)TkMacOSXGetNSViewForDrawable(macDraw); if (view) { #endif +#if TK_MAC_SYNCHRONOUS_DRAWING + // Already scrolled using XCopyArea() +#else /* * Get the scroll area in NSView coordinates (origin at bottom left). */ - bounds = [view bounds]; - viewSrcRect = NSMakeRect(macDraw->xOff + x, + NSRect bounds = [view bounds]; + NSRect viewSrcRect = NSMakeRect(macDraw->xOff + x, bounds.size.height - height - (macDraw->yOff + y), width, height); - -#if TK_MAC_SYNCHRONOUS_DRAWING - // Already scrolled using XCopyArea() -#else /* * Scroll the rectangle. */ @@ -1275,7 +1273,7 @@ TkMacOSXSetupDrawingContext( dc.context = TkMacOSXGetCGContextForDrawable(d); if (!dc.context) { - NSRect drawingBounds, currentBounds; + NSRect drawingBounds; dc.view = view; #if TK_MAC_CGIMAGE_DRAWING dc.context = view.tkLayerBitmapContext; @@ -1295,8 +1293,56 @@ TkMacOSXSetupDrawingContext( #if TK_MAC_SYNCHRONOUS_DRAWING // It seems this should be the only place to use addTkDirtyRect: - // and that it should not be used elsewhere as a proxy to generate Expose events, which will not work. + // and that it should not be used elsewhere as a proxy to generate + // Expose events, which will not work. + [view addTkDirtyRect:drawingBounds]; + + /* + * Workaround for an Apple bug. + * + * Without the block below, ttk frames, labelframes and labels do not + * get the correct background color on macOS 12.5 after the appearance + * changes. This function is only called when drawing, so we know that + * our view is the focus view. Even though the effective appearance of + * the view has been changed, the currentAppearance, i.e. the + * appearance that will be used for drawing, may not have been changed + * to match. + * + * Prior to macOS 12.0 the currentAppearance property of NSAppearance + * was settable. In macOS 12.0 currentAppearance was deprecated and + * replaced by the read-only property currentDrawingAppearance. The + * ttk color issues are fixed by setting the currentAppearance to + * the effectiveAppearance of the view. So we are forced to use this + * deprecated function until Apple fixes this. + * + * It is a mystery why this only affects the ttk widgets. A possible + * clue is that when drawing a ttk widget this function is called with + * a NULL gc, whereas the gc is non-null when it is called for drawing + * a Tk widget. This means that the CGContext setup below is not done + * for ttk widgets. Perhaps that setup triggers an update of the + * currentAppearance property, but that has not been verified. + */ + + if (@available(macOS 12.0, *)) { + NSAppearance *current = NSAppearance.currentDrawingAppearance; + NSAppearance *effective = view.effectiveAppearance; + if( current != effective) { + // printf("Appearances are out of sync!\n"); + // Deprecations be damned! + NSAppearance.currentAppearance = effective; + } + } else { + /* + *It is not clear if this is a problem before macos 12.0, but + * we might as well do the update anyway. + */ + +#if MAC_OS_X_VERSION_MIN_REQUIRED < 120000 +/* currentAppearance is not deprecated. */ + NSAppearance.currentAppearance = view.effectiveAppearance; +#endif + } #else /* * We can only draw into the NSView which is the current focusView. @@ -1401,14 +1447,7 @@ TkMacOSXSetupDrawingContext( CGContextClipToRect(dc.context, r); } } -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 - if (@available(macOS 10.14, *)) { - dc.savedAppearance = NSAppearance.currentAppearance; - if (view) { - NSAppearance.currentAppearance = view.effectiveAppearance; - } - } -#endif + if (gc) { static const CGLineCap cgCap[] = { [CapNotLast] = kCGLineCapButt, @@ -1520,7 +1559,7 @@ TkMacOSXRestoreDrawingContext( } #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 if (@available(macOS 10.14, *)) { - NSAppearance.currentAppearance = dcPtr->savedAppearance; + //NSAppearance.currentAppearance = [dcPtr->view effectiveAppearance]; } #endif diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h index 70a2e22340..454d0d171a 100644 --- a/macosx/tkMacOSXPrivate.h +++ b/macosx/tkMacOSXPrivate.h @@ -207,9 +207,6 @@ typedef struct TkMacOSXDrawingContext { CGContextRef context; NSView *view; HIShapeRef clipRgn; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 - NSAppearance *savedAppearance; -#endif } TkMacOSXDrawingContext; /* diff --git a/macosx/tkMacOSXRegion.c b/macosx/tkMacOSXRegion.c index 4e185079a0..c11c6b797f 100644 --- a/macosx/tkMacOSXRegion.c +++ b/macosx/tkMacOSXRegion.c @@ -12,7 +12,6 @@ */ #include "tkMacOSXPrivate.h" -static void RetainRegion(TkRegion r); static void ReleaseRegion(TkRegion r); #ifdef DEBUG @@ -331,30 +330,6 @@ TkpBuildRegionFromAlphaData( } } -/* - *---------------------------------------------------------------------- - * - * RetainRegion -- - * - * Increases reference count of region. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -static void -RetainRegion( - TkRegion r) -{ - CFRetain(r); - DebugLog("Retained region: total count is %d\n", ++totalRegionRetainCount); -} - /* *---------------------------------------------------------------------- * From be7e1a71c0edac08079d3da630630fad7d15a7b0 Mon Sep 17 00:00:00 2001 From: fvogel Date: Wed, 19 Jul 2023 20:53:35 +0000 Subject: [PATCH 05/96] Fix [a510843631]: Restore macDraw statement. --- macosx/tkMacOSXDraw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index 5c9c0e834a..82242254e9 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -1131,6 +1131,7 @@ TkScrollWindow( if (XCopyArea(Tk_Display(tkwin), drawable, drawable, gc, x, y, (unsigned)width, (unsigned)height, x+dx, y+dy) == Success) { #else + MacDrawable* macDraw = (MacDrawable*)drawable; TKContentView *view = (TKContentView *)TkMacOSXGetNSViewForDrawable(macDraw); if (view) { #endif From d2ef41555d8bb64bf21f2065a56365aa840cda77 Mon Sep 17 00:00:00 2001 From: culler Date: Sun, 19 May 2024 02:41:36 +0000 Subject: [PATCH 06/96] Attempt to fix [22349fc78a] for macOS. Currently only event-9.16 fails unless there is a timeout waiting for an enter or leave event. --- generic/tkGrab.c | 16 ++ generic/tkPointer.c | 5 +- generic/tkWindow.c | 51 +++++ macosx/tkMacOSXInit.c | 12 ++ macosx/tkMacOSXMouseEvent.c | 20 +- macosx/tkMacOSXSubwindows.c | 1 + macosx/tkMacOSXWindowEvent.c | 2 + macosx/tkMacOSXWm.c | 71 ++++--- tests/event.test | 350 ++++++++++++++++++++++++++++++----- 9 files changed, 454 insertions(+), 74 deletions(-) diff --git a/generic/tkGrab.c b/generic/tkGrab.c index 2232ba5ded..c5f2a3b230 100644 --- a/generic/tkGrab.c +++ b/generic/tkGrab.c @@ -998,6 +998,10 @@ TkInOutEvents( { TkWindow *winPtr; int upLevels, downLevels, i, j, focus; + //fprintf(stderr, "TkInOutEvents: source is %s, destination is %s, leaveType is %d, enterType is %d\n", + // sourcePtr ? Tk_PathName(sourcePtr) : "NULL", + // destPtr ? Tk_PathName(destPtr) : "NULL", + // leaveType, enterType); /* * There are four possible cases to deal with: @@ -1025,6 +1029,7 @@ TkInOutEvents( focus = 0; } FindCommonAncestor(sourcePtr, destPtr, &upLevels, &downLevels); + //fprintf(stderr, "upLevels = %d, downLevels = %d\n", upLevels, downLevels); /* * Generate enter/leave events and add them to the grab event queue. @@ -1083,10 +1088,15 @@ TkInOutEvents( * Non-linear: neither window is an inferior of the other. */ + //fprintf(stderr, " Nonlinear path\n"); if (leaveType != 0) { + //fprintf(stderr, " Queueing leave event for %s\n", + // Tk_PathName(sourcePtr)); QUEUE(sourcePtr, leaveType, NotifyNonlinear); for (winPtr = sourcePtr->parentPtr, i = upLevels-1; i > 0; winPtr = winPtr->parentPtr, i--) { + //fprintf(stderr, " Queueing Virtual leave event for %s\n", + // Tk_PathName(winPtr)); QUEUE(winPtr, leaveType, NotifyNonlinearVirtual); } } @@ -1095,13 +1105,18 @@ TkInOutEvents( for (winPtr = destPtr->parentPtr, j = 1; j < i; winPtr = winPtr->parentPtr, j++) { } + //fprintf(stderr, " Queueing Virtual enter event for %s\n", + // Tk_PathName(winPtr)); QUEUE(winPtr, enterType, NotifyNonlinearVirtual); } if (destPtr != NULL) { + //fprintf(stderr, " Queueing enter event for %s\n", + // Tk_PathName(destPtr)); QUEUE(destPtr, enterType, NotifyNonlinear); } } } + fflush(stderr); } /* @@ -1167,6 +1182,7 @@ MovePointer2( TkInOutEvents(&event, sourcePtr, destPtr, (leaveEvents) ? LeaveNotify : 0, (enterEvents) ? EnterNotify : 0, TCL_QUEUE_MARK); } + /* *---------------------------------------------------------------------- diff --git a/generic/tkPointer.c b/generic/tkPointer.c index 905489a72f..2b5b40e01b 100644 --- a/generic/tkPointer.c +++ b/generic/tkPointer.c @@ -183,7 +183,6 @@ GenerateEnterLeave( InitializeEvent(&event, targetPtr, LeaveNotify, x, y, state, NotifyNormal); - TkInOutEvents(&event, lastWinPtr, winPtr, LeaveNotify, EnterNotify, TCL_QUEUE_TAIL); crossed = 1; @@ -228,6 +227,10 @@ Tk_UpdatePointer( unsigned changes = (state ^ tsdPtr->lastState) & ALL_BUTTONS; int type, b; unsigned mask; + //fprintf(stderr, " Tk_UpdatePointer: %s %d %d %x --> %s %d %d %x\n", + // tsdPtr->lastWinPtr ? Tk_PathName(tsdPtr->lastWinPtr): "NULL", + // tsdPtr->lastPos.x, tsdPtr->lastPos.y, tsdPtr->lastState, + // tkwin ? Tk_PathName(tkwin) : "NULL", x, y, state); pos.x = x; pos.y = y; diff --git a/generic/tkWindow.c b/generic/tkWindow.c index 5d664b957d..5c3700b44f 100644 --- a/generic/tkWindow.c +++ b/generic/tkWindow.c @@ -1322,6 +1322,33 @@ Tk_CreateWindowFromPath( *-------------------------------------------------------------- */ + +static void SendEnterLeaveForDestroy( + Tk_Window tkwin) +{ + int x, y; + unsigned int state; + Tk_Window pointerWin; + TkWindow *containerPtr; + + XQueryPointer(NULL, None, NULL, NULL, &x, &y, NULL, NULL, &state); + pointerWin = Tk_CoordsToWindow(x, y, tkwin); + if (pointerWin == tkwin) { + //fprintf(stderr, " Pointer window is being destroyed\n"); + if (!Tk_IsTopLevel(tkwin)) { + containerPtr = TkGetContainer((TkWindow *)pointerWin); + //fprintf(stderr, " Moving pointer from topmost %s to container %s\n", + // pointerWin ? Tk_PathName(pointerWin) : "NULL", + // containerPtr ? Tk_PathName(containerPtr) : "NULL"); + Tk_UpdatePointer((Tk_Window) containerPtr, x, y, state); + } + // else { + //fprintf(stderr, " Pointer window is a toplevel\n"); + // } + } + // fflush(stderr); +} + void Tk_DestroyWindow( Tk_Window tkwin) /* Window to destroy. */ @@ -1341,6 +1368,10 @@ Tk_DestroyWindow( return; } + //fprintf(stderr, "Tk_DestroyWindow: destroying %s\n", Tk_PathName(tkwin)); + //fflush(stderr); + SendEnterLeaveForDestroy(tkwin); + winPtr->flags |= TK_ALREADY_DEAD; /* @@ -1711,6 +1742,22 @@ Tk_DestroyWindow( *-------------------------------------------------------------- */ +static void SendEnterLeaveForMap( + Tk_Window tkwin) +{ + int x, y; + unsigned int state; + Tk_Window pointerWin; + + XQueryPointer(NULL, None, NULL, NULL, &x, &y, NULL, NULL, &state); + pointerWin = Tk_CoordsToWindow(x, y, tkwin); + if (pointerWin == tkwin) { + //fprintf(stderr, " New window contains pointer.\n"); + //fflush(stderr); + Tk_UpdatePointer(tkwin, x, y, state); + } +} + void Tk_MapWindow( Tk_Window tkwin) /* Token for window to map. */ @@ -1740,6 +1787,7 @@ Tk_MapWindow( TkWmMapWindow(winPtr); return; } + winPtr->flags |= TK_MAPPED; XMapWindow(winPtr->display, winPtr->window); event.type = MapNotify; @@ -1750,6 +1798,9 @@ Tk_MapWindow( event.xmap.window = winPtr->window; event.xmap.override_redirect = winPtr->atts.override_redirect; Tk_HandleEvent(&event); + //fprintf(stderr, "Tk_MapWindow: mapped %s\n", Tk_PathName(tkwin)); + //fflush(stderr); + SendEnterLeaveForMap(tkwin); } /* diff --git a/macosx/tkMacOSXInit.c b/macosx/tkMacOSXInit.c index e6a68356c4..aa3cd7a6d8 100644 --- a/macosx/tkMacOSXInit.c +++ b/macosx/tkMacOSXInit.c @@ -424,6 +424,18 @@ TCL_NORETURN void TkpExitProc( closePanels(); } + /* + * At this point it is too late to be looking up the Tk window associated + * to any NSWindows, but it can happen. This makes sure the answer is None + * if such a query is attempted. + */ + + for (TKWindow *w in [NSApp orderedWindows]) { + if ([w respondsToSelector: @selector (tkWindow)]) { + [w setTkWindow: None]; + } + } + /* * Tcl_Exit does not call Tcl_Finalize if there is an exit proc installed. */ diff --git a/macosx/tkMacOSXMouseEvent.c b/macosx/tkMacOSXMouseEvent.c index 9b88eb932e..8d94300585 100644 --- a/macosx/tkMacOSXMouseEvent.c +++ b/macosx/tkMacOSXMouseEvent.c @@ -199,6 +199,7 @@ enum { int fakeState = [NSApp tkButtonState] & ~TkGetButtonMask(Button1); int x = location.x; int y = floor(TkMacOSXZeroScreenHeight() - location.y); + //fprintf(stderr, "(1) calling Tk_UpdatePointer)\n"); fflush(stderr); Tk_UpdatePointer((Tk_Window) [NSApp tkEventTarget], x, y, fakeState); } @@ -500,18 +501,31 @@ enum { state |= TkGetButtonMask(Button1); } if (eventType == NSMouseEntered) { - Tk_UpdatePointer((Tk_Window) [NSApp tkPointerWindow], - global.x, global.y, state); + //fprintf(stderr, "handling NSMouseEntered for %s\n", + //Tk_PathName([NSApp tkPointerWindow])); + //fflush(stderr); + Tk_Window new_win = Tk_CoordsToWindow(global.x, global.y, + (Tk_Window) [NSApp tkPointerWindow]); + // WHEN SHOULD THIS CALL HAPPEN? + // It only happens for toplevels, but can happen when a window is + // deiconified, in which case the highest Tk window containing the + // mouse should be the target. + //fprintf(stderr, "(2) calling Tk_UpdatePointer)\n"); fflush(stderr); + Tk_UpdatePointer(new_win, global.x, global.y, state); } else if (eventType == NSMouseExited) { + //fprintf(stderr, "handling NSMouseExited\n"); fflush(stderr); if ([NSApp tkDragTarget]) { + //fprintf(stderr, "(3) calling Tk_UpdatePointer)\n"); fflush(stderr); Tk_UpdatePointer((Tk_Window) [NSApp tkDragTarget], global.x, global.y, state); } else { + //fprintf(stderr, "(4) calling Tk_UpdatePointer)\n"); fflush(stderr); Tk_UpdatePointer(NULL, global.x, global.y, state); } } else if (eventType == NSMouseMoved || eventType == NSLeftMouseDragged) { if ([NSApp tkPointerWindow]) { + //fprintf(stderr, "(5) calling Tk_UpdatePointer)\n"); fflush(stderr); Tk_UpdatePointer(target, global.x, global.y, state); } else { XEvent xEvent = {0}; @@ -536,6 +550,7 @@ enum { } } else { + //fprintf(stderr, "(6) calling Tk_UpdatePointer)\n"); fflush(stderr); Tk_UpdatePointer(target, global.x, global.y, state); } } else { @@ -825,6 +840,7 @@ GenerateButtonEvent( tkwin = Tk_TopCoordsToWindow(tkwin, medPtr->local.h, medPtr->local.v, &dummy, &dummy); } + //fprintf(stderr, "(7) calling Tk_UpdatePointer)\n"); fflush(stderr); Tk_UpdatePointer(tkwin, medPtr->global.h, medPtr->global.v, medPtr->state); return true; } diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index fb71a0c922..732ed1d6a7 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -197,6 +197,7 @@ XMapWindow( NSPoint viewLocation = [view convertPoint:mouse fromView:nil]; if (NSPointInRect(viewLocation, NSInsetRect([view bounds], 2, 2))) { + fprintf(stderr, "XMapWindow: calling Tk_UpdatePointer\n"); Tk_UpdatePointer((Tk_Window) winPtr, x, y, [NSApp tkButtonState]); } } else { diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 09112eebbf..4e40bb6c18 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -74,6 +74,8 @@ extern NSString *NSWindowDidOrderOffScreenNotification; NSView *view = [w contentView]; NSPoint viewLocation = [view convertPoint:location fromView:nil]; if (NSPointInRect(viewLocation, NSInsetRect([view bounds], 2, 2))) { + fprintf(stderr, "windowActivation calling TkUpdatePointer\n"); + fflush(stderr); Tk_UpdatePointer((Tk_Window) winPtr, x, y, [NSApp tkButtonState]); } } diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index 2fa9434785..1ada28917b 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -911,13 +911,17 @@ void TkWmDeadWindow( TkWindow *winPtr) /* Top-level window that's being deleted. */ { + TkWindow *winPtr2; WmInfo *wmPtr = winPtr->wmInfoPtr, *wmPtr2; - TKWindow *deadNSWindow; - if (wmPtr == NULL) { return; } - + TKWindow *deadNSWindow = (TKWindow *)TkMacOSXGetNSWindowForDrawable( + Tk_WindowId(winPtr)); + //fprintf(stderr, "TkWmDeadWindow: will destroy %s\n", Tk_PathName(winPtr)); + if (deadNSWindow == NULL) { + return; + } /* *If the dead window is a transient, remove it from the container's list. */ @@ -985,31 +989,49 @@ TkWmDeadWindow( ckfree(transientPtr); } - deadNSWindow = (TKWindow *)wmPtr->window; - /* * Remove references to the Tk window from the mouse event processing - * state which is recorded in the NSApplication object. + * state which is recorded in the NSApplication object and notify Tk + * of the new pointer window. */ - if (winPtr == [NSApp tkPointerWindow]) { - NSWindow *w; - NSPoint mouse = [NSEvent mouseLocation]; - [NSApp setTkPointerWindow:nil]; - for (w in [NSApp orderedWindows]) { - if (w == deadNSWindow) { - continue; - } - if (NSPointInRect(mouse, [w frame])) { - TkWindow *winPtr2 = TkMacOSXGetTkWindow(w); - int x = mouse.x, y = TkMacOSXZeroScreenHeight() - mouse.y; - [NSApp setTkPointerWindow:winPtr2]; - Tk_UpdatePointer((Tk_Window) winPtr2, x, y, - [NSApp tkButtonState]); - break; - } + NSPoint mouse = [NSEvent mouseLocation]; + NSWindow *w; + [NSApp setTkPointerWindow:nil]; + //fprintf(stderr, " Looking for new pointer window\n"); + winPtr2 = NULL; + + for (w in [NSApp orderedWindows]) { + if (w == deadNSWindow || w == NULL) { + continue; + } + winPtr2 = TkMacOSXGetTkWindow(w); + if (winPtr2 == NULL) { + continue; + } + if (NSPointInRect(mouse, [w frame])) { + [NSApp setTkPointerWindow: winPtr2]; + break; } } + if (winPtr2) { + /* + * We now know which toplevel will contain the pointer when the window + * is destroyed. We need to know which Tk window within the + * toplevel will contain the pointer. + */ + NSPoint local = [w tkConvertPointFromScreen: mouse]; + int top_x = floor(local.x), + top_y = floor(w.frame.size.height - local.y); + int root_x = floor(mouse.x), + root_y = floor(TkMacOSXZeroScreenHeight() - mouse.y); + int win_x, win_y; + Tk_Window target = Tk_TopCoordsToWindow((Tk_Window) winPtr2, top_x, top_y, &win_x, &win_y); + //fprintf(stderr, " new mouseWindow is %s\n", + // target ? Tk_PathName(target) : "NULL"); + Tk_UpdatePointer((Tk_Window) target, root_x, root_y, [NSApp tkButtonState]); + } +// fflush(stderr); /* * Unregister the NSWindow and remove all references to it from the Tk @@ -1017,7 +1039,7 @@ TkWmDeadWindow( * the parent. Then close and release the NSWindow. */ - if (deadNSWindow && !Tk_IsEmbedded(winPtr)) { + if (0 && deadNSWindow && winPtr && !Tk_IsEmbedded(winPtr)) { NSWindow *parent = [deadNSWindow parentWindow]; [deadNSWindow setTkWindow:None]; if (winPtr->window) { @@ -1059,9 +1081,10 @@ TkWmDeadWindow( * set tkEventTarget to NULL when there is no window to send Tk events to. */ TkWindow *newTkEventTarget = NULL; + winPtr2 = NULL; for (NSWindow *w in [NSApp orderedWindows]) { - TkWindow *winPtr2 = TkMacOSXGetTkWindow(w); + winPtr2 = TkMacOSXGetTkWindow(w); BOOL isOnScreen; if (!winPtr2 || !winPtr2->wmInfoPtr) { diff --git a/tests/event.test b/tests/event.test index 015720e2dd..e398b8705d 100644 --- a/tests/event.test +++ b/tests/event.test @@ -311,6 +311,7 @@ test event-2.5(keypress) {type into text widget and then delete some text} -setu test event-2.6(keypress) {type into text widget, triple click, hit Delete key, and then type some more} -setup { deleteWindows + update idletasks } -body { set t [toplevel .t] set e [text $t.e] @@ -861,60 +862,313 @@ test event-8 {event generate with keysyms corresponding to deleteWindows } -result {OK} -test event-9.1 {enter . window by destroying a toplevel - bug b1d115fa60} -setup { - set EnterBind [bind . ] -} -body { - wm geometry . 200x200+300+300 - wm deiconify . +proc waitForWindowEvent {w event {timeout 1000}} { +# This proc is intended to overcome latency of windowing system +# notifications when toplevel windows are involved. These latencies vary +# considerably with the window manager in use, with the system load, +# with configured scheduling priorities for processes, etc ... +# Waiting for the corresponding window events evades the trouble that is +# associated with the alternative: waiting or halting the Tk process for a +# fixed amount of time (using "after ms"). With the latter strategy it's +# always a gamble how much waiting time is enough on an end user's system. +# It also leads to long fixed waiting times in order to be on the safe side. + + variable _windowEvent + + # Use counter as a unique ID to prevent subsequent waits + # from interfering with each other. + set counter [incr _windowEvent(counter)] + set _windowEvent($counter) 1 + set savedBinding [bind $w $event] + bind $w $event [list +waitForWindowEvent.signal $counter] + set afterID [after $timeout [list set _windowEvent($counter) -1]] + vwait _windowEvent($counter) + set late [expr {$_windowEvent($counter) == -1}] + bind $w $event $savedBinding + unset _windowEvent($counter) + if {$late} { + puts "waiting for $event event on $w timed out (> $timeout ms)" + } else { + after cancel $afterID + } +} +proc waitForWindowEvent.signal {counter} { +# Helper proc that records the triggering of a window event. + incr ::_windowEvent($counter) +} + +proc create_and_pack_frames {{w {}}} { + frame $w.f1 -bg blue -width 200 -height 200 + pack propagate $w.f1 0 + frame $w.f1.f2 -bg yellow -width 100 -height 100 + pack $w.f1.f2 $w.f1 -side bottom -anchor se +} + +proc setup_win_mousepointer {w} { +# Position the window and the mouse pointer as an initial state for some tests. +# The so-called "pointer window" is the $w window that will now contain the mouse pointer. + wm geometry . +700+700; # root window out of our way - must not cover windows from event-9.1* + toplevel $w + pack propagate $w 0 + wm geometry $w 300x300+100+100 + tkwait visibility $w + update; # service remaining screen drawing events (e.g. ) + set pointerWin [winfo containing [winfo pointerx $w] [winfo pointery $w]] + event generate $w -warp 1 -x 250 -y 250 +# if {[tk windowingsystem] eq "aqua"} { +# # Generate a NSMouseMoved NSevent with no mouse pointer position change. +# # This is to let event-9.1? tests pass on macOS aqua and is only a workaround +# # since macOS aqua should send the adequate NSevent by itself. +# movemouse [expr {[winfo rootx $w] + 250}] [expr {[winfo rooty $w] + 250}] +# } + if {($pointerWin ne $w) && ([tk windowingsystem] ne "aqua")} { + waitForWindowEvent $w + } else { + controlPointerWarpTiming + } +} + +test event-9.11 {pointer window container = parent} -setup { + setup_win_mousepointer .one + wm withdraw .one + create_and_pack_frames .one + wm deiconify .one + tkwait visibility .one.f1.f2 _pause 200 - toplevel .top2 -width 200 -height 200 - wm geometry .top2 +[expr {[winfo rootx .]+50}]+[expr {[winfo rooty .]+50}] - wm deiconify .top2 - raise .top2 - _pause 400 - event generate .top2 -warp 1 -x 50 -y 50 - _pause 100 - bind . {lappend res %W} - set res [list ] - destroy .top2 + bind all {append result " %d %W|"} + bind all {append result " %d %W|"} + set result "|" +} -body { + destroy .one.f1.f2 +# if {[tk windowingsystem] eq "aqua"} { +# # Generate a NSMouseMoved NSevent with no mouse pointer position change. +# # This is to let event-9.1? tests pass on macOS aqua and is only a workaround +# # since macOS aqua should send the adequate NSevent by itself. +# movemouse [expr {[winfo rootx .one] + 250}] [expr {[winfo rooty .one] + 250}] +# } + _pause 200; # service crossing events + set result +} -cleanup { + bind all {} + bind all {} + destroy .one + unset result +} -result {| NotifyInferior .one.f1|} + +test event-9.12 {pointer window container != parent} -setup { + setup_win_mousepointer .one + wm withdraw .one + create_and_pack_frames .one + pack propagate .one.f1.f2 0 + pack [frame .one.g -bg orange -width 80 -height 80] -anchor se -side bottom -in .one.f1.f2 + wm deiconify .one + tkwait visibility .one.g + event generate .one -warp 1 -x 250 -y 250 _pause 200 - set res + bind all {append result " %d %W|"} + bind all {append result " %d %W|"} + set result "|" +} -body { + destroy .one.g + _pause 200; # service crossing events -- crashes without this + set result } -cleanup { - deleteWindows - bind . $EnterBind -} -result {.} -test event-9.2 {enter toplevel window by destroying a toplevel - bug b1d115fa60} -setup { - set iconified false - if {[winfo ismapped .]} { - wm iconify . - update - set iconified true - } + bind all {} + bind all {} + destroy .one + unset result +} -result {| NotifyNonlinearVirtual .one.f1| NotifyNonlinear .one.f1.f2|} + +test event-9.13 {pointer window is a toplevel, toplevel destination} -setup { + setup_win_mousepointer .one + wm withdraw .one + toplevel .two + wm geometry .two 300x300+150+150 + wm withdraw .two + wm deiconify .one + wm deiconify .two + waitForWindowEvent .two + bind all {append result " %d %W|"} + bind all {append result " %d %W|"} + set result "|" } -body { - toplevel .top1 - wm geometry .top1 200x200+300+300 - wm deiconify .top1 - _pause 200 - toplevel .top2 -width 200 -height 200 - wm geometry .top2 +[expr {[winfo rootx .top1]+50}]+[expr {[winfo rooty .top1]+50}] + destroy .two + waitForWindowEvent .one + set result +} -cleanup { + bind all {} + bind all {} + destroy .one + unset result +} -result {| NotifyNonlinear .one|} + +test event-9.14 {pointer window is a toplevel, tk internal destination} -setup { + setup_win_mousepointer .one + wm withdraw .one + create_and_pack_frames .one + toplevel .two + wm geometry .two 300x300+150+150 + wm withdraw .two + wm deiconify .one + wm deiconify .two + waitForWindowEvent .two + bind all {append result " %d %W|"} + bind all {append result " %d %W|"} + set result "|" +} -body { + destroy .two + waitForWindowEvent .one.f1.f2 + set result +} -cleanup { + bind all {} + bind all {} + destroy .one + unset result +} -result {| NotifyNonlinearVirtual .one| NotifyNonlinearVirtual .one.f1| NotifyNonlinear .one.f1.f2|} + +test event-9.15 {pointer window is a toplevel, destination is screen root} -setup { + setup_win_mousepointer .one; # ensure the mouse pointer is where we want it to be (the .one toplevel is not itself used in this test) + toplevel .two + wm geometry .two 300x300+150+150 + wm deiconify .two + waitForWindowEvent .two + event generate .two -warp 1 -x 275 -y 275 + controlPointerWarpTiming + bind all {append result " %d %W|"} + bind all {append result " %d %W|"} + set result "|" +} -body { + destroy .two + _pause 200; # ensure servicing of all scheduled events (only events expected) + set result +} -cleanup { + bind all {} + bind all {} + destroy .one + unset result +} -result {|} + +test event-9.16 {Successive destructions (pointer window + parent), single generation of crossing events} -setup { + # Tests correctness of overwriting the dead window struct in + # TkPointerDeadWindow() and subsequent reading in GenerateEnterLeave(). + setup_win_mousepointer .one + wm withdraw .one + create_and_pack_frames .one + wm deiconify .one + tkwait visibility .one.f1.f2 _pause 200 - wm deiconify .top2 - raise .top2 - _pause 400 - event generate .top2 -warp 1 -x 50 -y 50 - _pause 100 - bind .top1 {lappend res %W} - set res [list ] - destroy .top2 + bind all {append result " %d %W|"} + bind all {append result " %d %W|"} + set result "|" +} -body { + destroy .one.f1 + _pause 200; # service crossing events + set result +} -cleanup { + bind all {} + bind all {} + destroy .one + unset result +} -result {| NotifyInferior .one|} + +test event-9.17 {Successive destructions (pointer window + parent), separate crossing events} -setup { + # Tests correctness of overwriting the dead window struct in + # TkPointerDeadWindow() and subsequent reading in GenerateEnterLeave(). + setup_win_mousepointer .one + wm withdraw .one + create_and_pack_frames .one + wm deiconify .one + tkwait visibility .one.f1.f2 _pause 200 - set res + bind all {append result " %d %W|"} + bind all {append result " %d %W|"} + set result "|" +} -body { + destroy .one.f1.f2 + _pause 200; # service crossing events + destroy .one.f1 + _pause 200; # service crossing events + set result } -cleanup { - deleteWindows ; # destroy all children of ".", this already includes .top1 - if {$iconified} { - wm deiconify . - update - } -} -result {.top1} + bind all {} + bind all {} + destroy .one + unset result +} -result {| NotifyInferior .one.f1| NotifyInferior .one|} + +test event-9.18 {Successive destructions (pointer window + ancestors including its toplevel), destination is non-root toplevel} -setup { + setup_win_mousepointer .one + toplevel .two + pack propagate .two 0 + wm geometry .two 300x300+100+100 + create_and_pack_frames .two + wm deiconify .two + waitForWindowEvent .two.f1.f2 + bind all {append result " %d %W|"} + bind all {append result " %d %W|"} + set result "|" +} -body { + destroy .two + waitForWindowEvent .one + set result +} -cleanup { + bind all {} + bind all {} + destroy .one + unset result +} -result {| NotifyNonlinear .one|} + +test event-9.19 {Successive destructions (pointer window + ancestors including its toplevel), destination is internal window, bypass root win} -setup { + setup_win_mousepointer .one; # ensure the mouse pointer is where we want it to be (the .one toplevel is not itself used in this test) + toplevel .two + pack propagate .two 0 + wm geometry .two 300x300+100+100 + create_and_pack_frames .two + wm deiconify .two + waitForWindowEvent .two.f1.f2 + toplevel .three + pack propagate .three 0 + wm geometry .three 300x300+110+110 + create_and_pack_frames .three + wm deiconify .three + waitForWindowEvent .three.f1.f2 + bind all {append result " %d %W|"} + bind all {append result " %d %W|"} + set result "|" +} -body { + destroy .three + waitForWindowEvent .two.f1.f2 + set result +} -cleanup { + bind all {} + bind all {} + destroy .two + destroy .one + unset result +} -result {| NotifyNonlinearVirtual .two| NotifyNonlinearVirtual .two.f1| NotifyNonlinear .two.f1.f2|} + +test event-9.20 {Successive destructions (pointer window + ancestors including its toplevel), destination is screen root} -setup { + setup_win_mousepointer .one; # ensure the mouse pointer is where we want it to be (the .one toplevel is not itself used in this test) + wm withdraw .one + toplevel .two + pack propagate .two 0 + wm geometry .two 300x300+100+100 + create_and_pack_frames .two + wm deiconify .two + waitForWindowEvent .two.f1.f2 + bind all {append result " %d %W|"} + bind all {append result " %d %W|"} + set result "|" +} -body { + destroy .two + _pause 200; # service events (only screen drawing events expected) + set result +} -cleanup { + bind all {} + bind all {} + destroy .one + unset result +} -result {|} # cleanup update @@ -925,6 +1179,8 @@ rename _keypress {} rename _pause {} rename _text_ind_to_x_y {} rename _get_selection {} +rename create_and_pack_frames {} +rename setup_win_mousepointer {} cleanupTests return From c9eed3be52a8f16837c1aaf6099edc1668739832 Mon Sep 17 00:00:00 2001 From: culler Date: Sun, 19 May 2024 03:08:05 +0000 Subject: [PATCH 07/96] Remove debugging print statements. --- generic/tkGrab.c | 15 --------------- generic/tkPointer.c | 4 ---- generic/tkWindow.c | 14 -------------- macosx/tkMacOSXMouseEvent.c | 15 --------------- macosx/tkMacOSXSubwindows.c | 1 - macosx/tkMacOSXWm.c | 5 ----- 6 files changed, 54 deletions(-) diff --git a/generic/tkGrab.c b/generic/tkGrab.c index c5f2a3b230..c8e52539a1 100644 --- a/generic/tkGrab.c +++ b/generic/tkGrab.c @@ -998,10 +998,6 @@ TkInOutEvents( { TkWindow *winPtr; int upLevels, downLevels, i, j, focus; - //fprintf(stderr, "TkInOutEvents: source is %s, destination is %s, leaveType is %d, enterType is %d\n", - // sourcePtr ? Tk_PathName(sourcePtr) : "NULL", - // destPtr ? Tk_PathName(destPtr) : "NULL", - // leaveType, enterType); /* * There are four possible cases to deal with: @@ -1029,7 +1025,6 @@ TkInOutEvents( focus = 0; } FindCommonAncestor(sourcePtr, destPtr, &upLevels, &downLevels); - //fprintf(stderr, "upLevels = %d, downLevels = %d\n", upLevels, downLevels); /* * Generate enter/leave events and add them to the grab event queue. @@ -1088,15 +1083,10 @@ TkInOutEvents( * Non-linear: neither window is an inferior of the other. */ - //fprintf(stderr, " Nonlinear path\n"); if (leaveType != 0) { - //fprintf(stderr, " Queueing leave event for %s\n", - // Tk_PathName(sourcePtr)); QUEUE(sourcePtr, leaveType, NotifyNonlinear); for (winPtr = sourcePtr->parentPtr, i = upLevels-1; i > 0; winPtr = winPtr->parentPtr, i--) { - //fprintf(stderr, " Queueing Virtual leave event for %s\n", - // Tk_PathName(winPtr)); QUEUE(winPtr, leaveType, NotifyNonlinearVirtual); } } @@ -1105,18 +1095,13 @@ TkInOutEvents( for (winPtr = destPtr->parentPtr, j = 1; j < i; winPtr = winPtr->parentPtr, j++) { } - //fprintf(stderr, " Queueing Virtual enter event for %s\n", - // Tk_PathName(winPtr)); QUEUE(winPtr, enterType, NotifyNonlinearVirtual); } if (destPtr != NULL) { - //fprintf(stderr, " Queueing enter event for %s\n", - // Tk_PathName(destPtr)); QUEUE(destPtr, enterType, NotifyNonlinear); } } } - fflush(stderr); } /* diff --git a/generic/tkPointer.c b/generic/tkPointer.c index 2b5b40e01b..ae3bef932f 100644 --- a/generic/tkPointer.c +++ b/generic/tkPointer.c @@ -227,10 +227,6 @@ Tk_UpdatePointer( unsigned changes = (state ^ tsdPtr->lastState) & ALL_BUTTONS; int type, b; unsigned mask; - //fprintf(stderr, " Tk_UpdatePointer: %s %d %d %x --> %s %d %d %x\n", - // tsdPtr->lastWinPtr ? Tk_PathName(tsdPtr->lastWinPtr): "NULL", - // tsdPtr->lastPos.x, tsdPtr->lastPos.y, tsdPtr->lastState, - // tkwin ? Tk_PathName(tkwin) : "NULL", x, y, state); pos.x = x; pos.y = y; diff --git a/generic/tkWindow.c b/generic/tkWindow.c index 5c3700b44f..3703d369d7 100644 --- a/generic/tkWindow.c +++ b/generic/tkWindow.c @@ -1334,19 +1334,11 @@ static void SendEnterLeaveForDestroy( XQueryPointer(NULL, None, NULL, NULL, &x, &y, NULL, NULL, &state); pointerWin = Tk_CoordsToWindow(x, y, tkwin); if (pointerWin == tkwin) { - //fprintf(stderr, " Pointer window is being destroyed\n"); if (!Tk_IsTopLevel(tkwin)) { containerPtr = TkGetContainer((TkWindow *)pointerWin); - //fprintf(stderr, " Moving pointer from topmost %s to container %s\n", - // pointerWin ? Tk_PathName(pointerWin) : "NULL", - // containerPtr ? Tk_PathName(containerPtr) : "NULL"); Tk_UpdatePointer((Tk_Window) containerPtr, x, y, state); } - // else { - //fprintf(stderr, " Pointer window is a toplevel\n"); - // } } - // fflush(stderr); } void @@ -1368,8 +1360,6 @@ Tk_DestroyWindow( return; } - //fprintf(stderr, "Tk_DestroyWindow: destroying %s\n", Tk_PathName(tkwin)); - //fflush(stderr); SendEnterLeaveForDestroy(tkwin); winPtr->flags |= TK_ALREADY_DEAD; @@ -1752,8 +1742,6 @@ static void SendEnterLeaveForMap( XQueryPointer(NULL, None, NULL, NULL, &x, &y, NULL, NULL, &state); pointerWin = Tk_CoordsToWindow(x, y, tkwin); if (pointerWin == tkwin) { - //fprintf(stderr, " New window contains pointer.\n"); - //fflush(stderr); Tk_UpdatePointer(tkwin, x, y, state); } } @@ -1798,8 +1786,6 @@ Tk_MapWindow( event.xmap.window = winPtr->window; event.xmap.override_redirect = winPtr->atts.override_redirect; Tk_HandleEvent(&event); - //fprintf(stderr, "Tk_MapWindow: mapped %s\n", Tk_PathName(tkwin)); - //fflush(stderr); SendEnterLeaveForMap(tkwin); } diff --git a/macosx/tkMacOSXMouseEvent.c b/macosx/tkMacOSXMouseEvent.c index 8d94300585..f942485267 100644 --- a/macosx/tkMacOSXMouseEvent.c +++ b/macosx/tkMacOSXMouseEvent.c @@ -199,7 +199,6 @@ enum { int fakeState = [NSApp tkButtonState] & ~TkGetButtonMask(Button1); int x = location.x; int y = floor(TkMacOSXZeroScreenHeight() - location.y); - //fprintf(stderr, "(1) calling Tk_UpdatePointer)\n"); fflush(stderr); Tk_UpdatePointer((Tk_Window) [NSApp tkEventTarget], x, y, fakeState); } @@ -501,31 +500,19 @@ enum { state |= TkGetButtonMask(Button1); } if (eventType == NSMouseEntered) { - //fprintf(stderr, "handling NSMouseEntered for %s\n", - //Tk_PathName([NSApp tkPointerWindow])); - //fflush(stderr); Tk_Window new_win = Tk_CoordsToWindow(global.x, global.y, (Tk_Window) [NSApp tkPointerWindow]); - // WHEN SHOULD THIS CALL HAPPEN? - // It only happens for toplevels, but can happen when a window is - // deiconified, in which case the highest Tk window containing the - // mouse should be the target. - //fprintf(stderr, "(2) calling Tk_UpdatePointer)\n"); fflush(stderr); Tk_UpdatePointer(new_win, global.x, global.y, state); } else if (eventType == NSMouseExited) { - //fprintf(stderr, "handling NSMouseExited\n"); fflush(stderr); if ([NSApp tkDragTarget]) { - //fprintf(stderr, "(3) calling Tk_UpdatePointer)\n"); fflush(stderr); Tk_UpdatePointer((Tk_Window) [NSApp tkDragTarget], global.x, global.y, state); } else { - //fprintf(stderr, "(4) calling Tk_UpdatePointer)\n"); fflush(stderr); Tk_UpdatePointer(NULL, global.x, global.y, state); } } else if (eventType == NSMouseMoved || eventType == NSLeftMouseDragged) { if ([NSApp tkPointerWindow]) { - //fprintf(stderr, "(5) calling Tk_UpdatePointer)\n"); fflush(stderr); Tk_UpdatePointer(target, global.x, global.y, state); } else { XEvent xEvent = {0}; @@ -550,7 +537,6 @@ enum { } } else { - //fprintf(stderr, "(6) calling Tk_UpdatePointer)\n"); fflush(stderr); Tk_UpdatePointer(target, global.x, global.y, state); } } else { @@ -840,7 +826,6 @@ GenerateButtonEvent( tkwin = Tk_TopCoordsToWindow(tkwin, medPtr->local.h, medPtr->local.v, &dummy, &dummy); } - //fprintf(stderr, "(7) calling Tk_UpdatePointer)\n"); fflush(stderr); Tk_UpdatePointer(tkwin, medPtr->global.h, medPtr->global.v, medPtr->state); return true; } diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index 732ed1d6a7..fb71a0c922 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -197,7 +197,6 @@ XMapWindow( NSPoint viewLocation = [view convertPoint:mouse fromView:nil]; if (NSPointInRect(viewLocation, NSInsetRect([view bounds], 2, 2))) { - fprintf(stderr, "XMapWindow: calling Tk_UpdatePointer\n"); Tk_UpdatePointer((Tk_Window) winPtr, x, y, [NSApp tkButtonState]); } } else { diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index 1ada28917b..952da39618 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -918,7 +918,6 @@ TkWmDeadWindow( } TKWindow *deadNSWindow = (TKWindow *)TkMacOSXGetNSWindowForDrawable( Tk_WindowId(winPtr)); - //fprintf(stderr, "TkWmDeadWindow: will destroy %s\n", Tk_PathName(winPtr)); if (deadNSWindow == NULL) { return; } @@ -998,7 +997,6 @@ TkWmDeadWindow( NSPoint mouse = [NSEvent mouseLocation]; NSWindow *w; [NSApp setTkPointerWindow:nil]; - //fprintf(stderr, " Looking for new pointer window\n"); winPtr2 = NULL; for (w in [NSApp orderedWindows]) { @@ -1027,11 +1025,8 @@ TkWmDeadWindow( root_y = floor(TkMacOSXZeroScreenHeight() - mouse.y); int win_x, win_y; Tk_Window target = Tk_TopCoordsToWindow((Tk_Window) winPtr2, top_x, top_y, &win_x, &win_y); - //fprintf(stderr, " new mouseWindow is %s\n", - // target ? Tk_PathName(target) : "NULL"); Tk_UpdatePointer((Tk_Window) target, root_x, root_y, [NSApp tkButtonState]); } -// fflush(stderr); /* * Unregister the NSWindow and remove all references to it from the Tk From 7312614099fe545740036db6a632e1cdebc04e88 Mon Sep 17 00:00:00 2001 From: culler Date: Sun, 19 May 2024 21:07:21 +0000 Subject: [PATCH 08/96] All event-9.xy tests now pass, but 9.13 fails one time out of five. --- generic/tkPointer.c | 1 + generic/tkWindow.c | 23 ++++++++++++++++++----- macosx/tkMacOSXWindowEvent.c | 20 +++++++++----------- macosx/tkMacOSXWm.c | 10 +++++----- tests/event.test | 6 +++--- 5 files changed, 36 insertions(+), 24 deletions(-) diff --git a/generic/tkPointer.c b/generic/tkPointer.c index ae3bef932f..be05b06678 100644 --- a/generic/tkPointer.c +++ b/generic/tkPointer.c @@ -45,6 +45,7 @@ static int GenerateEnterLeave(TkWindow *winPtr, int x, int y, static void InitializeEvent(XEvent *eventPtr, TkWindow *winPtr, int type, int x, int y, int state, int detail); static void UpdateCursor(TkWindow *winPtr); + /* *---------------------------------------------------------------------- diff --git a/generic/tkWindow.c b/generic/tkWindow.c index 3703d369d7..90a20f2a4f 100644 --- a/generic/tkWindow.c +++ b/generic/tkWindow.c @@ -215,6 +215,7 @@ static int Initialize(Tcl_Interp *interp); static int NameWindow(Tcl_Interp *interp, TkWindow *winPtr, TkWindow *parentPtr, const char *name); static void UnlinkWindow(TkWindow *winPtr); + /* *---------------------------------------------------------------------- @@ -1322,15 +1323,15 @@ Tk_CreateWindowFromPath( *-------------------------------------------------------------- */ - static void SendEnterLeaveForDestroy( Tk_Window tkwin) { +#if defined(MAC_OSX_TK) || defined(_WIN32) int x, y; unsigned int state; Tk_Window pointerWin; TkWindow *containerPtr; - + XQueryPointer(NULL, None, NULL, NULL, &x, &y, NULL, NULL, &state); pointerWin = Tk_CoordsToWindow(x, y, tkwin); if (pointerWin == tkwin) { @@ -1338,7 +1339,15 @@ static void SendEnterLeaveForDestroy( containerPtr = TkGetContainer((TkWindow *)pointerWin); Tk_UpdatePointer((Tk_Window) containerPtr, x, y, state); } + else if (pointerWin) { + TkWmDeadWindow((TkWindow *) pointerWin); + } } + + if (pointerWin && (tkwin == Tk_Parent(pointerWin))) { + Tk_UpdatePointer(Tk_Parent(tkwin), x, y, state); + } +#endif } void @@ -1360,7 +1369,9 @@ Tk_DestroyWindow( return; } - SendEnterLeaveForDestroy(tkwin); + if ((winPtr->flags & TK_DONT_DESTROY_WINDOW) == 0) { + SendEnterLeaveForDestroy(tkwin); + } winPtr->flags |= TK_ALREADY_DEAD; @@ -1532,7 +1543,7 @@ Tk_DestroyWindow( * Cleanup the data structures associated with this window. */ - if (winPtr->flags & TK_WIN_MANAGED) { + if (winPtr->wmInfoPtr && (winPtr->flags & TK_WIN_MANAGED)) { TkWmDeadWindow(winPtr); } else if (winPtr->flags & TK_WM_COLORMAP_WINDOW) { TkWmRemoveFromColormapWindows(winPtr); @@ -1735,6 +1746,7 @@ Tk_DestroyWindow( static void SendEnterLeaveForMap( Tk_Window tkwin) { +#if defined(MAC_OSX_TK) || defined(_WIN32) int x, y; unsigned int state; Tk_Window pointerWin; @@ -1744,6 +1756,7 @@ static void SendEnterLeaveForMap( if (pointerWin == tkwin) { Tk_UpdatePointer(tkwin, x, y, state); } +#endif } void @@ -2634,7 +2647,7 @@ Tk_RestackWindow( TkWindow *otherPtr = (TkWindow *) other; /* - * Special case: if winPtr is a top-level window then just find the + * Special case: if winPtr is a toplevel window then just find the * top-level ancestor of otherPtr and restack winPtr above otherPtr * without changing any of Tk's childLists. */ diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 4e40bb6c18..41c9ede84e 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -74,8 +74,6 @@ extern NSString *NSWindowDidOrderOffScreenNotification; NSView *view = [w contentView]; NSPoint viewLocation = [view convertPoint:location fromView:nil]; if (NSPointInRect(viewLocation, NSInsetRect([view bounds], 2, 2))) { - fprintf(stderr, "windowActivation calling TkUpdatePointer\n"); - fflush(stderr); Tk_UpdatePointer((Tk_Window) winPtr, x, y, [NSApp tkButtonState]); } } @@ -252,15 +250,15 @@ extern NSString *NSWindowDidOrderOffScreenNotification; } } -- (void) windowMapped: (NSNotification *) notification -{ - NSWindow *w = [notification object]; - TkWindow *winPtr = TkMacOSXGetTkWindow(w); +//- (void) windowMapped: (NSNotification *) notification +//{ +// NSWindow *w = [notification object]; +// TkWindow *winPtr = TkMacOSXGetTkWindow(w); - if (winPtr) { - while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} - } -} +// if (winPtr) { +// while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} +// } +//} - (void) windowLiveResize: (NSNotification *) notification { @@ -306,7 +304,7 @@ extern NSString *NSWindowDidOrderOffScreenNotification; observe(NSWindowDidResizeNotification, windowBoundsChanged:); observe(NSWindowDidDeminiaturizeNotification, windowExpanded:); observe(NSWindowDidMiniaturizeNotification, windowCollapsed:); - observe(NSWindowWillOrderOnScreenNotification, windowMapped:); + //observe(NSWindowWillOrderOnScreenNotification, windowMapped:); observe(NSWindowDidOrderOnScreenNotification, windowBecameVisible:); observe(NSWindowWillStartLiveResizeNotification, windowLiveResize:); observe(NSWindowDidEndLiveResizeNotification, windowLiveResize:); diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index 952da39618..c771ec3962 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -897,12 +897,15 @@ TkWmUnmapWindow( * * This procedure is invoked when a top-level window is about to be * deleted. It cleans up the wm-related data structures for the window. + * If the dead window contains the pointer, TkUpdatePointer is called + * to tell Tk which window will be the new pointer window. * * Results: * None. * * Side effects: - * The WmInfo structure for winPtr gets freed up. + * The WmInfo structure for winPtr gets freed. Tk's cached pointer + * window may change. * *---------------------------------------------------------------------- */ @@ -913,9 +916,6 @@ TkWmDeadWindow( { TkWindow *winPtr2; WmInfo *wmPtr = winPtr->wmInfoPtr, *wmPtr2; - if (wmPtr == NULL) { - return; - } TKWindow *deadNSWindow = (TKWindow *)TkMacOSXGetNSWindowForDrawable( Tk_WindowId(winPtr)); if (deadNSWindow == NULL) { @@ -2037,7 +2037,7 @@ WmForgetCmd( macWin->toplevel->referenceCount++; macWin->flags &= ~TK_HOST_EXISTS; - TkWmDeadWindow(winPtr); + //TkWmDeadWindow(winPtr); RemapWindows(winPtr, (MacDrawable *)winPtr->parentPtr->window); /* diff --git a/tests/event.test b/tests/event.test index e398b8705d..29588508f4 100644 --- a/tests/event.test +++ b/tests/event.test @@ -887,7 +887,7 @@ proc waitForWindowEvent {w event {timeout 1000}} { bind $w $event $savedBinding unset _windowEvent($counter) if {$late} { - puts "waiting for $event event on $w timed out (> $timeout ms)" + puts stderr "wait for $event event on $w timed out (> $timeout ms)" } else { after cancel $afterID } @@ -902,6 +902,7 @@ proc create_and_pack_frames {{w {}}} { pack propagate $w.f1 0 frame $w.f1.f2 -bg yellow -width 100 -height 100 pack $w.f1.f2 $w.f1 -side bottom -anchor se + update idletasks } proc setup_win_mousepointer {w} { @@ -1149,7 +1150,7 @@ test event-9.19 {Successive destructions (pointer window + ancestors including i test event-9.20 {Successive destructions (pointer window + ancestors including its toplevel), destination is screen root} -setup { setup_win_mousepointer .one; # ensure the mouse pointer is where we want it to be (the .one toplevel is not itself used in this test) - wm withdraw .one + destroy .one toplevel .two pack propagate .two 0 wm geometry .two 300x300+100+100 @@ -1166,7 +1167,6 @@ test event-9.20 {Successive destructions (pointer window + ancestors including i } -cleanup { bind all {} bind all {} - destroy .one unset result } -result {|} From 5a43993cb3259453bb802dfbb09cd9d88afd3201 Mon Sep 17 00:00:00 2001 From: culler Date: Sun, 19 May 2024 21:38:16 +0000 Subject: [PATCH 09/96] Remove call to TkPointerDeadWindow in macOS and simplify 9.13 so it tests the same thing but fails less often. --- macosx/tkMacOSXSubwindows.c | 1 - tests/event.test | 2 -- 2 files changed, 3 deletions(-) diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index fb71a0c922..b3eab4aec9 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -62,7 +62,6 @@ XDestroyWindow( * deleting is being tracked by the grab code. */ - TkPointerDeadWindow(macWin->winPtr); TkMacOSXSelDeadWindow(macWin->winPtr); macWin->toplevel->referenceCount--; diff --git a/tests/event.test b/tests/event.test index 29588508f4..63885331ba 100644 --- a/tests/event.test +++ b/tests/event.test @@ -982,11 +982,9 @@ test event-9.12 {pointer window container != parent} -setup { test event-9.13 {pointer window is a toplevel, toplevel destination} -setup { setup_win_mousepointer .one - wm withdraw .one toplevel .two wm geometry .two 300x300+150+150 wm withdraw .two - wm deiconify .one wm deiconify .two waitForWindowEvent .two bind all {append result " %d %W|"} From 32eb23bc4d60fab1c8691f1afce5a10a05c70365 Mon Sep 17 00:00:00 2001 From: culler Date: Mon, 20 May 2024 15:41:09 +0000 Subject: [PATCH 10/96] Don't pass a NULL display and None window to XQueryPointer. --- generic/tkWindow.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/generic/tkWindow.c b/generic/tkWindow.c index 90a20f2a4f..e322bb437c 100644 --- a/generic/tkWindow.c +++ b/generic/tkWindow.c @@ -1332,7 +1332,8 @@ static void SendEnterLeaveForDestroy( Tk_Window pointerWin; TkWindow *containerPtr; - XQueryPointer(NULL, None, NULL, NULL, &x, &y, NULL, NULL, &state); + XQueryPointer(Tk_Display(tkwin), None, NULL, NULL, &x, &y, + NULL, NULL, &state); pointerWin = Tk_CoordsToWindow(x, y, tkwin); if (pointerWin == tkwin) { if (!Tk_IsTopLevel(tkwin)) { @@ -1751,7 +1752,8 @@ static void SendEnterLeaveForMap( unsigned int state; Tk_Window pointerWin; - XQueryPointer(NULL, None, NULL, NULL, &x, &y, NULL, NULL, &state); + XQueryPointer(Tk_Display(tkwin), None, NULL, NULL, &x, &y, + NULL, NULL, &state); pointerWin = Tk_CoordsToWindow(x, y, tkwin); if (pointerWin == tkwin) { Tk_UpdatePointer(tkwin, x, y, state); From 080aeeed22a914bdf8f1de650e6a4f2c34824104 Mon Sep 17 00:00:00 2001 From: culler Date: Mon, 20 May 2024 23:04:54 +0000 Subject: [PATCH 11/96] Restore the call to TkPointerDeadWindow - Windows needs it; probably macOS does too. --- macosx/tkMacOSXSubwindows.c | 1 + 1 file changed, 1 insertion(+) diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index b3eab4aec9..0e4820e35b 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -63,6 +63,7 @@ XDestroyWindow( */ TkMacOSXSelDeadWindow(macWin->winPtr); + TkPointerDeadWindow(macWin->winPtr); macWin->toplevel->referenceCount--; if (!Tk_IsTopLevel(macWin->winPtr)) { From e4fbaea258605ad01892f783375433c8bf613270 Mon Sep 17 00:00:00 2001 From: culler Date: Tue, 21 May 2024 22:42:45 +0000 Subject: [PATCH 12/96] Only 9.13, 9.14. and 9.19 fail on windows. Debug output included. --- generic/tkGrab.c | 21 +++++++++++++++++++-- generic/tkPointer.c | 11 +++++++++-- generic/tkWindow.c | 5 ++--- tests/event.test | 11 ++++++++++- win/tkWinWm.c | 39 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 79 insertions(+), 8 deletions(-) diff --git a/generic/tkGrab.c b/generic/tkGrab.c index c8e52539a1..b4228bd04a 100644 --- a/generic/tkGrab.c +++ b/generic/tkGrab.c @@ -998,6 +998,10 @@ TkInOutEvents( { TkWindow *winPtr; int upLevels, downLevels, i, j, focus; +#define NAME(w) (w ? Tk_PathName(w) : "NULL") + + fprintf(stderr, "TkInOutEvents: %s -> %s\n", + NAME(sourcePtr), NAME(destPtr)); /* * There are four possible cases to deal with: @@ -1029,7 +1033,7 @@ TkInOutEvents( /* * Generate enter/leave events and add them to the grab event queue. */ - + #define QUEUE(w, t, d) \ if (w->window != None) { \ eventPtr->type = t; \ @@ -1043,19 +1047,24 @@ TkInOutEvents( Tk_QueueWindowEvent(eventPtr, position); \ } +#define DBGEV(w, t, d) fprintf(stderr, " Queueing %s %s for %s\n", t, d, NAME(w)) + if (downLevels == 0) { /* * SourcePtr is an inferior of destPtr. */ if (leaveType != 0) { + DBGEV(sourcePtr, "", "NotifyAncestor"); QUEUE(sourcePtr, leaveType, NotifyAncestor); for (winPtr = sourcePtr->parentPtr, i = upLevels-1; i > 0; winPtr = winPtr->parentPtr, i--) { + DBGEV(winPtr, "", "NotifyVirtual"); QUEUE(winPtr, leaveType, NotifyVirtual); } } if ((enterType != 0) && (destPtr != NULL)) { + DBGEV(destPtr, "", "NotifyVirtual"); QUEUE(destPtr, enterType, NotifyInferior); } } else if (upLevels == 0) { @@ -1064,6 +1073,7 @@ TkInOutEvents( */ if ((leaveType != 0) && (sourcePtr != NULL)) { + DBGEV(sourcePtr, "", "NotifyInferior"); QUEUE(sourcePtr, leaveType, NotifyInferior); } if (enterType != 0) { @@ -1072,9 +1082,11 @@ TkInOutEvents( winPtr = winPtr->parentPtr, j++) { /* empty */ } + DBGEV(winPtr, "", "NotifyVirtual"); QUEUE(winPtr, enterType, NotifyVirtual); } if (destPtr != NULL) { + DBGEV(winPtr, "", "NotifyVirtual"); QUEUE(destPtr, enterType, NotifyAncestor); } } @@ -1084,10 +1096,12 @@ TkInOutEvents( */ if (leaveType != 0) { + DBGEV(sourcePtr, "", "NotifyNonlinear"); QUEUE(sourcePtr, leaveType, NotifyNonlinear); for (winPtr = sourcePtr->parentPtr, i = upLevels-1; i > 0; winPtr = winPtr->parentPtr, i--) { - QUEUE(winPtr, leaveType, NotifyNonlinearVirtual); + DBGEV(winPtr, "", "NotifyNonlinearVirtual"); + QUEUE(winPtr, leaveType, NotifyNonlinearVirtual); } } if (enterType != 0) { @@ -1095,13 +1109,16 @@ TkInOutEvents( for (winPtr = destPtr->parentPtr, j = 1; j < i; winPtr = winPtr->parentPtr, j++) { } + DBGEV(winPtr, "", "NotifyNonlinearVirtual"); QUEUE(winPtr, enterType, NotifyNonlinearVirtual); } if (destPtr != NULL) { + DBGEV(destPtr, "", "NotifyNonlinear"); QUEUE(destPtr, enterType, NotifyNonlinear); } } } + fflush(stderr); } /* diff --git a/generic/tkPointer.c b/generic/tkPointer.c index be05b06678..df26de1637 100644 --- a/generic/tkPointer.c +++ b/generic/tkPointer.c @@ -228,7 +228,10 @@ Tk_UpdatePointer( unsigned changes = (state ^ tsdPtr->lastState) & ALL_BUTTONS; int type, b; unsigned mask; - + fprintf(stderr, "TkUpdatePointer: %s -> %s\n", + tsdPtr->lastWinPtr ? Tk_PathName(tsdPtr->lastWinPtr) : "NULL", + tkwin ? Tk_PathName(tkwin) : "NULL"); + fflush(stderr); pos.x = x; pos.y = y; @@ -493,8 +496,11 @@ TkPointerDeadWindow( { ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); - + fprintf(stderr, "TkPointerDeadWindow: %s\n", winPtr ? Tk_PathName(winPtr) : "NULL"); if (winPtr == tsdPtr->lastWinPtr) { + //This fails on Windows because the windows version of TkWnDeadWindow + //does not (yet) call TkUpdatePointer when a dead toplevel contains the + //pointer. tsdPtr->lastWinPtr = TkGetContainer(winPtr); } if (winPtr == tsdPtr->grabWinPtr) { @@ -514,6 +520,7 @@ TkPointerDeadWindow( TkpSetCapture(NULL); } } + fflush(stderr); } /* diff --git a/generic/tkWindow.c b/generic/tkWindow.c index e322bb437c..e8c56dd2f1 100644 --- a/generic/tkWindow.c +++ b/generic/tkWindow.c @@ -1328,12 +1328,11 @@ static void SendEnterLeaveForDestroy( { #if defined(MAC_OSX_TK) || defined(_WIN32) int x, y; - unsigned int state; + unsigned int state = TkWinGetModifierState(); Tk_Window pointerWin; TkWindow *containerPtr; - XQueryPointer(Tk_Display(tkwin), None, NULL, NULL, &x, &y, - NULL, NULL, &state); + TkGetPointerCoords(NULL, &x, &y); pointerWin = Tk_CoordsToWindow(x, y, tkwin); if (pointerWin == tkwin) { if (!Tk_IsTopLevel(tkwin)) { diff --git a/tests/event.test b/tests/event.test index 63885331ba..42f28f5608 100644 --- a/tests/event.test +++ b/tests/event.test @@ -873,6 +873,7 @@ proc waitForWindowEvent {w event {timeout 1000}} { # always a gamble how much waiting time is enough on an end user's system. # It also leads to long fixed waiting times in order to be on the safe side. + puts stderr "Waiting for $event on $w" variable _windowEvent # Use counter as a unique ID to prevent subsequent waits @@ -890,6 +891,7 @@ proc waitForWindowEvent {w event {timeout 1000}} { puts stderr "wait for $event event on $w timed out (> $timeout ms)" } else { after cancel $afterID + puts stderr "Event received" } } proc waitForWindowEvent.signal {counter} { @@ -981,6 +983,7 @@ test event-9.12 {pointer window container != parent} -setup { } -result {| NotifyNonlinearVirtual .one.f1| NotifyNonlinear .one.f1.f2|} test event-9.13 {pointer window is a toplevel, toplevel destination} -setup { + puts stderr "9.13 setup started" setup_win_mousepointer .one toplevel .two wm geometry .two 300x300+150+150 @@ -989,11 +992,14 @@ test event-9.13 {pointer window is a toplevel, toplevel destination} -setup { waitForWindowEvent .two bind all {append result " %d %W|"} bind all {append result " %d %W|"} - set result "|" + set result | + puts stderr "9.13 setup done" } -body { + puts stderr "9.13 body started - destroying .two" destroy .two waitForWindowEvent .one set result + puts stderr "9.13 body finished" } -cleanup { bind all {} bind all {} @@ -1002,6 +1008,7 @@ test event-9.13 {pointer window is a toplevel, toplevel destination} -setup { } -result {| NotifyNonlinear .one|} test event-9.14 {pointer window is a toplevel, tk internal destination} -setup { + puts stderr "9.14 setup" setup_win_mousepointer .one wm withdraw .one create_and_pack_frames .one @@ -1015,9 +1022,11 @@ test event-9.14 {pointer window is a toplevel, tk internal destination} -setup { bind all {append result " %d %W|"} set result "|" } -body { + puts stderr "9.14 body" destroy .two waitForWindowEvent .one.f1.f2 set result + puts stderr "9.14 body finished" } -cleanup { bind all {} bind all {} diff --git a/win/tkWinWm.c b/win/tkWinWm.c index 90503a695b..146b82bab2 100644 --- a/win/tkWinWm.c +++ b/win/tkWinWm.c @@ -2577,6 +2577,36 @@ TkpWmGetState( *-------------------------------------------------------------- */ +static void CheckForPointer(TkWindow *winPtr) +{ + Display *display = Tk_Display(winPtr); + POINT mouse; + unsigned int state = TkWinGetModifierState(); + TkWindow **windows = TkWmStackorderToplevel(winPtr->mainPtr->winPtr); + TkWindow **w; + TkGetPointerCoords(NULL, &mouse.x, &mouse.y); + fprintf(stderr, "CheckForPointer: %s with mouse @(%d, %d)\n", + Tk_PathName(winPtr), mouse.x, mouse.y); + if (windows != NULL) { + for (w = windows; *w ; w++) { + RECT windowRect; + HWND hwnd = Tk_GetHWND(Tk_WindowId((Tk_Window) *w)); + fprintf(stderr, " Checking %s\n", Tk_PathName(*w)); + if (GetWindowRect(hwnd, &windowRect) == 0) { + continue; + } + if (winPtr != *w && PtInRect(&windowRect, mouse)) { + fprintf(stderr, "Pointer is in %s. Calling Tk_UpdatePointer\n", + Tk_PathName(*w)); + Tk_UpdatePointer((Tk_Window) *w, mouse.x, mouse.y, state); + break; + } + } + ckfree(windows); + } + fflush(stderr); +} + void TkWmDeadWindow( TkWindow *winPtr) /* Top-level window that's being deleted. */ @@ -2715,6 +2745,15 @@ TkWmDeadWindow( DecrIconRefCount(wmPtr->iconPtr); } + /* + * Check if the dead window is a toplevel containing the pointer. If so, + * find the window which will inherit the pointer and call + * TkUpdatePointer. + */ + + CheckForPointer(winPtr); + //TkPointerDeadWindow(winPtr); + ckfree(wmPtr); winPtr->wmInfoPtr = NULL; } From ec44aa0c277d6c671ec0024785197e2f2619993a Mon Sep 17 00:00:00 2001 From: culler Date: Wed, 22 May 2024 02:44:54 +0000 Subject: [PATCH 13/96] All event tests now pass on Windows --- tests/event.test | 20 ++++++-------------- win/tkWinWm.c | 12 ++++-------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/tests/event.test b/tests/event.test index 42f28f5608..f24c3c63aa 100644 --- a/tests/event.test +++ b/tests/event.test @@ -873,7 +873,6 @@ proc waitForWindowEvent {w event {timeout 1000}} { # always a gamble how much waiting time is enough on an end user's system. # It also leads to long fixed waiting times in order to be on the safe side. - puts stderr "Waiting for $event on $w" variable _windowEvent # Use counter as a unique ID to prevent subsequent waits @@ -891,7 +890,6 @@ proc waitForWindowEvent {w event {timeout 1000}} { puts stderr "wait for $event event on $w timed out (> $timeout ms)" } else { after cancel $afterID - puts stderr "Event received" } } proc waitForWindowEvent.signal {counter} { @@ -983,32 +981,28 @@ test event-9.12 {pointer window container != parent} -setup { } -result {| NotifyNonlinearVirtual .one.f1| NotifyNonlinear .one.f1.f2|} test event-9.13 {pointer window is a toplevel, toplevel destination} -setup { - puts stderr "9.13 setup started" setup_win_mousepointer .one toplevel .two wm geometry .two 300x300+150+150 wm withdraw .two wm deiconify .two waitForWindowEvent .two - bind all {append result " %d %W|"} - bind all {append result " %d %W|"} + bind all {append result " %d %W|" ; puts stderr "=leave=" } + bind all {append result " %d %W|" ; puts stderr "-enter=" } set result | - puts stderr "9.13 setup done" } -body { - puts stderr "9.13 body started - destroying .two" destroy .two waitForWindowEvent .one + # destroying .one here instead of in cleanup makes the test pass + destroy .one set result - puts stderr "9.13 body finished" } -cleanup { bind all {} bind all {} - destroy .one unset result } -result {| NotifyNonlinear .one|} test event-9.14 {pointer window is a toplevel, tk internal destination} -setup { - puts stderr "9.14 setup" setup_win_mousepointer .one wm withdraw .one create_and_pack_frames .one @@ -1022,15 +1016,13 @@ test event-9.14 {pointer window is a toplevel, tk internal destination} -setup { bind all {append result " %d %W|"} set result "|" } -body { - puts stderr "9.14 body" destroy .two waitForWindowEvent .one.f1.f2 - set result - puts stderr "9.14 body finished" -} -cleanup { bind all {} bind all {} destroy .one + set result +} -cleanup { unset result } -result {| NotifyNonlinearVirtual .one| NotifyNonlinearVirtual .one.f1| NotifyNonlinear .one.f1.f2|} diff --git a/win/tkWinWm.c b/win/tkWinWm.c index 146b82bab2..a550302d7f 100644 --- a/win/tkWinWm.c +++ b/win/tkWinWm.c @@ -2585,20 +2585,18 @@ static void CheckForPointer(TkWindow *winPtr) TkWindow **windows = TkWmStackorderToplevel(winPtr->mainPtr->winPtr); TkWindow **w; TkGetPointerCoords(NULL, &mouse.x, &mouse.y); - fprintf(stderr, "CheckForPointer: %s with mouse @(%d, %d)\n", - Tk_PathName(winPtr), mouse.x, mouse.y); if (windows != NULL) { for (w = windows; *w ; w++) { RECT windowRect; HWND hwnd = Tk_GetHWND(Tk_WindowId((Tk_Window) *w)); - fprintf(stderr, " Checking %s\n", Tk_PathName(*w)); if (GetWindowRect(hwnd, &windowRect) == 0) { continue; } if (winPtr != *w && PtInRect(&windowRect, mouse)) { - fprintf(stderr, "Pointer is in %s. Calling Tk_UpdatePointer\n", - Tk_PathName(*w)); - Tk_UpdatePointer((Tk_Window) *w, mouse.x, mouse.y, state); + Tk_Window target = Tk_CoordsToWindow(mouse.x, mouse.y, + (Tk_Window) *w); + Tk_UpdatePointer((Tk_Window) target, + mouse.x, mouse.y, state); break; } } @@ -6710,8 +6708,6 @@ TkWmStackorderToplevelEnumProc( TkWmStackorderToplevelPair *pair = (TkWmStackorderToplevelPair *) lParam; - /*fprintf(stderr, "Looking up HWND %d\n", hwnd);*/ - hPtr = Tcl_FindHashEntry(pair->table, (char *) hwnd); if (hPtr != NULL) { childWinPtr = (TkWindow *)Tcl_GetHashValue(hPtr); From 9a54c3a18c796aa1ec12622752092c15a85b2e54 Mon Sep 17 00:00:00 2001 From: culler Date: Wed, 22 May 2024 03:16:50 +0000 Subject: [PATCH 14/96] Remove debugging code. Issues remain with macOS for other tests in event.test. --- generic/tkGrab.c | 16 ---------------- generic/tkPointer.c | 9 +-------- generic/tkWindow.c | 5 +++-- tests/event.test | 4 ++-- 4 files changed, 6 insertions(+), 28 deletions(-) diff --git a/generic/tkGrab.c b/generic/tkGrab.c index b4228bd04a..2c7c6ab191 100644 --- a/generic/tkGrab.c +++ b/generic/tkGrab.c @@ -998,10 +998,6 @@ TkInOutEvents( { TkWindow *winPtr; int upLevels, downLevels, i, j, focus; -#define NAME(w) (w ? Tk_PathName(w) : "NULL") - - fprintf(stderr, "TkInOutEvents: %s -> %s\n", - NAME(sourcePtr), NAME(destPtr)); /* * There are four possible cases to deal with: @@ -1047,24 +1043,19 @@ TkInOutEvents( Tk_QueueWindowEvent(eventPtr, position); \ } -#define DBGEV(w, t, d) fprintf(stderr, " Queueing %s %s for %s\n", t, d, NAME(w)) - if (downLevels == 0) { /* * SourcePtr is an inferior of destPtr. */ if (leaveType != 0) { - DBGEV(sourcePtr, "", "NotifyAncestor"); QUEUE(sourcePtr, leaveType, NotifyAncestor); for (winPtr = sourcePtr->parentPtr, i = upLevels-1; i > 0; winPtr = winPtr->parentPtr, i--) { - DBGEV(winPtr, "", "NotifyVirtual"); QUEUE(winPtr, leaveType, NotifyVirtual); } } if ((enterType != 0) && (destPtr != NULL)) { - DBGEV(destPtr, "", "NotifyVirtual"); QUEUE(destPtr, enterType, NotifyInferior); } } else if (upLevels == 0) { @@ -1073,7 +1064,6 @@ TkInOutEvents( */ if ((leaveType != 0) && (sourcePtr != NULL)) { - DBGEV(sourcePtr, "", "NotifyInferior"); QUEUE(sourcePtr, leaveType, NotifyInferior); } if (enterType != 0) { @@ -1082,11 +1072,9 @@ TkInOutEvents( winPtr = winPtr->parentPtr, j++) { /* empty */ } - DBGEV(winPtr, "", "NotifyVirtual"); QUEUE(winPtr, enterType, NotifyVirtual); } if (destPtr != NULL) { - DBGEV(winPtr, "", "NotifyVirtual"); QUEUE(destPtr, enterType, NotifyAncestor); } } @@ -1096,11 +1084,9 @@ TkInOutEvents( */ if (leaveType != 0) { - DBGEV(sourcePtr, "", "NotifyNonlinear"); QUEUE(sourcePtr, leaveType, NotifyNonlinear); for (winPtr = sourcePtr->parentPtr, i = upLevels-1; i > 0; winPtr = winPtr->parentPtr, i--) { - DBGEV(winPtr, "", "NotifyNonlinearVirtual"); QUEUE(winPtr, leaveType, NotifyNonlinearVirtual); } } @@ -1109,11 +1095,9 @@ TkInOutEvents( for (winPtr = destPtr->parentPtr, j = 1; j < i; winPtr = winPtr->parentPtr, j++) { } - DBGEV(winPtr, "", "NotifyNonlinearVirtual"); QUEUE(winPtr, enterType, NotifyNonlinearVirtual); } if (destPtr != NULL) { - DBGEV(destPtr, "", "NotifyNonlinear"); QUEUE(destPtr, enterType, NotifyNonlinear); } } diff --git a/generic/tkPointer.c b/generic/tkPointer.c index df26de1637..9f88d2f3fc 100644 --- a/generic/tkPointer.c +++ b/generic/tkPointer.c @@ -228,10 +228,7 @@ Tk_UpdatePointer( unsigned changes = (state ^ tsdPtr->lastState) & ALL_BUTTONS; int type, b; unsigned mask; - fprintf(stderr, "TkUpdatePointer: %s -> %s\n", - tsdPtr->lastWinPtr ? Tk_PathName(tsdPtr->lastWinPtr) : "NULL", - tkwin ? Tk_PathName(tkwin) : "NULL"); - fflush(stderr); + pos.x = x; pos.y = y; @@ -496,11 +493,7 @@ TkPointerDeadWindow( { ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); - fprintf(stderr, "TkPointerDeadWindow: %s\n", winPtr ? Tk_PathName(winPtr) : "NULL"); if (winPtr == tsdPtr->lastWinPtr) { - //This fails on Windows because the windows version of TkWnDeadWindow - //does not (yet) call TkUpdatePointer when a dead toplevel contains the - //pointer. tsdPtr->lastWinPtr = TkGetContainer(winPtr); } if (winPtr == tsdPtr->grabWinPtr) { diff --git a/generic/tkWindow.c b/generic/tkWindow.c index e8c56dd2f1..e322bb437c 100644 --- a/generic/tkWindow.c +++ b/generic/tkWindow.c @@ -1328,11 +1328,12 @@ static void SendEnterLeaveForDestroy( { #if defined(MAC_OSX_TK) || defined(_WIN32) int x, y; - unsigned int state = TkWinGetModifierState(); + unsigned int state; Tk_Window pointerWin; TkWindow *containerPtr; - TkGetPointerCoords(NULL, &x, &y); + XQueryPointer(Tk_Display(tkwin), None, NULL, NULL, &x, &y, + NULL, NULL, &state); pointerWin = Tk_CoordsToWindow(x, y, tkwin); if (pointerWin == tkwin) { if (!Tk_IsTopLevel(tkwin)) { diff --git a/tests/event.test b/tests/event.test index f24c3c63aa..a099b5aceb 100644 --- a/tests/event.test +++ b/tests/event.test @@ -987,8 +987,8 @@ test event-9.13 {pointer window is a toplevel, toplevel destination} -setup { wm withdraw .two wm deiconify .two waitForWindowEvent .two - bind all {append result " %d %W|" ; puts stderr "=leave=" } - bind all {append result " %d %W|" ; puts stderr "-enter=" } + bind all {append result " %d %W|"} + bind all {append result " %d %W|"} set result | } -body { destroy .two From 26ddf13d3772b5ac21eab4f997628305d5622543 Mon Sep 17 00:00:00 2001 From: culler Date: Wed, 22 May 2024 04:22:40 +0000 Subject: [PATCH 15/96] Make destruction of windows on macOS work again. But more issues remain. --- macosx/tkMacOSXWm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index c771ec3962..810b700e0c 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -1034,7 +1034,7 @@ TkWmDeadWindow( * the parent. Then close and release the NSWindow. */ - if (0 && deadNSWindow && winPtr && !Tk_IsEmbedded(winPtr)) { + if (deadNSWindow && !Tk_IsEmbedded(winPtr)) { NSWindow *parent = [deadNSWindow parentWindow]; [deadNSWindow setTkWindow:None]; if (winPtr->window) { @@ -2037,7 +2037,6 @@ WmForgetCmd( macWin->toplevel->referenceCount++; macWin->flags &= ~TK_HOST_EXISTS; - //TkWmDeadWindow(winPtr); RemapWindows(winPtr, (MacDrawable *)winPtr->parentPtr->window); /* From 000fab56ad22db72cc7b854e859cc8597bac1575 Mon Sep 17 00:00:00 2001 From: culler Date: Wed, 22 May 2024 16:58:35 +0000 Subject: [PATCH 16/96] Add some update hacks to defer macOS crashes until xmfbox.test. --- tests/unixWm.test | 8 ++++++++ tests/wm.test | 2 ++ 2 files changed, 10 insertions(+) diff --git a/tests/unixWm.test b/tests/unixWm.test index 4bf466cb5b..472db21075 100644 --- a/tests/unixWm.test +++ b/tests/unixWm.test @@ -641,6 +641,8 @@ test unixWm-16.1 {Tk_WmCmd procedure, "deiconify" option} unix { test unixWm-16.2 {Tk_WmCmd procedure, "deiconify" option} unix { destroy .icon toplevel .icon -width 50 -height 50 -bg red + # calling update here prevents a crash in 16.3 on macOS + update wm iconwindow .t .icon set result [list [catch {wm deiconify .icon} msg] $msg] destroy .icon @@ -1352,8 +1354,12 @@ test unixWm-38.2 {Tk_WmCmd procedure, "withdraw" option} unix { test unixWm-38.3 {Tk_WmCmd procedure, "withdraw" option} unix { set result {} wm withdraw .t + #added to avoid a crash on macOS + update idletasks lappend result [wm state .t] [winfo ismapped .t] wm deiconify .t + #added to avoid a crash on macOS + update idletasks lappend result [wm state .t] [winfo ismapped .t] } {withdrawn 0 normal 1} @@ -1373,7 +1379,9 @@ test unixWm-40.1 {Tk_SetGrid procedure, set grid dimensions before turning on gr wm geometry .t } {30x10+0+0} test unixWm-40.2 {Tk_SetGrid procedure, turning on grid when dimensions already set} unix { + update destroy .t + update toplevel .t wm geometry .t 200x100+100+$Y0 listbox .t.l -height 20 -width 20 diff --git a/tests/wm.test b/tests/wm.test index 6f6dc80273..926615e3c0 100644 --- a/tests/wm.test +++ b/tests/wm.test @@ -947,6 +947,8 @@ test wm-iconwindow-2.1 {setting and reading values} -setup { destroy .icon set result {} } -body { + #added to avoid a crash on macOS + deiconify .t; update lappend result [wm iconwindow .t] toplevel .icon -width 50 -height 50 -bg green wm iconwindow .t .icon From 338f1ec85f66ca2e6643e65ae85a94cdc0fb7679 Mon Sep 17 00:00:00 2001 From: culler Date: Wed, 22 May 2024 18:45:38 +0000 Subject: [PATCH 17/96] xmfbox now passes - the crash occurs at the end of the tk tests, before the ttk tests --- macosx/tkMacOSXWm.c | 15 ++++----------- tests/xmfbox.test | 7 +++++++ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index 810b700e0c..6c7468afad 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -623,24 +623,17 @@ FrontWindowAtPoint( for (NSWindow *w in windows) { winPtr = TkMacOSXGetTkWindow(w); if (winPtr) { - WmInfo *wmPtr = winPtr->wmInfoPtr; NSRect windowFrame = [w frame]; - NSRect contentFrame = [w frame]; - - contentFrame.size.height = [[w contentView] frame].size.height; + NSRect contentFrame = windowFrame; /* * For consistency with other platforms, points in the * title bar are not considered to be contained in the * window. */ - if ((wmPtr->hints.initial_state == NormalState || - wmPtr->hints.initial_state == ZoomState)) { - if (NSMouseInRect(p, contentFrame, NO)) { - return winPtr; - } else if (NSMouseInRect(p, windowFrame, NO)) { - return NULL; - } + contentFrame.size.height = [[w contentView] frame].size.height; + if (NSMouseInRect(p, contentFrame, NO)) { + return winPtr; } } } diff --git a/tests/xmfbox.test b/tests/xmfbox.test index f50329ce86..cef9910bd4 100644 --- a/tests/xmfbox.test +++ b/tests/xmfbox.test @@ -54,6 +54,7 @@ proc cleanup {} { } catch {unset foo} destroy .foo + update } # ---------------------------------------------------------------------- @@ -76,6 +77,7 @@ test xmfbox-1.2 {tk::MotifFDialog_Create, -parent switch} -constraints { } -body { toplevel .bar wm geometry .bar +0+0 + update set x [tk::MotifFDialog_Create foo open {-parent .bar}] } -cleanup { destroy $x @@ -89,6 +91,7 @@ test xmfbox-2.1 {tk::MotifFDialog_InterpFilter, ~ in dir names} -constraints { cleanup file mkdir ./~nosuchuser1 set x [tk::MotifFDialog_Create foo open {}] + update $::tk::dialog::file::foo(fEnt) delete 0 end $::tk::dialog::file::foo(fEnt) insert 0 [pwd]/~nosuchuser1 set kk [tk::MotifFDialog_InterpFilter $x] @@ -100,6 +103,7 @@ test xmfbox-2.2 {tk::MotifFDialog_InterpFilter, ~ in file names} -constraints { cleanup close [open ./~nosuchuser1 {CREAT TRUNC WRONLY}] set x [tk::MotifFDialog_Create foo open {}] + update $::tk::dialog::file::foo(fEnt) delete 0 end $::tk::dialog::file::foo(fEnt) insert 0 [pwd]/~nosuchuser1 set kk [tk::MotifFDialog_InterpFilter $x] @@ -111,6 +115,7 @@ test xmfbox-2.3 {tk::MotifFDialog_Update, ~ in file names} -constraints { cleanup close [open ./~nosuchuser1 {CREAT TRUNC WRONLY}] set x [tk::MotifFDialog_Create foo open {}] + update $::tk::dialog::file::foo(fEnt) delete 0 end $::tk::dialog::file::foo(fEnt) insert 0 [pwd]/~nosuchuser1 tk::MotifFDialog_InterpFilter $x @@ -124,6 +129,7 @@ test xmfbox-2.4 {tk::MotifFDialog_LoadFile, ~ in file names} -constraints { cleanup close [open ./~nosuchuser1 {CREAT TRUNC WRONLY}] set x [tk::MotifFDialog_Create foo open {}] + update set i [lsearch [$::tk::dialog::file::foo(fList) get 0 end] ~nosuchuser1] expr {$i >= 0} } -result 1 @@ -134,6 +140,7 @@ test xmfbox-2.5 {tk::MotifFDialog_BrowseFList, ~ in file names} -constraints { cleanup close [open ./~nosuchuser1 {CREAT TRUNC WRONLY}] set x [tk::MotifFDialog_Create foo open {}] + update set i [lsearch [$::tk::dialog::file::foo(fList) get 0 end] ~nosuchuser1] $::tk::dialog::file::foo(fList) selection clear 0 end $::tk::dialog::file::foo(fList) selection set $i From e85680bb451049993bad99dc1e47ec9b58da680a Mon Sep 17 00:00:00 2001 From: culler Date: Wed, 22 May 2024 19:00:35 +0000 Subject: [PATCH 18/96] Readjust event-9.15 and event-9.19 --- tests/event.test | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/tests/event.test b/tests/event.test index a099b5aceb..f35e5a2602 100644 --- a/tests/event.test +++ b/tests/event.test @@ -916,12 +916,6 @@ proc setup_win_mousepointer {w} { update; # service remaining screen drawing events (e.g. ) set pointerWin [winfo containing [winfo pointerx $w] [winfo pointery $w]] event generate $w -warp 1 -x 250 -y 250 -# if {[tk windowingsystem] eq "aqua"} { -# # Generate a NSMouseMoved NSevent with no mouse pointer position change. -# # This is to let event-9.1? tests pass on macOS aqua and is only a workaround -# # since macOS aqua should send the adequate NSevent by itself. -# movemouse [expr {[winfo rootx $w] + 250}] [expr {[winfo rooty $w] + 250}] -# } if {($pointerWin ne $w) && ([tk windowingsystem] ne "aqua")} { waitForWindowEvent $w } else { @@ -1038,8 +1032,8 @@ test event-9.15 {pointer window is a toplevel, destination is screen root} -setu bind all {append result " %d %W|"} set result "|" } -body { - destroy .two _pause 200; # ensure servicing of all scheduled events (only events expected) + destroy .two set result } -cleanup { bind all {} @@ -1138,12 +1132,12 @@ test event-9.19 {Successive destructions (pointer window + ancestors including i } -body { destroy .three waitForWindowEvent .two.f1.f2 - set result -} -cleanup { bind all {} bind all {} destroy .two destroy .one + set result +} -cleanup { unset result } -result {| NotifyNonlinearVirtual .two| NotifyNonlinearVirtual .two.f1| NotifyNonlinear .two.f1.f2|} From 6704a6d04762b9963ce78a778ff706b3e7a90d61 Mon Sep 17 00:00:00 2001 From: culler Date: Thu, 23 May 2024 15:08:00 +0000 Subject: [PATCH 19/96] Prevent crash at exit on macOS --- generic/tkWindow.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++ tests/event.test | 6 ++--- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/generic/tkWindow.c b/generic/tkWindow.c index e322bb437c..cbbdafed39 100644 --- a/generic/tkWindow.c +++ b/generic/tkWindow.c @@ -215,6 +215,19 @@ static int Initialize(Tcl_Interp *interp); static int NameWindow(Tcl_Interp *interp, TkWindow *winPtr, TkWindow *parentPtr, const char *name); static void UnlinkWindow(TkWindow *winPtr); +#if 0 +static Bool HasValidDisplay(Tk_Window tkwin); +#endif + +/* + * This static variable only makes sense for macOS and Windows, which never + * have more than one display. It is set by TkCloseDisplay, and when set + * prevents sending Enter and Leave events when all of the windows in the + * display are being destroyed. Tk does not send those events on X11; that + * job is handled by the X server. + */ + +static int displayBeingClosed = 0; /* @@ -240,6 +253,7 @@ static void TkCloseDisplay( TkDisplay *dispPtr) { + displayBeingClosed = 1; TkClipCleanup(dispPtr); TkpCancelWarp(dispPtr); @@ -509,7 +523,40 @@ GetScreen( *screenPtr = screenId; return dispPtr; } +#if 0 +/* + *-------------------------------------------------------------- + * + * HasValidDisplay -- + * + * Given a Tk window, returns True if the window's display is still on + * the display list. + * + * Results: + * + * True if the display is on the display list, False otherwise/ + * + * Side effects: + * None + * + *-------------------------------------------------------------- + */ + +static Bool HasValidDisplay( + Tk_Window tkwin) +{ + TkWindow *winPtr = (TkWindow *) tkwin; + TkDisplay *dispPtr = (TkDisplay *) winPtr->display, *dispPtr2; + + for (dispPtr2 = TkGetDisplayList(); dispPtr2; dispPtr2 = dispPtr2->nextPtr) { + if (dispPtr2 == dispPtr) { + return True; + } + } + return False; +} +#endif /* *---------------------------------------------------------------------- * @@ -1332,6 +1379,13 @@ static void SendEnterLeaveForDestroy( Tk_Window pointerWin; TkWindow *containerPtr; + if (displayBeingClosed) { + return; + } + // if (!HasValidDisplay(tkwin)) { + // return; + // } + XQueryPointer(Tk_Display(tkwin), None, NULL, NULL, &x, &y, NULL, NULL, &state); pointerWin = Tk_CoordsToWindow(x, y, tkwin); @@ -1752,6 +1806,13 @@ static void SendEnterLeaveForMap( unsigned int state; Tk_Window pointerWin; + if (displayBeingClosed) { + return; + } + // if (!HasValidDisplay(tkwin)) { + // return; + // } + XQueryPointer(Tk_Display(tkwin), None, NULL, NULL, &x, &y, NULL, NULL, &state); pointerWin = Tk_CoordsToWindow(x, y, tkwin); diff --git a/tests/event.test b/tests/event.test index f35e5a2602..d2f65e56a4 100644 --- a/tests/event.test +++ b/tests/event.test @@ -1104,16 +1104,17 @@ test event-9.18 {Successive destructions (pointer window + ancestors including i } -body { destroy .two waitForWindowEvent .one - set result -} -cleanup { bind all {} bind all {} destroy .one + set result +} -cleanup { unset result } -result {| NotifyNonlinear .one|} test event-9.19 {Successive destructions (pointer window + ancestors including its toplevel), destination is internal window, bypass root win} -setup { setup_win_mousepointer .one; # ensure the mouse pointer is where we want it to be (the .one toplevel is not itself used in this test) + destroy .one toplevel .two pack propagate .two 0 wm geometry .two 300x300+100+100 @@ -1135,7 +1136,6 @@ test event-9.19 {Successive destructions (pointer window + ancestors including i bind all {} bind all {} destroy .two - destroy .one set result } -cleanup { unset result From 00623999b20177be1ce0a6ca8c83dd0d9269e9d2 Mon Sep 17 00:00:00 2001 From: culler Date: Thu, 23 May 2024 15:22:01 +0000 Subject: [PATCH 20/96] Remove general code for checking if a display is valid because it affects timing, which breaks all of the event-9 tests. --- generic/tkWindow.c | 44 -------------------------------------------- 1 file changed, 44 deletions(-) diff --git a/generic/tkWindow.c b/generic/tkWindow.c index cbbdafed39..644fea3df4 100644 --- a/generic/tkWindow.c +++ b/generic/tkWindow.c @@ -215,9 +215,6 @@ static int Initialize(Tcl_Interp *interp); static int NameWindow(Tcl_Interp *interp, TkWindow *winPtr, TkWindow *parentPtr, const char *name); static void UnlinkWindow(TkWindow *winPtr); -#if 0 -static Bool HasValidDisplay(Tk_Window tkwin); -#endif /* * This static variable only makes sense for macOS and Windows, which never @@ -523,40 +520,7 @@ GetScreen( *screenPtr = screenId; return dispPtr; } -#if 0 -/* - *-------------------------------------------------------------- - * - * HasValidDisplay -- - * - * Given a Tk window, returns True if the window's display is still on - * the display list. - * - * Results: - * - * True if the display is on the display list, False otherwise/ - * - * Side effects: - * None - * - *-------------------------------------------------------------- - */ - -static Bool HasValidDisplay( - Tk_Window tkwin) -{ - TkWindow *winPtr = (TkWindow *) tkwin; - TkDisplay *dispPtr = (TkDisplay *) winPtr->display, *dispPtr2; - - for (dispPtr2 = TkGetDisplayList(); dispPtr2; dispPtr2 = dispPtr2->nextPtr) { - if (dispPtr2 == dispPtr) { - return True; - } - } - return False; -} -#endif /* *---------------------------------------------------------------------- * @@ -1382,10 +1346,6 @@ static void SendEnterLeaveForDestroy( if (displayBeingClosed) { return; } - // if (!HasValidDisplay(tkwin)) { - // return; - // } - XQueryPointer(Tk_Display(tkwin), None, NULL, NULL, &x, &y, NULL, NULL, &state); pointerWin = Tk_CoordsToWindow(x, y, tkwin); @@ -1809,10 +1769,6 @@ static void SendEnterLeaveForMap( if (displayBeingClosed) { return; } - // if (!HasValidDisplay(tkwin)) { - // return; - // } - XQueryPointer(Tk_Display(tkwin), None, NULL, NULL, &x, &y, NULL, NULL, &state); pointerWin = Tk_CoordsToWindow(x, y, tkwin); From c03a103ca11b8710b13b35b4bf5de0b46379d89c Mon Sep 17 00:00:00 2001 From: culler Date: Thu, 23 May 2024 16:55:00 +0000 Subject: [PATCH 21/96] Clean up. --- generic/tkGrab.c | 1 - generic/tkPointer.c | 1 - macosx/tkMacOSXWindowEvent.c | 22 ++++++++++++---------- win/tkWinWm.c | 2 -- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/generic/tkGrab.c b/generic/tkGrab.c index 2c7c6ab191..d4ecdb0c5e 100644 --- a/generic/tkGrab.c +++ b/generic/tkGrab.c @@ -1102,7 +1102,6 @@ TkInOutEvents( } } } - fflush(stderr); } /* diff --git a/generic/tkPointer.c b/generic/tkPointer.c index 9f88d2f3fc..c2ba29f7c1 100644 --- a/generic/tkPointer.c +++ b/generic/tkPointer.c @@ -513,7 +513,6 @@ TkPointerDeadWindow( TkpSetCapture(NULL); } } - fflush(stderr); } /* diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 41c9ede84e..fc7acfdd02 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -250,15 +250,15 @@ extern NSString *NSWindowDidOrderOffScreenNotification; } } -//- (void) windowMapped: (NSNotification *) notification -//{ -// NSWindow *w = [notification object]; -// TkWindow *winPtr = TkMacOSXGetTkWindow(w); +- (void) windowMapped: (NSNotification *) notification +{ + NSWindow *w = [notification object]; + TkWindow *winPtr = TkMacOSXGetTkWindow(w); -// if (winPtr) { -// while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} -// } -//} + if (winPtr) { + while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} + } +} - (void) windowLiveResize: (NSNotification *) notification { @@ -284,9 +284,11 @@ extern NSString *NSWindowDidOrderOffScreenNotification; NSWindow *w = [notification object]; TkWindow *winPtr = TkMacOSXGetTkWindow(w); +#if 0 if (winPtr) { - //Tk_UnmapWindow((Tk_Window)winPtr); + Tk_UnmapWindow((Tk_Window)winPtr); } +#endif } #endif /* TK_MAC_DEBUG_NOTIFICATIONS */ @@ -304,7 +306,7 @@ extern NSString *NSWindowDidOrderOffScreenNotification; observe(NSWindowDidResizeNotification, windowBoundsChanged:); observe(NSWindowDidDeminiaturizeNotification, windowExpanded:); observe(NSWindowDidMiniaturizeNotification, windowCollapsed:); - //observe(NSWindowWillOrderOnScreenNotification, windowMapped:); + observe(NSWindowWillOrderOnScreenNotification, windowMapped:); observe(NSWindowDidOrderOnScreenNotification, windowBecameVisible:); observe(NSWindowWillStartLiveResizeNotification, windowLiveResize:); observe(NSWindowDidEndLiveResizeNotification, windowLiveResize:); diff --git a/win/tkWinWm.c b/win/tkWinWm.c index a550302d7f..df9b1ce686 100644 --- a/win/tkWinWm.c +++ b/win/tkWinWm.c @@ -2750,8 +2750,6 @@ TkWmDeadWindow( */ CheckForPointer(winPtr); - //TkPointerDeadWindow(winPtr); - ckfree(wmPtr); winPtr->wmInfoPtr = NULL; } From 0a51685f4bf2ef83d5ef6e3a32be16f7f34d86aa Mon Sep 17 00:00:00 2001 From: culler Date: Thu, 23 May 2024 17:43:55 +0000 Subject: [PATCH 22/96] White space. --- generic/tkGrab.c | 5 ++--- generic/tkPointer.c | 3 ++- generic/tkWindow.c | 1 - macosx/tkMacOSXWindowEvent.c | 8 ++++---- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/generic/tkGrab.c b/generic/tkGrab.c index d4ecdb0c5e..2232ba5ded 100644 --- a/generic/tkGrab.c +++ b/generic/tkGrab.c @@ -1029,7 +1029,7 @@ TkInOutEvents( /* * Generate enter/leave events and add them to the grab event queue. */ - + #define QUEUE(w, t, d) \ if (w->window != None) { \ eventPtr->type = t; \ @@ -1087,7 +1087,7 @@ TkInOutEvents( QUEUE(sourcePtr, leaveType, NotifyNonlinear); for (winPtr = sourcePtr->parentPtr, i = upLevels-1; i > 0; winPtr = winPtr->parentPtr, i--) { - QUEUE(winPtr, leaveType, NotifyNonlinearVirtual); + QUEUE(winPtr, leaveType, NotifyNonlinearVirtual); } } if (enterType != 0) { @@ -1167,7 +1167,6 @@ MovePointer2( TkInOutEvents(&event, sourcePtr, destPtr, (leaveEvents) ? LeaveNotify : 0, (enterEvents) ? EnterNotify : 0, TCL_QUEUE_MARK); } - /* *---------------------------------------------------------------------- diff --git a/generic/tkPointer.c b/generic/tkPointer.c index c2ba29f7c1..905489a72f 100644 --- a/generic/tkPointer.c +++ b/generic/tkPointer.c @@ -45,7 +45,6 @@ static int GenerateEnterLeave(TkWindow *winPtr, int x, int y, static void InitializeEvent(XEvent *eventPtr, TkWindow *winPtr, int type, int x, int y, int state, int detail); static void UpdateCursor(TkWindow *winPtr); - /* *---------------------------------------------------------------------- @@ -184,6 +183,7 @@ GenerateEnterLeave( InitializeEvent(&event, targetPtr, LeaveNotify, x, y, state, NotifyNormal); + TkInOutEvents(&event, lastWinPtr, winPtr, LeaveNotify, EnterNotify, TCL_QUEUE_TAIL); crossed = 1; @@ -493,6 +493,7 @@ TkPointerDeadWindow( { ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + if (winPtr == tsdPtr->lastWinPtr) { tsdPtr->lastWinPtr = TkGetContainer(winPtr); } diff --git a/generic/tkWindow.c b/generic/tkWindow.c index 644fea3df4..44f27f7072 100644 --- a/generic/tkWindow.c +++ b/generic/tkWindow.c @@ -1807,7 +1807,6 @@ Tk_MapWindow( TkWmMapWindow(winPtr); return; } - winPtr->flags |= TK_MAPPED; XMapWindow(winPtr->display, winPtr->window); event.type = MapNotify; diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index fc7acfdd02..68ec36974e 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -252,12 +252,12 @@ extern NSString *NSWindowDidOrderOffScreenNotification; - (void) windowMapped: (NSNotification *) notification { - NSWindow *w = [notification object]; - TkWindow *winPtr = TkMacOSXGetTkWindow(w); + NSWindow *w = [notification object]; + TkWindow *winPtr = TkMacOSXGetTkWindow(w); - if (winPtr) { + if (winPtr) { while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} - } + } } - (void) windowLiveResize: (NSNotification *) notification From 012c6e1a16eb7548e1d804b07740458d6328dfb8 Mon Sep 17 00:00:00 2001 From: culler Date: Thu, 23 May 2024 19:53:24 +0000 Subject: [PATCH 23/96] Schedule a CI run --- .github/workflows/linux-build.yml | 1 + .github/workflows/mac-build.yml | 1 + .github/workflows/win-build.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml index 94140373d9..15a1a48052 100644 --- a/.github/workflows/linux-build.yml +++ b/.github/workflows/linux-build.yml @@ -5,6 +5,7 @@ on: - "main" - "core-8-branch" - "core-8-6-branch" + - "bug-22349fc78a-v2" tags: - "core-**" permissions: diff --git a/.github/workflows/mac-build.yml b/.github/workflows/mac-build.yml index 9fd297a226..b5f077f4a0 100644 --- a/.github/workflows/mac-build.yml +++ b/.github/workflows/mac-build.yml @@ -5,6 +5,7 @@ on: - "main" - "core-8-branch" - "core-8-6-branch" + - "bug-22349fc78a-v2" tags: - "core-**" permissions: diff --git a/.github/workflows/win-build.yml b/.github/workflows/win-build.yml index 6642a8dc6d..c1a44e5654 100644 --- a/.github/workflows/win-build.yml +++ b/.github/workflows/win-build.yml @@ -5,6 +5,7 @@ on: - "main" - "core-8-branch" - "core-8-6-branch" + - "bug-22349fc78a-v2" tags: - "core-**" permissions: From ba5defb6f8c03eb5f52b6641bf002059467df4a1 Mon Sep 17 00:00:00 2001 From: culler Date: Thu, 23 May 2024 23:32:31 +0000 Subject: [PATCH 24/96] fix wm-iconwindow-2.1 --- tests/wm.test | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/wm.test b/tests/wm.test index 926615e3c0..2b31e5e70d 100644 --- a/tests/wm.test +++ b/tests/wm.test @@ -945,10 +945,11 @@ test wm-iconwindow-1.5 {usage} -setup { test wm-iconwindow-2.1 {setting and reading values} -setup { destroy .icon + # Destroying and creating .t prevents a segfault on macOS + destroy .t + toplevel .t set result {} } -body { - #added to avoid a crash on macOS - deiconify .t; update lappend result [wm iconwindow .t] toplevel .icon -width 50 -height 50 -bg green wm iconwindow .t .icon From f0a5f7aa739d4042b0718f644987e04e1166b3f1 Mon Sep 17 00:00:00 2001 From: culler Date: Fri, 24 May 2024 01:54:08 +0000 Subject: [PATCH 25/96] Simpler fix for wm-iconwindow-2.1 which still does not explain the crash. --- tests/wm.test | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/wm.test b/tests/wm.test index 2b31e5e70d..9cb402a7b5 100644 --- a/tests/wm.test +++ b/tests/wm.test @@ -944,10 +944,9 @@ test wm-iconwindow-1.5 {usage} -setup { } -result {.icon is already an icon for .t2} test wm-iconwindow-2.1 {setting and reading values} -setup { + # without this macOS crashes for unknown reasons + wm iconwindow .t {} destroy .icon - # Destroying and creating .t prevents a segfault on macOS - destroy .t - toplevel .t set result {} } -body { lappend result [wm iconwindow .t] From c4c94b611dab530185007be10efece9695dcdb72 Mon Sep 17 00:00:00 2001 From: fvogel Date: Fri, 24 May 2024 03:44:47 +0000 Subject: [PATCH 26/96] Remove leftover debugging code. --- win/tkWinWm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/win/tkWinWm.c b/win/tkWinWm.c index df9b1ce686..c387111819 100644 --- a/win/tkWinWm.c +++ b/win/tkWinWm.c @@ -2602,7 +2602,6 @@ static void CheckForPointer(TkWindow *winPtr) } ckfree(windows); } - fflush(stderr); } void From 285f6cdbf8bad836d1de14c2dd11f7e86daeeb8e Mon Sep 17 00:00:00 2001 From: culler Date: Fri, 24 May 2024 15:30:48 +0000 Subject: [PATCH 27/96] Fix bug introduced in FrontWindowAtPoint which causes unixWm-50.1 to fail by ignoring that the pointer is in a titlebar. Also, make the unixWm-50 tests more robust by ensuring that the root window is out of the way. (This matters for macOS 14, not 11). --- macosx/tkMacOSXWm.c | 13 +++++++++++-- tests/unixWm.test | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index 6c7468afad..e782dda5aa 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -623,17 +623,26 @@ FrontWindowAtPoint( for (NSWindow *w in windows) { winPtr = TkMacOSXGetTkWindow(w); if (winPtr) { + // WmInfo *wmPtr = winPtr->wmInfoPtr; NSRect windowFrame = [w frame]; - NSRect contentFrame = windowFrame; + NSRect contentFrame = [w frame]; + + contentFrame.size.height = [[w contentView] frame].size.height; /* * For consistency with other platforms, points in the * title bar are not considered to be contained in the * window. */ - contentFrame.size.height = [[w contentView] frame].size.height; if (NSMouseInRect(p, contentFrame, NO)) { return winPtr; + } else if (NSMouseInRect(p, windowFrame, NO)) { + /* + * The pointer is in the title bar of the highest NSWindow + * containing it, and therefore is should not be considered + * to be contained in any Tk window. + */ + return NULL; } } } diff --git a/tests/unixWm.test b/tests/unixWm.test index 472db21075..65561d47fa 100644 --- a/tests/unixWm.test +++ b/tests/unixWm.test @@ -1806,6 +1806,8 @@ test unixWm-49.2 {Tk_GetRootCoords procedure, menubars} {unix testmenubar} { } {52 7 12 62} deleteWindows +# Make sure that the root window is out of the way! +wm geom . +700+700 wm withdraw . if {[tk windowingsystem] eq "aqua"} { # Modern mac windows have no border. From 9317a30b3627fcf0010375bf708cc3fb5432a915 Mon Sep 17 00:00:00 2001 From: culler Date: Fri, 24 May 2024 19:46:55 +0000 Subject: [PATCH 28/96] Deal with compiler warnings when building with mingw gcc on Windows. --- win/tkWinWm.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/win/tkWinWm.c b/win/tkWinWm.c index c387111819..31fa03fe9b 100644 --- a/win/tkWinWm.c +++ b/win/tkWinWm.c @@ -2579,12 +2579,14 @@ TkpWmGetState( static void CheckForPointer(TkWindow *winPtr) { - Display *display = Tk_Display(winPtr); POINT mouse; + int x, y; unsigned int state = TkWinGetModifierState(); TkWindow **windows = TkWmStackorderToplevel(winPtr->mainPtr->winPtr); TkWindow **w; - TkGetPointerCoords(NULL, &mouse.x, &mouse.y); + x = (int) mouse.x; + y = (int) mouse.y; + TkGetPointerCoords(NULL, &x, &y); if (windows != NULL) { for (w = windows; *w ; w++) { RECT windowRect; @@ -2593,10 +2595,8 @@ static void CheckForPointer(TkWindow *winPtr) continue; } if (winPtr != *w && PtInRect(&windowRect, mouse)) { - Tk_Window target = Tk_CoordsToWindow(mouse.x, mouse.y, - (Tk_Window) *w); - Tk_UpdatePointer((Tk_Window) target, - mouse.x, mouse.y, state); + Tk_Window target = Tk_CoordsToWindow(x, y, (Tk_Window) *w); + Tk_UpdatePointer((Tk_Window) target, x, y, state); break; } } From c7a1c8ab1b6d29b631d2b77c3ddf3159b191000e Mon Sep 17 00:00:00 2001 From: culler Date: Sat, 25 May 2024 13:19:06 +0000 Subject: [PATCH 29/96] Fix silly error. --- win/tkWinWm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/win/tkWinWm.c b/win/tkWinWm.c index 31fa03fe9b..6d72009f30 100644 --- a/win/tkWinWm.c +++ b/win/tkWinWm.c @@ -2584,9 +2584,9 @@ static void CheckForPointer(TkWindow *winPtr) unsigned int state = TkWinGetModifierState(); TkWindow **windows = TkWmStackorderToplevel(winPtr->mainPtr->winPtr); TkWindow **w; - x = (int) mouse.x; - y = (int) mouse.y; TkGetPointerCoords(NULL, &x, &y); + mouse.x = x; + mouse.y = y; if (windows != NULL) { for (w = windows; *w ; w++) { RECT windowRect; From 596308daa8e8f621aafc4849bd0a98842a15402f Mon Sep 17 00:00:00 2001 From: culler Date: Sun, 26 May 2024 01:26:05 +0000 Subject: [PATCH 30/96] Try macOS 12 --- .github/workflows/mac-build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/mac-build.yml b/.github/workflows/mac-build.yml index b5f077f4a0..7f07669434 100644 --- a/.github/workflows/mac-build.yml +++ b/.github/workflows/mac-build.yml @@ -14,7 +14,7 @@ env: ERROR_ON_FAILURES: 1 jobs: xcode: - runs-on: macos-11 + runs-on: macos-12 defaults: run: shell: bash @@ -49,7 +49,7 @@ jobs: } - name: Run Tests run: | - make test | tee out.txt + make TESTFLAGS="-verbose bpest" test | tee out.txt nmatches=$( grep -c "Failed 0" out.txt ) if [ $nmatches -lt 4 ] then @@ -58,7 +58,7 @@ jobs: fi timeout-minutes: 30 clang: - runs-on: macos-11 + runs-on: macos-12 strategy: matrix: symbols: From 10156faa8f5ac4df59f0e7e6356dc1262bd1e3c5 Mon Sep 17 00:00:00 2001 From: fvogel Date: Sun, 26 May 2024 18:38:52 +0000 Subject: [PATCH 31/96] Remove commented statements in event.test. --- tests/event.test | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/event.test b/tests/event.test index d2f65e56a4..7fcdab3fa9 100644 --- a/tests/event.test +++ b/tests/event.test @@ -935,12 +935,6 @@ test event-9.11 {pointer window container = parent} -setup { set result "|" } -body { destroy .one.f1.f2 -# if {[tk windowingsystem] eq "aqua"} { -# # Generate a NSMouseMoved NSevent with no mouse pointer position change. -# # This is to let event-9.1? tests pass on macOS aqua and is only a workaround -# # since macOS aqua should send the adequate NSevent by itself. -# movemouse [expr {[winfo rootx .one] + 250}] [expr {[winfo rooty .one] + 250}] -# } _pause 200; # service crossing events set result } -cleanup { From 1bb534ff3475c78cff17d791f2d046490b1fcd1e Mon Sep 17 00:00:00 2001 From: culler Date: Sun, 26 May 2024 20:17:58 +0000 Subject: [PATCH 32/96] Remove unneeded call to TkWmDeadWindow --- generic/tkWindow.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/generic/tkWindow.c b/generic/tkWindow.c index 44f27f7072..c411bd3bf8 100644 --- a/generic/tkWindow.c +++ b/generic/tkWindow.c @@ -1354,9 +1354,6 @@ static void SendEnterLeaveForDestroy( containerPtr = TkGetContainer((TkWindow *)pointerWin); Tk_UpdatePointer((Tk_Window) containerPtr, x, y, state); } - else if (pointerWin) { - TkWmDeadWindow((TkWindow *) pointerWin); - } } if (pointerWin && (tkwin == Tk_Parent(pointerWin))) { From d2d0cce3dd0d647a9744613bff159313ff486260 Mon Sep 17 00:00:00 2001 From: culler Date: Mon, 27 May 2024 16:07:10 +0000 Subject: [PATCH 33/96] Don't call tkUpdatePointer from TkWmDeadWindow unless it is necessary. --- macosx/tkMacOSXWm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index e782dda5aa..96478f4441 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -1027,7 +1027,9 @@ TkWmDeadWindow( root_y = floor(TkMacOSXZeroScreenHeight() - mouse.y); int win_x, win_y; Tk_Window target = Tk_TopCoordsToWindow((Tk_Window) winPtr2, top_x, top_y, &win_x, &win_y); - Tk_UpdatePointer((Tk_Window) target, root_x, root_y, [NSApp tkButtonState]); + if (target != (Tk_Window) winPtr) { + Tk_UpdatePointer((Tk_Window) target, root_x, root_y, [NSApp tkButtonState]); + } } /* From d0863507871c8c40fefb431343082e2607e2f583 Mon Sep 17 00:00:00 2001 From: culler Date: Tue, 28 May 2024 03:35:38 +0000 Subject: [PATCH 34/96] Avoid calling TkUpdatePointer for a window which is being destroyed by a child's binding script. --- macosx/tkMacOSXWm.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index 96478f4441..d5575ccaa8 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -1027,8 +1027,11 @@ TkWmDeadWindow( root_y = floor(TkMacOSXZeroScreenHeight() - mouse.y); int win_x, win_y; Tk_Window target = Tk_TopCoordsToWindow((Tk_Window) winPtr2, top_x, top_y, &win_x, &win_y); - if (target != (Tk_Window) winPtr) { - Tk_UpdatePointer((Tk_Window) target, root_x, root_y, [NSApp tkButtonState]); + /* A non-toplevel window can have a NULL parent while it is in the process of + * being destroyed. We should not call Tk_UpdatePointer in that case. + */ + if (Tk_Parent(target) != NULL || Tk_IsTopLevel(target)) { + Tk_UpdatePointer(target, root_x, root_y, [NSApp tkButtonState]); } } From 4e2ce7f2c671d121814f282afbe74d72a6bdd217 Mon Sep 17 00:00:00 2001 From: culler Date: Tue, 28 May 2024 16:10:46 +0000 Subject: [PATCH 35/96] Adjust some tests and schedule a CI run on macOS 14. --- .github/workflows/mac-build.yml | 3 ++- tests/font.test | 13 +++++++------ tests/unixWm.test | 1 + 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/mac-build.yml b/.github/workflows/mac-build.yml index 9fd297a226..cb4b6f756f 100644 --- a/.github/workflows/mac-build.yml +++ b/.github/workflows/mac-build.yml @@ -5,6 +5,7 @@ on: - "main" - "core-8-branch" - "core-8-6-branch" + - "macOS-14-CI" tags: - "core-**" permissions: @@ -13,7 +14,7 @@ env: ERROR_ON_FAILURES: 1 jobs: xcode: - runs-on: macos-11 + runs-on: macos-14 defaults: run: shell: bash diff --git a/tests/font.test b/tests/font.test index 4a264f8b1c..5ceb1ee521 100644 --- a/tests/font.test +++ b/tests/font.test @@ -2427,15 +2427,15 @@ test font-47.2 {Bug 3049518 - Canvas} -body { set twidth [font measure MyFont $text] set theight [font metrics MyFont -linespace] set circid [$c create polygon \ - 15 15 \ - [expr {15 + $twidth}] 15 \ - [expr {15 + $twidth}] [expr {15 + $theight}] \ - 15 [expr {15 + $theight}] \ - -width 1 -joinstyle round -smooth true -fill {} -outline blue] + 15 15 \ + [expr {15 + $twidth}] 15 \ + [expr {15 + $twidth}] [expr {15 + $theight}] \ + 15 [expr {15 + $theight}] \ + -width 1 -joinstyle round -smooth true -fill {} -outline blue] pack $c -fill both -expand 1 -side top update - # Lamda test functions + # Lambda test functions set circle_text {{w user_data text circ} { if {[winfo class $w] ne "Canvas"} { puts "Wrong widget type: $w" @@ -2461,6 +2461,7 @@ test font-47.2 {Bug 3049518 - Canvas} -body { apply $circle_text $c FontChanged $textid $circid update bind $c <> [list apply $circle_text %W %d $textid $circid] + update idletasks # Begin test: set results {} diff --git a/tests/unixWm.test b/tests/unixWm.test index 4bf466cb5b..2898d19aee 100644 --- a/tests/unixWm.test +++ b/tests/unixWm.test @@ -105,6 +105,7 @@ foreach geom "+20+80 +80+$Y0 +0+$Y0 -0-0 +0-0 -0+$Y0 -10-5 -10+$Y5 +10-5" { set i 1 foreach geom "+20+80 +80+$Y0 +0+$Y0 -0-0 +0-0 -0+$Y0 -10-5 -10+$Y5 +10-5" { test unixWm-3.$i {moving window while iconified} unix { + update wm iconify .t update idletasks wm geom .t $geom From 5c57addb770317e6c2acafffbed2f3d51485bcfd Mon Sep 17 00:00:00 2001 From: culler Date: Tue, 28 May 2024 22:28:13 +0000 Subject: [PATCH 36/96] fix wm-manage-1.8 (without fixing wm manage) --- macosx/tkMacOSXWm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index d5575ccaa8..50ff5f8194 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -2922,6 +2922,7 @@ WmManageCmd( } else if (Tk_IsTopLevel(frameWin)) { /* Already managed by wm - ignore it */ } + winPtr->geomMgrPtr = &wmMgrType; return TCL_OK; } From 65eb0c8c381b378de6ac9095d2b5549078b4eee5 Mon Sep 17 00:00:00 2001 From: culler Date: Wed, 29 May 2024 01:20:07 +0000 Subject: [PATCH 37/96] Use Tk_ManageGeometry here too. --- macosx/tkMacOSXWm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index 50ff5f8194..b154f182a8 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -2922,7 +2922,7 @@ WmManageCmd( } else if (Tk_IsTopLevel(frameWin)) { /* Already managed by wm - ignore it */ } - winPtr->geomMgrPtr = &wmMgrType; + Tk_ManageGeometry((Tk_Window)winPtr, &wmMgrType, NULL); return TCL_OK; } From b39986a99d587498f5ba40e71cddf6ea0360b89f Mon Sep 17 00:00:00 2001 From: culler Date: Wed, 29 May 2024 16:18:42 +0000 Subject: [PATCH 38/96] Add a simple version of the processevents test command and use it for the event-9* tests. --- generic/tkTest.c | 46 ++++++++++++++++++++++++++ tests/event.test | 85 +++++++++++++++++++++++++----------------------- 2 files changed, 91 insertions(+), 40 deletions(-) diff --git a/generic/tkTest.c b/generic/tkTest.c index 755a6bef8b..4fc6a80d75 100644 --- a/generic/tkTest.c +++ b/generic/tkTest.c @@ -147,6 +147,9 @@ typedef struct TrivialCommandHeader { static int ImageObjCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj * const objv[]); +static int ProcessEventsObjCmd(ClientData dummy, + Tcl_Interp *interp, int objc, + Tcl_Obj * const objv[]); static int TestbitmapObjCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj * const objv[]); @@ -247,6 +250,7 @@ Tktest_Init( return TCL_ERROR; } + Tcl_CreateObjCommand(interp, "processevents", ProcessEventsObjCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "square", SquareObjCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "testbitmap", TestbitmapObjCmd, (ClientData) Tk_MainWindow(interp), NULL); @@ -1679,6 +1683,48 @@ ImageDelete( ckfree(timPtr); } +/* + *---------------------------------------------------------------------- + * + * ProcessEventsObjCmd -- + * + * This function implements the "processevents" command. Currently + * It processes all or events on the queue. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Events are processed + * + *---------------------------------------------------------------------- + */ + +static Tk_RestrictAction +CrossingRestrictProc( + ClientData arg, + XEvent *eventPtr) +{ + if (eventPtr->type == EnterNotify || eventPtr->type == LeaveNotify) { + return TK_PROCESS_EVENT; + } + return TK_DEFER_EVENT; +} + +static int ProcessEventsObjCmd(ClientData dummy, + Tcl_Interp *interp, + int objc, + Tcl_Obj * const objv[]) +{ + ClientData oldArg; + Tk_RestrictProc *oldProc; + int count = 0; + oldProc = Tk_RestrictEvents(CrossingRestrictProc, NULL, &oldArg); + while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS|TCL_DONT_WAIT)) {}; + Tk_RestrictEvents(oldProc, oldArg, &oldArg); + return TCL_OK; +} + /* *---------------------------------------------------------------------- * diff --git a/tests/event.test b/tests/event.test index 7fcdab3fa9..10dcf0780b 100644 --- a/tests/event.test +++ b/tests/event.test @@ -929,13 +929,13 @@ test event-9.11 {pointer window container = parent} -setup { create_and_pack_frames .one wm deiconify .one tkwait visibility .one.f1.f2 - _pause 200 - bind all {append result " %d %W|"} - bind all {append result " %d %W|"} + update; # finish display of window set result "|" } -body { + bind all {append result " %d %W|"} + bind all {append result " %d %W|"} destroy .one.f1.f2 - _pause 200; # service crossing events + processevents end set result } -cleanup { bind all {} @@ -953,13 +953,13 @@ test event-9.12 {pointer window container != parent} -setup { wm deiconify .one tkwait visibility .one.g event generate .one -warp 1 -x 250 -y 250 - _pause 200 - bind all {append result " %d %W|"} - bind all {append result " %d %W|"} + update; # finish mouse warp set result "|" } -body { + bind all {append result " %d %W|"} + bind all {append result " %d %W|"} destroy .one.g - _pause 200; # service crossing events -- crashes without this + processevents end set result } -cleanup { bind all {} @@ -975,18 +975,19 @@ test event-9.13 {pointer window is a toplevel, toplevel destination} -setup { wm withdraw .two wm deiconify .two waitForWindowEvent .two - bind all {append result " %d %W|"} - bind all {append result " %d %W|"} + update; # finish displaying windows set result | } -body { + bind all {append result " %d %W|"} + bind all {append result " %d %W|"} destroy .two waitForWindowEvent .one - # destroying .one here instead of in cleanup makes the test pass - destroy .one + processevents end set result } -cleanup { bind all {} bind all {} + destroy .one unset result } -result {| NotifyNonlinear .one|} @@ -1000,17 +1001,18 @@ test event-9.14 {pointer window is a toplevel, tk internal destination} -setup { wm deiconify .one wm deiconify .two waitForWindowEvent .two - bind all {append result " %d %W|"} - bind all {append result " %d %W|"} set result "|" } -body { + bind all {append result " %d %W|"} + bind all {append result " %d %W|"} destroy .two waitForWindowEvent .one.f1.f2 + processevents end + set result +} -cleanup { bind all {} bind all {} destroy .one - set result -} -cleanup { unset result } -result {| NotifyNonlinearVirtual .one| NotifyNonlinearVirtual .one.f1| NotifyNonlinear .one.f1.f2|} @@ -1022,12 +1024,12 @@ test event-9.15 {pointer window is a toplevel, destination is screen root} -setu waitForWindowEvent .two event generate .two -warp 1 -x 275 -y 275 controlPointerWarpTiming - bind all {append result " %d %W|"} - bind all {append result " %d %W|"} set result "|" } -body { - _pause 200; # ensure servicing of all scheduled events (only events expected) + bind all {append result " %d %W|"} + bind all {append result " %d %W|"} destroy .two + processevents end set result } -cleanup { bind all {} @@ -1044,13 +1046,13 @@ test event-9.16 {Successive destructions (pointer window + parent), single gener create_and_pack_frames .one wm deiconify .one tkwait visibility .one.f1.f2 - _pause 200 - bind all {append result " %d %W|"} - bind all {append result " %d %W|"} + update; # finish displaying window set result "|" } -body { + bind all {append result " %d %W|"} + bind all {append result " %d %W|"} destroy .one.f1 - _pause 200; # service crossing events + processevents end set result } -cleanup { bind all {} @@ -1067,15 +1069,16 @@ test event-9.17 {Successive destructions (pointer window + parent), separate cro create_and_pack_frames .one wm deiconify .one tkwait visibility .one.f1.f2 - _pause 200 - bind all {append result " %d %W|"} - bind all {append result " %d %W|"} + update; # finish displaying window set result "|" } -body { + bind all {append result " %d %W|"} + bind all {append result " %d %W|"} destroy .one.f1.f2 - _pause 200; # service crossing events + update; # make sure window is gone destroy .one.f1 - _pause 200; # service crossing events + update; # make sure window is gone + processevents end set result } -cleanup { bind all {} @@ -1092,17 +1095,18 @@ test event-9.18 {Successive destructions (pointer window + ancestors including i create_and_pack_frames .two wm deiconify .two waitForWindowEvent .two.f1.f2 - bind all {append result " %d %W|"} - bind all {append result " %d %W|"} set result "|" } -body { + bind all {append result " %d %W|"} + bind all {append result " %d %W|"} destroy .two waitForWindowEvent .one + processevents end + set result +} -cleanup { bind all {} bind all {} destroy .one - set result -} -cleanup { unset result } -result {| NotifyNonlinear .one|} @@ -1114,24 +1118,25 @@ test event-9.19 {Successive destructions (pointer window + ancestors including i wm geometry .two 300x300+100+100 create_and_pack_frames .two wm deiconify .two - waitForWindowEvent .two.f1.f2 toplevel .three pack propagate .three 0 wm geometry .three 300x300+110+110 create_and_pack_frames .three wm deiconify .three waitForWindowEvent .three.f1.f2 - bind all {append result " %d %W|"} - bind all {append result " %d %W|"} + update; # finish displaying windows set result "|" } -body { + bind all {append result " %d %W|"} + bind all {append result " %d %W|"} destroy .three waitForWindowEvent .two.f1.f2 + processevents end + set result +} -cleanup { bind all {} bind all {} destroy .two - set result -} -cleanup { unset result } -result {| NotifyNonlinearVirtual .two| NotifyNonlinearVirtual .two.f1| NotifyNonlinear .two.f1.f2|} @@ -1144,12 +1149,12 @@ test event-9.20 {Successive destructions (pointer window + ancestors including i create_and_pack_frames .two wm deiconify .two waitForWindowEvent .two.f1.f2 - bind all {append result " %d %W|"} - bind all {append result " %d %W|"} set result "|" } -body { + bind all {append result " %d %W|"} + bind all {append result " %d %W|"} destroy .two - _pause 200; # service events (only screen drawing events expected) + processevents end set result } -cleanup { bind all {} From c333b6fa71d92be0364b3ed3b7ecd9af3c58f3cc Mon Sep 17 00:00:00 2001 From: culler Date: Wed, 29 May 2024 16:23:21 +0000 Subject: [PATCH 39/96] remove unused variable; forgot to save events.test --- generic/tkTest.c | 1 - tests/event.test | 20 ++++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/generic/tkTest.c b/generic/tkTest.c index 4fc6a80d75..a5f1c340ca 100644 --- a/generic/tkTest.c +++ b/generic/tkTest.c @@ -1718,7 +1718,6 @@ static int ProcessEventsObjCmd(ClientData dummy, { ClientData oldArg; Tk_RestrictProc *oldProc; - int count = 0; oldProc = Tk_RestrictEvents(CrossingRestrictProc, NULL, &oldArg); while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS|TCL_DONT_WAIT)) {}; Tk_RestrictEvents(oldProc, oldArg, &oldArg); diff --git a/tests/event.test b/tests/event.test index 10dcf0780b..65208b1af8 100644 --- a/tests/event.test +++ b/tests/event.test @@ -935,7 +935,7 @@ test event-9.11 {pointer window container = parent} -setup { bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .one.f1.f2 - processevents end + processevents set result } -cleanup { bind all {} @@ -959,7 +959,7 @@ test event-9.12 {pointer window container != parent} -setup { bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .one.g - processevents end + processevents set result } -cleanup { bind all {} @@ -982,7 +982,7 @@ test event-9.13 {pointer window is a toplevel, toplevel destination} -setup { bind all {append result " %d %W|"} destroy .two waitForWindowEvent .one - processevents end + processevents set result } -cleanup { bind all {} @@ -1007,7 +1007,7 @@ test event-9.14 {pointer window is a toplevel, tk internal destination} -setup { bind all {append result " %d %W|"} destroy .two waitForWindowEvent .one.f1.f2 - processevents end + processevents set result } -cleanup { bind all {} @@ -1029,7 +1029,7 @@ test event-9.15 {pointer window is a toplevel, destination is screen root} -setu bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .two - processevents end + processevents set result } -cleanup { bind all {} @@ -1052,7 +1052,7 @@ test event-9.16 {Successive destructions (pointer window + parent), single gener bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .one.f1 - processevents end + processevents set result } -cleanup { bind all {} @@ -1078,7 +1078,7 @@ test event-9.17 {Successive destructions (pointer window + parent), separate cro update; # make sure window is gone destroy .one.f1 update; # make sure window is gone - processevents end + processevents set result } -cleanup { bind all {} @@ -1101,7 +1101,7 @@ test event-9.18 {Successive destructions (pointer window + ancestors including i bind all {append result " %d %W|"} destroy .two waitForWindowEvent .one - processevents end + processevents set result } -cleanup { bind all {} @@ -1131,7 +1131,7 @@ test event-9.19 {Successive destructions (pointer window + ancestors including i bind all {append result " %d %W|"} destroy .three waitForWindowEvent .two.f1.f2 - processevents end + processevents set result } -cleanup { bind all {} @@ -1154,7 +1154,7 @@ test event-9.20 {Successive destructions (pointer window + ancestors including i bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .two - processevents end + processevents set result } -cleanup { bind all {} From 79c9294609224f128ca3f4b901ecefbf444955fd Mon Sep 17 00:00:00 2001 From: culler Date: Wed, 29 May 2024 16:50:18 +0000 Subject: [PATCH 40/96] Restore some calls to _pause, needed by Windows. --- tests/event.test | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/event.test b/tests/event.test index 65208b1af8..8a691045ff 100644 --- a/tests/event.test +++ b/tests/event.test @@ -929,9 +929,11 @@ test event-9.11 {pointer window container = parent} -setup { create_and_pack_frames .one wm deiconify .one tkwait visibility .one.f1.f2 + _pause 200; # needed for Windows update; # finish display of window set result "|" } -body { + processevents bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .one.f1.f2 @@ -954,6 +956,7 @@ test event-9.12 {pointer window container != parent} -setup { tkwait visibility .one.g event generate .one -warp 1 -x 250 -y 250 update; # finish mouse warp + _pause 200; # needed for Windows set result "|" } -body { bind all {append result " %d %W|"} @@ -1046,7 +1049,8 @@ test event-9.16 {Successive destructions (pointer window + parent), single gener create_and_pack_frames .one wm deiconify .one tkwait visibility .one.f1.f2 - update; # finish displaying window + update; # finish displaying window + _pause 200; # needed for Windows set result "|" } -body { bind all {append result " %d %W|"} @@ -1070,6 +1074,7 @@ test event-9.17 {Successive destructions (pointer window + parent), separate cro wm deiconify .one tkwait visibility .one.f1.f2 update; # finish displaying window + _pause 200; # needed for Windows set result "|" } -body { bind all {append result " %d %W|"} From 7555afa81653a8967d77ef4697b1d4b0f5d25e30 Mon Sep 17 00:00:00 2001 From: culler Date: Wed, 29 May 2024 18:11:08 +0000 Subject: [PATCH 41/96] Make the processevents command general. --- generic/tkTest.c | 41 +++++++++++++++++++++++++++++++++-------- tests/event.test | 22 +++++++++++----------- 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/generic/tkTest.c b/generic/tkTest.c index a5f1c340ca..2eaa4de125 100644 --- a/generic/tkTest.c +++ b/generic/tkTest.c @@ -1688,14 +1688,16 @@ ImageDelete( * * ProcessEventsObjCmd -- * - * This function implements the "processevents" command. Currently - * It processes all or events on the queue. + * This function implements the "processevents" command which processes + * all queued events of a type specified by one of the arguments to the + * command. Currently the supported arguments are leave, enter, and + * motion. Others could be added if needed. * * Results: - * A standard Tcl result. + * A standard Tcl result. * * Side effects: - * Events are processed + * Events are processed * *---------------------------------------------------------------------- */ @@ -1705,20 +1707,43 @@ CrossingRestrictProc( ClientData arg, XEvent *eventPtr) { - if (eventPtr->type == EnterNotify || eventPtr->type == LeaveNotify) { - return TK_PROCESS_EVENT; + int *eventTypes = (int *) arg; + for (int *t = eventTypes; *t != 0; t++) { + if (eventPtr->type == *t) { + return TK_PROCESS_EVENT; + } } return TK_DEFER_EVENT; } -static int ProcessEventsObjCmd(ClientData dummy, +static int ProcessEventsObjCmd( + ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj * const objv[]) { ClientData oldArg; Tk_RestrictProc *oldProc; - oldProc = Tk_RestrictEvents(CrossingRestrictProc, NULL, &oldArg); + int index; + static const char *const eventTypeNames[] = { + "leave", "enter", "motion", NULL}; + static const int eventTypes[] = { + LeaveNotify, EnterNotify, MotionNotify}; + int whichEvents[100]; + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "eventtype ?eventtype ...?"); + return TCL_ERROR; + } + for (int n = 1; n < objc; n++) { + if (Tcl_GetIndexFromObj(interp, objv[n], eventTypeNames, "eventtype", 0, + &index) != TCL_OK) { + return TCL_ERROR; + } + whichEvents[n - 1] = eventTypes[index]; + } + whichEvents[objc - 1] = 0; + oldProc = Tk_RestrictEvents(CrossingRestrictProc, (void *) whichEvents, + &oldArg); while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS|TCL_DONT_WAIT)) {}; Tk_RestrictEvents(oldProc, oldArg, &oldArg); return TCL_OK; diff --git a/tests/event.test b/tests/event.test index 8a691045ff..7bdf57c5af 100644 --- a/tests/event.test +++ b/tests/event.test @@ -933,11 +933,11 @@ test event-9.11 {pointer window container = parent} -setup { update; # finish display of window set result "|" } -body { - processevents + processevents enter leave bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .one.f1.f2 - processevents + processevents enter leave set result } -cleanup { bind all {} @@ -962,7 +962,7 @@ test event-9.12 {pointer window container != parent} -setup { bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .one.g - processevents + processevents enter leave set result } -cleanup { bind all {} @@ -985,7 +985,7 @@ test event-9.13 {pointer window is a toplevel, toplevel destination} -setup { bind all {append result " %d %W|"} destroy .two waitForWindowEvent .one - processevents + processevents enter leave set result } -cleanup { bind all {} @@ -1010,7 +1010,7 @@ test event-9.14 {pointer window is a toplevel, tk internal destination} -setup { bind all {append result " %d %W|"} destroy .two waitForWindowEvent .one.f1.f2 - processevents + processevents enter leave set result } -cleanup { bind all {} @@ -1032,7 +1032,7 @@ test event-9.15 {pointer window is a toplevel, destination is screen root} -setu bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .two - processevents + processevents enter leave set result } -cleanup { bind all {} @@ -1056,7 +1056,7 @@ test event-9.16 {Successive destructions (pointer window + parent), single gener bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .one.f1 - processevents + processevents enter leave set result } -cleanup { bind all {} @@ -1083,7 +1083,7 @@ test event-9.17 {Successive destructions (pointer window + parent), separate cro update; # make sure window is gone destroy .one.f1 update; # make sure window is gone - processevents + processevents enter leave set result } -cleanup { bind all {} @@ -1106,7 +1106,7 @@ test event-9.18 {Successive destructions (pointer window + ancestors including i bind all {append result " %d %W|"} destroy .two waitForWindowEvent .one - processevents + processevents enter leave set result } -cleanup { bind all {} @@ -1136,7 +1136,7 @@ test event-9.19 {Successive destructions (pointer window + ancestors including i bind all {append result " %d %W|"} destroy .three waitForWindowEvent .two.f1.f2 - processevents + processevents enter leave set result } -cleanup { bind all {} @@ -1159,7 +1159,7 @@ test event-9.20 {Successive destructions (pointer window + ancestors including i bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .two - processevents + processevents enter leave set result } -cleanup { bind all {} From cdbcd05743c083c4a28648aaa4c064ccef44b292 Mon Sep 17 00:00:00 2001 From: culler Date: Wed, 29 May 2024 19:07:21 +0000 Subject: [PATCH 42/96] Switch back to the macOS-11 runner, for comparison --- .github/workflows/mac-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mac-build.yml b/.github/workflows/mac-build.yml index d7fd4badbb..59b93d3872 100644 --- a/.github/workflows/mac-build.yml +++ b/.github/workflows/mac-build.yml @@ -14,7 +14,7 @@ env: ERROR_ON_FAILURES: 1 jobs: xcode: - runs-on: macos-14 + runs-on: macos-11 defaults: run: shell: bash From 44dc0fba4b17d751ad739e095a0459e69716085b Mon Sep 17 00:00:00 2001 From: culler Date: Thu, 30 May 2024 03:41:42 +0000 Subject: [PATCH 43/96] Small adjustments to tests. --- generic/tkTest.c | 4 ++-- tests/event.test | 23 +++++++++++++---------- tests/winfo.test | 2 ++ 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/generic/tkTest.c b/generic/tkTest.c index 2eaa4de125..b424a36247 100644 --- a/generic/tkTest.c +++ b/generic/tkTest.c @@ -1726,9 +1726,9 @@ static int ProcessEventsObjCmd( Tk_RestrictProc *oldProc; int index; static const char *const eventTypeNames[] = { - "leave", "enter", "motion", NULL}; + "leave", "enter", "motion", "expose", NULL}; static const int eventTypes[] = { - LeaveNotify, EnterNotify, MotionNotify}; + LeaveNotify, EnterNotify, MotionNotify, Expose}; int whichEvents[100]; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "eventtype ?eventtype ...?"); diff --git a/tests/event.test b/tests/event.test index 7bdf57c5af..292e41e287 100644 --- a/tests/event.test +++ b/tests/event.test @@ -930,10 +930,9 @@ test event-9.11 {pointer window container = parent} -setup { wm deiconify .one tkwait visibility .one.f1.f2 _pause 200; # needed for Windows - update; # finish display of window + update idletasks; # finish display of window set result "|" } -body { - processevents enter leave bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .one.f1.f2 @@ -955,7 +954,6 @@ test event-9.12 {pointer window container != parent} -setup { wm deiconify .one tkwait visibility .one.g event generate .one -warp 1 -x 250 -y 250 - update; # finish mouse warp _pause 200; # needed for Windows set result "|" } -body { @@ -978,7 +976,7 @@ test event-9.13 {pointer window is a toplevel, toplevel destination} -setup { wm withdraw .two wm deiconify .two waitForWindowEvent .two - update; # finish displaying windows + update idletasks; # finish displaying windows set result | } -body { bind all {append result " %d %W|"} @@ -1021,10 +1019,12 @@ test event-9.14 {pointer window is a toplevel, tk internal destination} -setup { test event-9.15 {pointer window is a toplevel, destination is screen root} -setup { setup_win_mousepointer .one; # ensure the mouse pointer is where we want it to be (the .one toplevel is not itself used in this test) +# destroy .one toplevel .two wm geometry .two 300x300+150+150 wm deiconify .two waitForWindowEvent .two + update idletasks; # finish displaying .two event generate .two -warp 1 -x 275 -y 275 controlPointerWarpTiming set result "|" @@ -1032,7 +1032,7 @@ test event-9.15 {pointer window is a toplevel, destination is screen root} -setu bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .two - processevents enter leave +# processevents enter leave set result } -cleanup { bind all {} @@ -1049,7 +1049,7 @@ test event-9.16 {Successive destructions (pointer window + parent), single gener create_and_pack_frames .one wm deiconify .one tkwait visibility .one.f1.f2 - update; # finish displaying window + update idletasks; # finish displaying window _pause 200; # needed for Windows set result "|" } -body { @@ -1073,7 +1073,7 @@ test event-9.17 {Successive destructions (pointer window + parent), separate cro create_and_pack_frames .one wm deiconify .one tkwait visibility .one.f1.f2 - update; # finish displaying window + update idletasks; # finish displaying window _pause 200; # needed for Windows set result "|" } -body { @@ -1117,7 +1117,7 @@ test event-9.18 {Successive destructions (pointer window + ancestors including i test event-9.19 {Successive destructions (pointer window + ancestors including its toplevel), destination is internal window, bypass root win} -setup { setup_win_mousepointer .one; # ensure the mouse pointer is where we want it to be (the .one toplevel is not itself used in this test) - destroy .one +# destroy .one toplevel .two pack propagate .two 0 wm geometry .two 300x300+100+100 @@ -1129,18 +1129,20 @@ test event-9.19 {Successive destructions (pointer window + ancestors including i create_and_pack_frames .three wm deiconify .three waitForWindowEvent .three.f1.f2 - update; # finish displaying windows + update idletasks; # finish displaying windows set result "|" } -body { bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .three waitForWindowEvent .two.f1.f2 + update idletasks; #finish destroying .two processevents enter leave set result } -cleanup { bind all {} bind all {} + destroy .one destroy .two unset result } -result {| NotifyNonlinearVirtual .two| NotifyNonlinearVirtual .two.f1| NotifyNonlinear .two.f1.f2|} @@ -1159,7 +1161,8 @@ test event-9.20 {Successive destructions (pointer window + ancestors including i bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .two - processevents enter leave + update idletasks; #finish destroying .two +# processevents enter leave set result } -cleanup { bind all {} diff --git a/tests/winfo.test b/tests/winfo.test index ffacf8f429..5a7a3eccd4 100644 --- a/tests/winfo.test +++ b/tests/winfo.test @@ -441,8 +441,10 @@ test winfo-13.3 {destroying container window} -setup { test winfo-13.4 {[winfo containing] with embedded windows} -setup { deleteWindows } -body { + wm geometry . +100+100 frame .con -container 1 pack .con -expand yes -fill both + update toplevel .emb -use [winfo id .con] -bd 0 -highlightthickness 0 button .emb.b pack .emb.b -expand yes -fill both From 789cd6ea2a3de0e5d19338d594b97c467316d897 Mon Sep 17 00:00:00 2001 From: culler Date: Thu, 30 May 2024 18:51:57 +0000 Subject: [PATCH 44/96] Don't use processevents on linux. --- tests/event.test | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/tests/event.test b/tests/event.test index 292e41e287..edfddc4ce7 100644 --- a/tests/event.test +++ b/tests/event.test @@ -936,7 +936,7 @@ test event-9.11 {pointer window container = parent} -setup { bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .one.f1.f2 - processevents enter leave + if {[tk windowingsystem] ne "x11" }]} {processevents enter leave} set result } -cleanup { bind all {} @@ -960,7 +960,7 @@ test event-9.12 {pointer window container != parent} -setup { bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .one.g - processevents enter leave + if {[tk windowingsystem] ne "x11" }]} {processevents enter leave} set result } -cleanup { bind all {} @@ -983,7 +983,7 @@ test event-9.13 {pointer window is a toplevel, toplevel destination} -setup { bind all {append result " %d %W|"} destroy .two waitForWindowEvent .one - processevents enter leave + if {[tk windowingsystem] ne "x11" }]} {processevents enter leave} set result } -cleanup { bind all {} @@ -1032,7 +1032,6 @@ test event-9.15 {pointer window is a toplevel, destination is screen root} -setu bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .two -# processevents enter leave set result } -cleanup { bind all {} @@ -1056,7 +1055,7 @@ test event-9.16 {Successive destructions (pointer window + parent), single gener bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .one.f1 - processevents enter leave + if {[tk windowingsystem] ne "x11" }]} {processevents enter leave} set result } -cleanup { bind all {} @@ -1083,7 +1082,7 @@ test event-9.17 {Successive destructions (pointer window + parent), separate cro update; # make sure window is gone destroy .one.f1 update; # make sure window is gone - processevents enter leave + if {[tk windowingsystem] ne "x11" }]} {processevents enter leave} set result } -cleanup { bind all {} @@ -1106,7 +1105,7 @@ test event-9.18 {Successive destructions (pointer window + ancestors including i bind all {append result " %d %W|"} destroy .two waitForWindowEvent .one - processevents enter leave + if {[tk windowingsystem] ne "x11" }]} {processevents enter leave} set result } -cleanup { bind all {} @@ -1137,7 +1136,7 @@ test event-9.19 {Successive destructions (pointer window + ancestors including i destroy .three waitForWindowEvent .two.f1.f2 update idletasks; #finish destroying .two - processevents enter leave + if {[tk windowingsystem] ne "x11" }]} {processevents enter leave} set result } -cleanup { bind all {} @@ -1162,7 +1161,6 @@ test event-9.20 {Successive destructions (pointer window + ancestors including i bind all {append result " %d %W|"} destroy .two update idletasks; #finish destroying .two -# processevents enter leave set result } -cleanup { bind all {} From 7e11e70c9bc4f893b845c39baea7c50cac82ffad Mon Sep 17 00:00:00 2001 From: culler Date: Fri, 31 May 2024 22:01:29 +0000 Subject: [PATCH 45/96] Fix typo in event.test; be less lazy in processevents --- generic/tkTest.c | 8 +++++++- tests/event.test | 14 +++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/generic/tkTest.c b/generic/tkTest.c index b424a36247..bd6dde3e3f 100644 --- a/generic/tkTest.c +++ b/generic/tkTest.c @@ -1729,11 +1729,17 @@ static int ProcessEventsObjCmd( "leave", "enter", "motion", "expose", NULL}; static const int eventTypes[] = { LeaveNotify, EnterNotify, MotionNotify, Expose}; - int whichEvents[100]; +#define NUM_TYPES (sizeof(eventTypes)/sizeof(int)) + int whichEvents[1 + NUM_TYPES]; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "eventtype ?eventtype ...?"); return TCL_ERROR; } + if (objc > NUM_TYPES + 1) { + Tcl_WrongNumArgs(interp, 1, objv, "too many event types"); + return TCL_ERROR; + } +#undef NUM_TYPES for (int n = 1; n < objc; n++) { if (Tcl_GetIndexFromObj(interp, objv[n], eventTypeNames, "eventtype", 0, &index) != TCL_OK) { diff --git a/tests/event.test b/tests/event.test index edfddc4ce7..585eb960c2 100644 --- a/tests/event.test +++ b/tests/event.test @@ -936,7 +936,7 @@ test event-9.11 {pointer window container = parent} -setup { bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .one.f1.f2 - if {[tk windowingsystem] ne "x11" }]} {processevents enter leave} + if {[tk windowingsystem] ne "x11"} {processevents enter leave} set result } -cleanup { bind all {} @@ -960,7 +960,7 @@ test event-9.12 {pointer window container != parent} -setup { bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .one.g - if {[tk windowingsystem] ne "x11" }]} {processevents enter leave} + if {[tk windowingsystem] ne "x11"} {processevents enter leave} set result } -cleanup { bind all {} @@ -983,7 +983,7 @@ test event-9.13 {pointer window is a toplevel, toplevel destination} -setup { bind all {append result " %d %W|"} destroy .two waitForWindowEvent .one - if {[tk windowingsystem] ne "x11" }]} {processevents enter leave} + if {[tk windowingsystem] ne "x11"} {processevents enter leave} set result } -cleanup { bind all {} @@ -1055,7 +1055,7 @@ test event-9.16 {Successive destructions (pointer window + parent), single gener bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .one.f1 - if {[tk windowingsystem] ne "x11" }]} {processevents enter leave} + if {[tk windowingsystem] ne "x11"} {processevents enter leave} set result } -cleanup { bind all {} @@ -1082,7 +1082,7 @@ test event-9.17 {Successive destructions (pointer window + parent), separate cro update; # make sure window is gone destroy .one.f1 update; # make sure window is gone - if {[tk windowingsystem] ne "x11" }]} {processevents enter leave} + if {[tk windowingsystem] ne "x11"} {processevents enter leave} set result } -cleanup { bind all {} @@ -1105,7 +1105,7 @@ test event-9.18 {Successive destructions (pointer window + ancestors including i bind all {append result " %d %W|"} destroy .two waitForWindowEvent .one - if {[tk windowingsystem] ne "x11" }]} {processevents enter leave} + if {[tk windowingsystem] ne "x11"} {processevents enter leave} set result } -cleanup { bind all {} @@ -1136,7 +1136,7 @@ test event-9.19 {Successive destructions (pointer window + ancestors including i destroy .three waitForWindowEvent .two.f1.f2 update idletasks; #finish destroying .two - if {[tk windowingsystem] ne "x11" }]} {processevents enter leave} + if {[tk windowingsystem] ne "x11"} {processevents enter leave} set result } -cleanup { bind all {} From 1419bbfee25ed9da6b99303e536b35dce74d91db Mon Sep 17 00:00:00 2001 From: culler Date: Fri, 31 May 2024 22:26:53 +0000 Subject: [PATCH 46/96] Remove commented out code --- macosx/tkMacOSXWm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index b154f182a8..09a77103e9 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -623,7 +623,6 @@ FrontWindowAtPoint( for (NSWindow *w in windows) { winPtr = TkMacOSXGetTkWindow(w); if (winPtr) { - // WmInfo *wmPtr = winPtr->wmInfoPtr; NSRect windowFrame = [w frame]; NSRect contentFrame = [w frame]; From a075e134b1d8a47abdce9fcedf5266529846f271 Mon Sep 17 00:00:00 2001 From: culler Date: Fri, 31 May 2024 22:36:47 +0000 Subject: [PATCH 47/96] keep the root on the screen for event tests --- tests/event.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/event.test b/tests/event.test index 585eb960c2..ea46dd1854 100644 --- a/tests/event.test +++ b/tests/event.test @@ -908,7 +908,7 @@ proc create_and_pack_frames {{w {}}} { proc setup_win_mousepointer {w} { # Position the window and the mouse pointer as an initial state for some tests. # The so-called "pointer window" is the $w window that will now contain the mouse pointer. - wm geometry . +700+700; # root window out of our way - must not cover windows from event-9.1* + wm geometry . +700+400; # root window out of our way - must not cover windows from event-9.1* toplevel $w pack propagate $w 0 wm geometry $w 300x300+100+100 From f1542aec0c1636298ec381a06c196f76a5807085 Mon Sep 17 00:00:00 2001 From: culler Date: Fri, 31 May 2024 23:09:32 +0000 Subject: [PATCH 48/96] Restore accidentally deleted linux test code --- tests/event.test | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/tests/event.test b/tests/event.test index ea46dd1854..c37fc67880 100644 --- a/tests/event.test +++ b/tests/event.test @@ -936,7 +936,11 @@ test event-9.11 {pointer window container = parent} -setup { bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .one.f1.f2 - if {[tk windowingsystem] ne "x11"} {processevents enter leave} + if {[tk windowingsystem] ne "x11"} { + processevents enter leave + } else { + update + } set result } -cleanup { bind all {} @@ -960,7 +964,11 @@ test event-9.12 {pointer window container != parent} -setup { bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .one.g - if {[tk windowingsystem] ne "x11"} {processevents enter leave} + if {[tk windowingsystem] ne "x11"} { + processevents enter leave + } else { + update + } set result } -cleanup { bind all {} @@ -983,7 +991,11 @@ test event-9.13 {pointer window is a toplevel, toplevel destination} -setup { bind all {append result " %d %W|"} destroy .two waitForWindowEvent .one - if {[tk windowingsystem] ne "x11"} {processevents enter leave} + if {[tk windowingsystem] ne "x11"} { + processevents enter leave + } else { + update + } set result } -cleanup { bind all {} @@ -1055,7 +1067,11 @@ test event-9.16 {Successive destructions (pointer window + parent), single gener bind all {append result " %d %W|"} bind all {append result " %d %W|"} destroy .one.f1 - if {[tk windowingsystem] ne "x11"} {processevents enter leave} + if {[tk windowingsystem] ne "x11"} { + processevents enter leave + } else { + update + } set result } -cleanup { bind all {} From b0afa64a2c8f5b2ea0535f19c4822c63e1444ef6 Mon Sep 17 00:00:00 2001 From: culler Date: Sun, 2 Jun 2024 11:58:50 +0000 Subject: [PATCH 49/96] partial progress on update failures and liveResize --- macosx/tkMacOSXNotify.c | 2 +- macosx/tkMacOSXSubwindows.c | 21 +++++------ macosx/tkMacOSXWindowEvent.c | 68 ++++++++++++++++++++++++++---------- 3 files changed, 61 insertions(+), 30 deletions(-) diff --git a/macosx/tkMacOSXNotify.c b/macosx/tkMacOSXNotify.c index 9d01450092..4b6a8182e1 100644 --- a/macosx/tkMacOSXNotify.c +++ b/macosx/tkMacOSXNotify.c @@ -383,7 +383,7 @@ TkMacOSXDrawAllViews( if ([view needsDisplay]) { #if TK_MAC_CGIMAGE_DRAWING // Should no longer ever need to setNeedsDisplay:NO - if (0) fprintf(stderr, "nD still set %p\n", view); + if (1) fprintf(stderr, "nD still set %p\n", view); #else [view setNeedsDisplay: NO]; #endif diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index 3fc476223e..3c9ebc8527 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -182,7 +182,7 @@ XMapWindow( [win setExcludedFromWindowsMenu:NO]; [NSApp activateIgnoringOtherApps:initialized]; // Not sure this does anything useful for TK_MAC_SYNCHRONOUS_DRAWING - [view addTkDirtyRect: [view bounds]]; + // [view addTkDirtyRect: [view bounds]]; if (initialized) { if ([win canBecomeKeyWindow]) { [win makeKeyAndOrderFront:NSApp]; @@ -235,11 +235,12 @@ XMapWindow( * or if using generateExposeEvents: is the best way; * what does XMapWindow() do on other platforms?) */ - [view generateExposeEvents:[view bounds]]; + // Probably only needs to use the widget bounds. + [view generateExposeEvents:[view bounds]]; #else - if (view != [NSView focusView]) { - [view addTkDirtyRect:[view bounds]]; - } + // if (view != [NSView focusView]) { + // [view addTkDirtyRect:[view bounds]]; + // } #endif /* @@ -384,10 +385,10 @@ XUnmapWindow( * i.e. why the existing approach uses addTkDirtyRect: here) */ #else - TKContentView *view = [win contentView]; - if (view != [NSView focusView]) { - [view addTkDirtyRect:[view bounds]]; - } + // TKContentView *view = [win contentView]; + // if (view != [NSView focusView]) { + // [view addTkDirtyRect:[view bounds]]; + // } #endif return Success; } @@ -1047,7 +1048,7 @@ InvalViewRect( // Cannot rely on addTkDirtyRect: to force redrawing. [view generateExposeEvents:dirtyRect]; #else - [view addTkDirtyRect:dirtyRect]; + // [view addTkDirtyRect:dirtyRect]; #endif break; } diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 0f9d57352e..b1a147d9de 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -506,17 +506,28 @@ GenerateUpdates( { TkWindow *childPtr; XEvent event; - CGRect bounds, damageBounds; + CGRect damageBounds; + CGRect bounds; TkMacOSXWinCGBounds(winPtr, &bounds); + +#if 0 + //This code is meant to increase efficiency but doesn't work. + //The rectangles are reported as disjoint when they are not. if (!CGRectIntersectsRect(bounds, *updateBounds)) { - return 0; + fprintf(stderr, "No intersection for %s\n", Tk_PathName(winPtr)); + fprintf(stderr, "Received %s\n", NSStringFromRect(*updateBounds).UTF8String); + fprintf(stderr, "Widget bounds %s\n", NSStringFromRect(bounds).UTF8String); + fflush(stderr); + return 0; } +#endif /* * Compute the bounding box of the area that the damage occurred in. */ + // Maybe this inteersection is suspect too! damageBounds = CGRectIntersection(bounds, *updateBounds); event.xany.serial = LastKnownRequestProcessed(Tk_Display(winPtr)); event.xany.send_event = false; @@ -994,10 +1005,20 @@ ConfigureRestrictProc( return YES; } - (void) updateLayer { - if (0) fprintf(stderr, "updateLayer\n"); CGContextRef ctx = self.tkLayerBitmapContext; - + if ([NSApp inLiveResize]) { + fprintf(stderr, "updateLayer called during liveResize: %s\n", + NSStringFromSize(self.frame.size).UTF8String); + fflush(stderr); + } if (ctx) { + /* + * Make a copy, probably using copy-on-write, of the CGImage + * currently being used for drawing, and assign the copy as the + * contents layer of the NSView. This will cause all drawing + * done since the last call to this function to now become + * visible. + */ CGImageRef newImg = CGBitmapContextCreateImage(ctx); self.layer.contents = (__bridge id)newImg; CGImageRelease(newImg); // will quickly leak memory if this is missing @@ -1039,12 +1060,13 @@ ConfigureRestrictProc( { _tkNeedsDisplay = YES; _tkDirtyRect = NSUnionRect(_tkDirtyRect, rect); - [self setNeedsDisplay:YES]; -#if TK_MAC_CGIMAGE_DRAWING + // This seems to not be needed. + // [self setNeedsDisplay:YES]; +////#if TK_MAC_CGIMAGE_DRAWING // Layer-backed: want the NSView to control when to draw -#else +////#else [[self layer] setNeedsDisplay]; -#endif +////#endif } - (void) clearTkDirtyRect @@ -1063,9 +1085,12 @@ ConfigureRestrictProc( TkWindow *winPtr = TkMacOSXGetTkWindow(w); Tk_Window tkwin = (Tk_Window)winPtr; - if (![self inLiveResize] && - [w respondsToSelector: @selector (tkLayoutChanged)]) { - [(TKWindow *)w tkLayoutChanged]; + if (![self inLiveResize]) { + if ([w respondsToSelector: @selector (tkLayoutChanged)]) { + [(TKWindow *)w tkLayoutChanged]; + } + } else { + [self viewDidChangeBackingProperties]; } if (winPtr) { @@ -1122,7 +1147,7 @@ ConfigureRestrictProc( - (void) generateExposeEvents: (NSRect) rect { - unsigned long serial; + // unsigned long serial; int updatesNeeded; CGRect updateBounds; TkWindow *winPtr = TkMacOSXGetTkWindow([self window]); @@ -1137,23 +1162,26 @@ ConfigureRestrictProc( * serial number. */ - updateBounds = NSRectToCGRect(rect); + if ([self inLiveResize]) { + updateBounds = [self bounds]; + } else { + updateBounds = NSRectToCGRect(rect); + } updateBounds.origin.y = ([self bounds].size.height - updateBounds.origin.y - updateBounds.size.height); updatesNeeded = GenerateUpdates(&updateBounds, winPtr); //if (!TK_MAC_SYNCHRONOUS_DRAWING && updatesNeeded) { - if ([self inLiveResize] && updatesNeeded) { - - serial = LastKnownRequestProcessed(Tk_Display(winPtr)); + if (updatesNeeded) { + //serial = LastKnownRequestProcessed(Tk_Display(winPtr)); /* * Use the ExposeRestrictProc to process only the expose events. This * will create idle drawing tasks, which we handle before we return. */ - oldProc = Tk_RestrictEvents(ExposeRestrictProc, UINT2PTR(serial), &oldArg); - while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS|TCL_DONT_WAIT)) {}; - Tk_RestrictEvents(oldProc, oldArg, &oldArg); + // oldProc = Tk_RestrictEvents(ExposeRestrictProc, UINT2PTR(serial), &oldArg); + // while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS|TCL_DONT_WAIT)) {}; + // Tk_RestrictEvents(oldProc, oldArg, &oldArg); /* * Starting with OSX 10.14, which uses Core Animation to draw windows, @@ -1347,9 +1375,11 @@ static const char *const accentNames[] = { kCGBitmapByteOrder32Big | kCGImageAlphaNoneSkipLast // will also need to specify this when capturing ); CGContextScaleCTM(newCtx, self.layer.contentsScale, self.layer.contentsScale); +#if 0 if (0) fprintf(stderr, "rTkLBC %.1f %s %p %p %ld\n", (float)self.layer.contentsScale, NSStringFromSize(self.frame.size).UTF8String, colorspace, newCtx, self.tkLayerBitmapContext ? (long)CFGetRetainCount(self.tkLayerBitmapContext) : INT_MIN); if (0) fprintf(stderr, "rTkLBC %p %ld\n", self.tkLayerBitmapContext, (long)(self.tkLayerBitmapContext ? CFGetRetainCount(self.tkLayerBitmapContext) : LONG_MIN)); +#endif CGContextRelease(self.tkLayerBitmapContext); // will also need this in a destructor somewhere self.tkLayerBitmapContext = newCtx; CGColorSpaceRelease(colorspace); From ad3fd76922bb42ea86416bbb7ae309555c28433a Mon Sep 17 00:00:00 2001 From: culler Date: Sun, 2 Jun 2024 14:07:47 +0000 Subject: [PATCH 50/96] Live resize now mostly works, but some frames do not get redrawn during the resize. --- macosx/tkMacOSXWindowEvent.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index b1a147d9de..8afdd8c9f7 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -512,7 +512,7 @@ GenerateUpdates( TkMacOSXWinCGBounds(winPtr, &bounds); #if 0 - //This code is meant to increase efficiency but doesn't work. + //This code is meant to increase efficiency but it doesn't work. //The rectangles are reported as disjoint when they are not. if (!CGRectIntersectsRect(bounds, *updateBounds)) { fprintf(stderr, "No intersection for %s\n", Tk_PathName(winPtr)); @@ -1006,11 +1006,6 @@ ConfigureRestrictProc( } - (void) updateLayer { CGContextRef ctx = self.tkLayerBitmapContext; - if ([NSApp inLiveResize]) { - fprintf(stderr, "updateLayer called during liveResize: %s\n", - NSStringFromSize(self.frame.size).UTF8String); - fflush(stderr); - } if (ctx) { /* * Make a copy, probably using copy-on-write, of the CGImage @@ -1051,7 +1046,9 @@ ConfigureRestrictProc( #if TK_MAC_CGIMAGE_DRAWING [self resetTkLayerBitmapContext]; // need to redraw + TkWindow *winPtr = TkMacOSXGetTkWindow([self window]); [self generateExposeEvents: [self bounds]]; + while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} #endif } #endif @@ -1085,14 +1082,6 @@ ConfigureRestrictProc( TkWindow *winPtr = TkMacOSXGetTkWindow(w); Tk_Window tkwin = (Tk_Window)winPtr; - if (![self inLiveResize]) { - if ([w respondsToSelector: @selector (tkLayoutChanged)]) { - [(TKWindow *)w tkLayoutChanged]; - } - } else { - [self viewDidChangeBackingProperties]; - } - if (winPtr) { unsigned int width = (unsigned int)newsize.width; unsigned int height=(unsigned int)newsize.height; @@ -1123,12 +1112,25 @@ ConfigureRestrictProc( Tk_RestrictEvents(oldProc, oldArg, &oldArg); /* - * Now that Tk has configured all subwindows, create the clip regions. + * Now that Tk has configured all subwindows, create the clip regions + * and re-enable drawing. */ - TkMacOSXSetDrawingEnabled(winPtr, 1); TkMacOSXInvalClipRgns(tkwin); TkMacOSXUpdateClipRgn(winPtr); + TkMacOSXSetDrawingEnabled(winPtr, 1); + + /* + * Redraw the entire content view. + */ + + if ([self inLiveResize]) { + [self viewDidChangeBackingProperties]; + } else { + if ([w respondsToSelector: @selector (tkLayoutChanged)]) { + [(TKWindow *)w tkLayoutChanged]; + } + } /* * Finally, unlock the main autoreleasePool. From 2f22f9f1bb057c4981f53ee8cfd0d5889b78bd3e Mon Sep 17 00:00:00 2001 From: culler Date: Sun, 2 Jun 2024 15:03:40 +0000 Subject: [PATCH 51/96] LiveResize working, but could be faster. --- macosx/tkMacOSXWindowEvent.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 8afdd8c9f7..992bbac3e7 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -508,7 +508,7 @@ GenerateUpdates( XEvent event; CGRect damageBounds; CGRect bounds; - + NSView *view = TkMacOSXGetNSViewForDrawable((Drawable)winPtr->privatePtr); TkMacOSXWinCGBounds(winPtr, &bounds); #if 0 @@ -527,8 +527,10 @@ GenerateUpdates( * Compute the bounding box of the area that the damage occurred in. */ - // Maybe this inteersection is suspect too! damageBounds = CGRectIntersection(bounds, *updateBounds); + // fprintf(stderr, "Generating updates for %s in %s\n", Tk_PathName(winPtr), + // NSStringFromRect(damageBounds).UTF8String); + // fflush(stderr); event.xany.serial = LastKnownRequestProcessed(Tk_Display(winPtr)); event.xany.send_event = false; event.xany.window = Tk_WindowId(winPtr); @@ -539,8 +541,12 @@ GenerateUpdates( event.xexpose.width = damageBounds.size.width; event.xexpose.height = damageBounds.size.height; event.xexpose.count = 0; - Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); - + if ([view inLiveResize]) { + Tk_HandleEvent(&event); + } else { + Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); + } + #ifdef TK_MAC_DEBUG_DRAWING TKLog(@"Exposed %p {{%d, %d}, {%d, %d}}", event.xany.window, event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height); From a97bd572a0f106f756deea1fd2bee638daa602ba Mon Sep 17 00:00:00 2001 From: culler Date: Sun, 2 Jun 2024 22:14:57 +0000 Subject: [PATCH 52/96] Try to optimize the CGImage creation a bit. --- macosx/tkMacOSXWindowEvent.c | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 992bbac3e7..fe25f52a92 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -1021,7 +1021,7 @@ ConfigureRestrictProc( * visible. */ CGImageRef newImg = CGBitmapContextCreateImage(ctx); - self.layer.contents = (__bridge id)newImg; + self.layer.contents = (__bridge id) newImg; CGImageRelease(newImg); // will quickly leak memory if this is missing [self clearTkDirtyRect]; } @@ -1054,7 +1054,6 @@ ConfigureRestrictProc( // need to redraw TkWindow *winPtr = TkMacOSXGetTkWindow([self window]); [self generateExposeEvents: [self bounds]]; - while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} #endif } #endif @@ -1063,12 +1062,11 @@ ConfigureRestrictProc( { _tkNeedsDisplay = YES; _tkDirtyRect = NSUnionRect(_tkDirtyRect, rect); - // This seems to not be needed. - // [self setNeedsDisplay:YES]; + //[self setNeedsDisplay:YES]; ////#if TK_MAC_CGIMAGE_DRAWING // Layer-backed: want the NSView to control when to draw ////#else - [[self layer] setNeedsDisplay]; +// [[self layer] setNeedsDisplay]; ////#endif } @@ -1132,6 +1130,7 @@ ConfigureRestrictProc( if ([self inLiveResize]) { [self viewDidChangeBackingProperties]; + [self setNeedsDisplay:YES]; } else { if ([w respondsToSelector: @selector (tkLayoutChanged)]) { [(TKWindow *)w tkLayoutChanged]; @@ -1364,19 +1363,11 @@ static const char *const accentNames[] = { #if TK_MAC_CGIMAGE_DRAWING -(void) resetTkLayerBitmapContext { - CGColorSpaceRef colorspace = NULL; - - // Try using display colorspace to avoid performance hit due to colorspace conversion - // (allow disabling in case color accuracy is needed) - if (!getenv("TK_NODISPLAYCOLORSPACE")) { - colorspace = CGDisplayCopyColorSpace(CGMainDisplayID()); - } - - // fallback (uses sRGB on macOS 10.8 and later) - if (!colorspace) { + static CGColorSpaceRef colorspace = NULL; + if (colorspace == NULL) { colorspace = CGColorSpaceCreateDeviceRGB(); + CGColorSpaceRetain(colorspace); } - CGContextRef newCtx = CGBitmapContextCreate( NULL, self.layer.contentsScale * self.frame.size.width, self.layer.contentsScale * self.frame.size.height, 8, 0, colorspace, @@ -1384,13 +1375,15 @@ static const char *const accentNames[] = { ); CGContextScaleCTM(newCtx, self.layer.contentsScale, self.layer.contentsScale); #if 0 - if (0) fprintf(stderr, "rTkLBC %.1f %s %p %p %ld\n", (float)self.layer.contentsScale, - NSStringFromSize(self.frame.size).UTF8String, colorspace, newCtx, self.tkLayerBitmapContext ? (long)CFGetRetainCount(self.tkLayerBitmapContext) : INT_MIN); - if (0) fprintf(stderr, "rTkLBC %p %ld\n", self.tkLayerBitmapContext, (long)(self.tkLayerBitmapContext ? CFGetRetainCount(self.tkLayerBitmapContext) : LONG_MIN)); + fprintf(stderr, "rTkLBC %.1f %s %p %p %ld\n", (float)self.layer.contentsScale, + NSStringFromSize(self.frame.size).UTF8String, colorspace, newCtx, + self.tkLayerBitmapContext ? (long)CFGetRetainCount(self.tkLayerBitmapContext) : INT_MIN); + fprintf(stderr, "rTkLBC %p %ld\n", self.tkLayerBitmapContext, + (long)(self.tkLayerBitmapContext ? CFGetRetainCount(self.tkLayerBitmapContext) : LONG_MIN)); + fflush(stderr); #endif CGContextRelease(self.tkLayerBitmapContext); // will also need this in a destructor somewhere self.tkLayerBitmapContext = newCtx; - CGColorSpaceRelease(colorspace); } #endif From 9152676202ae5b70ac54403989c6154962a471a0 Mon Sep 17 00:00:00 2001 From: culler Date: Mon, 3 Jun 2024 03:05:20 +0000 Subject: [PATCH 53/96] Fix segfault in font.test and other issues. --- macosx/tkMacOSXNotify.c | 2 +- macosx/tkMacOSXWindowEvent.c | 56 +++++++++++------------------------- 2 files changed, 17 insertions(+), 41 deletions(-) diff --git a/macosx/tkMacOSXNotify.c b/macosx/tkMacOSXNotify.c index 4b6a8182e1..9d01450092 100644 --- a/macosx/tkMacOSXNotify.c +++ b/macosx/tkMacOSXNotify.c @@ -383,7 +383,7 @@ TkMacOSXDrawAllViews( if ([view needsDisplay]) { #if TK_MAC_CGIMAGE_DRAWING // Should no longer ever need to setNeedsDisplay:NO - if (1) fprintf(stderr, "nD still set %p\n", view); + if (0) fprintf(stderr, "nD still set %p\n", view); #else [view setNeedsDisplay: NO]; #endif diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index fe25f52a92..b98e6c959f 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -506,20 +506,13 @@ GenerateUpdates( { TkWindow *childPtr; XEvent event; - CGRect damageBounds; - CGRect bounds; + CGRect bounds, damageBounds; NSView *view = TkMacOSXGetNSViewForDrawable((Drawable)winPtr->privatePtr); - TkMacOSXWinCGBounds(winPtr, &bounds); #if 0 - //This code is meant to increase efficiency but it doesn't work. - //The rectangles are reported as disjoint when they are not. + TkMacOSXWinCGBounds(winPtr, &bounds); if (!CGRectIntersectsRect(bounds, *updateBounds)) { - fprintf(stderr, "No intersection for %s\n", Tk_PathName(winPtr)); - fprintf(stderr, "Received %s\n", NSStringFromRect(*updateBounds).UTF8String); - fprintf(stderr, "Widget bounds %s\n", NSStringFromRect(bounds).UTF8String); - fflush(stderr); - return 0; + return 0; } #endif @@ -528,9 +521,6 @@ GenerateUpdates( */ damageBounds = CGRectIntersection(bounds, *updateBounds); - // fprintf(stderr, "Generating updates for %s in %s\n", Tk_PathName(winPtr), - // NSStringFromRect(damageBounds).UTF8String); - // fflush(stderr); event.xany.serial = LastKnownRequestProcessed(Tk_Display(winPtr)); event.xany.send_event = false; event.xany.window = Tk_WindowId(winPtr); @@ -546,7 +536,7 @@ GenerateUpdates( } else { Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); } - + #ifdef TK_MAC_DEBUG_DRAWING TKLog(@"Exposed %p {{%d, %d}, {%d, %d}}", event.xany.window, event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height); @@ -1012,6 +1002,7 @@ ConfigureRestrictProc( } - (void) updateLayer { CGContextRef ctx = self.tkLayerBitmapContext; + if (ctx) { /* * Make a copy, probably using copy-on-write, of the CGImage @@ -1052,7 +1043,6 @@ ConfigureRestrictProc( #if TK_MAC_CGIMAGE_DRAWING [self resetTkLayerBitmapContext]; // need to redraw - TkWindow *winPtr = TkMacOSXGetTkWindow([self window]); [self generateExposeEvents: [self bounds]]; #endif } @@ -1062,12 +1052,6 @@ ConfigureRestrictProc( { _tkNeedsDisplay = YES; _tkDirtyRect = NSUnionRect(_tkDirtyRect, rect); - //[self setNeedsDisplay:YES]; -////#if TK_MAC_CGIMAGE_DRAWING - // Layer-backed: want the NSView to control when to draw -////#else -// [[self layer] setNeedsDisplay]; -////#endif } - (void) clearTkDirtyRect @@ -1086,6 +1070,11 @@ ConfigureRestrictProc( TkWindow *winPtr = TkMacOSXGetTkWindow(w); Tk_Window tkwin = (Tk_Window)winPtr; + if (![self inLiveResize] && + [w respondsToSelector: @selector (tkLayoutChanged)]) { + [(TKWindow *)w tkLayoutChanged]; + } + if (winPtr) { unsigned int width = (unsigned int)newsize.width; unsigned int height=(unsigned int)newsize.height; @@ -1116,8 +1105,7 @@ ConfigureRestrictProc( Tk_RestrictEvents(oldProc, oldArg, &oldArg); /* - * Now that Tk has configured all subwindows, create the clip regions - * and re-enable drawing. + * Now that Tk has configured all subwindows, create the clip regions. */ TkMacOSXInvalClipRgns(tkwin); @@ -1154,7 +1142,6 @@ ConfigureRestrictProc( - (void) generateExposeEvents: (NSRect) rect { - // unsigned long serial; int updatesNeeded; CGRect updateBounds; TkWindow *winPtr = TkMacOSXGetTkWindow([self window]); @@ -1168,7 +1155,6 @@ ConfigureRestrictProc( * Generate Tk Expose events. All of these events will share the same * serial number. */ - if ([self inLiveResize]) { updateBounds = [self bounds]; } else { @@ -1177,18 +1163,7 @@ ConfigureRestrictProc( updateBounds.origin.y = ([self bounds].size.height - updateBounds.origin.y - updateBounds.size.height); updatesNeeded = GenerateUpdates(&updateBounds, winPtr); - //if (!TK_MAC_SYNCHRONOUS_DRAWING && updatesNeeded) { - if (updatesNeeded) { - //serial = LastKnownRequestProcessed(Tk_Display(winPtr)); - - /* - * Use the ExposeRestrictProc to process only the expose events. This - * will create idle drawing tasks, which we handle before we return. - */ - - // oldProc = Tk_RestrictEvents(ExposeRestrictProc, UINT2PTR(serial), &oldArg); - // while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS|TCL_DONT_WAIT)) {}; - // Tk_RestrictEvents(oldProc, oldArg, &oldArg); + if ([self inLiveResize] && updatesNeeded) { /* * Starting with OSX 10.14, which uses Core Animation to draw windows, @@ -1377,10 +1352,11 @@ static const char *const accentNames[] = { #if 0 fprintf(stderr, "rTkLBC %.1f %s %p %p %ld\n", (float)self.layer.contentsScale, NSStringFromSize(self.frame.size).UTF8String, colorspace, newCtx, - self.tkLayerBitmapContext ? (long)CFGetRetainCount(self.tkLayerBitmapContext) : INT_MIN); + self.tkLayerBitmapContext ? + (long)CFGetRetainCount(self.tkLayerBitmapContext) : INT_MIN); fprintf(stderr, "rTkLBC %p %ld\n", self.tkLayerBitmapContext, - (long)(self.tkLayerBitmapContext ? CFGetRetainCount(self.tkLayerBitmapContext) : LONG_MIN)); - fflush(stderr); + (long)(self.tkLayerBitmapContext ? + CFGetRetainCount(self.tkLayerBitmapContext) : LONG_MIN)); #endif CGContextRelease(self.tkLayerBitmapContext); // will also need this in a destructor somewhere self.tkLayerBitmapContext = newCtx; From 76b3d8182fa5a7bc55475313f14c55601907085a Mon Sep 17 00:00:00 2001 From: culler Date: Tue, 4 Jun 2024 03:31:32 +0000 Subject: [PATCH 54/96] typo --- macosx/tkMacOSXWindowEvent.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index b98e6c959f..0713862dd6 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -509,8 +509,8 @@ GenerateUpdates( CGRect bounds, damageBounds; NSView *view = TkMacOSXGetNSViewForDrawable((Drawable)winPtr->privatePtr); -#if 0 TkMacOSXWinCGBounds(winPtr, &bounds); +#if 0 if (!CGRectIntersectsRect(bounds, *updateBounds)) { return 0; } From 8a64061d4a91cfc949aa598b67d455bc5e7e0566 Mon Sep 17 00:00:00 2001 From: culler Date: Tue, 4 Jun 2024 13:33:06 +0000 Subject: [PATCH 55/96] Need to do a redraw in setFrameSize when not in liveResize as well. --- macosx/tkMacOSXWindowEvent.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 0713862dd6..3906223bad 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -1073,6 +1073,8 @@ ConfigureRestrictProc( if (![self inLiveResize] && [w respondsToSelector: @selector (tkLayoutChanged)]) { [(TKWindow *)w tkLayoutChanged]; + [self generateExposeEvents:[self bounds]]; + } if (winPtr) { From ce6a5945cca949ab5db2ce0ec8a534f4a5fcdd45 Mon Sep 17 00:00:00 2001 From: culler Date: Tue, 4 Jun 2024 18:46:34 +0000 Subject: [PATCH 56/96] Cleanup --- library/demos/mac_styles.tcl | 12 +++--- macosx/tkMacOSXDraw.c | 71 ------------------------------------ macosx/tkMacOSXImage.c | 30 +-------------- macosx/tkMacOSXInt.h | 15 -------- macosx/tkMacOSXNotify.c | 25 ------------- macosx/tkMacOSXPrivate.h | 4 -- macosx/tkMacOSXSubwindows.c | 27 +------------- macosx/tkMacOSXTest.c | 18 +-------- macosx/tkMacOSXWindowEvent.c | 37 ------------------- 9 files changed, 11 insertions(+), 228 deletions(-) diff --git a/library/demos/mac_styles.tcl b/library/demos/mac_styles.tcl index 3fff03c3b9..804b5c0ba5 100644 --- a/library/demos/mac_styles.tcl +++ b/library/demos/mac_styles.tcl @@ -245,16 +245,16 @@ if { [wm attributes $w -isdark] } { } proc beLight {f w} { wm attributes $w -appearance aqua - $f.dark state !selected - $f.light state selected - after 10 $f.light state !hover + # A small delay is needed for the appearance change to complete. + after 10 [list $f.dark state !selected] + after 10 [list $f.light state selected] } proc beDark {f w} { wm attributes $w -appearance darkaqua - $f.light state !selected - $f.dark state selected - after 10 $f.dark state !hover + # A small delay is needed for the appearance change to complete. + after 10 [list $f.light state !selected] + after 10 [list $f.dark state selected] } $w.notebook add $appearanceFrame -text "Appearance" diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index c1f844ef6c..881a635592 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -1088,11 +1088,7 @@ XFillArcs( int TkScrollWindow( Tk_Window tkwin, /* The window to be scrolled. */ -#if TK_MAC_SYNCHRONOUS_DRAWING GC gc, /* GC for window to be scrolled. */ -#else - TCL_UNUSED(GC), /* GC for window to be scrolled. */ -#endif int x, int y, /* Position rectangle to be scrolled. */ int width, int height, int dx, int dy, /* Distance rectangle should be moved. */ @@ -1104,33 +1100,9 @@ TkScrollWindow( NSRect srcRect, dstRect; int result = 0; -#if TK_MAC_SYNCHRONOUS_DRAWING // Should behave more like TkScrollWindow on other platforms if (XCopyArea(Tk_Display(tkwin), drawable, drawable, gc, x, y, (unsigned)width, (unsigned)height, x+dx, y+dy) == Success) { -#else - MacDrawable* macDraw = (MacDrawable*)drawable; - TKContentView *view = (TKContentView *)TkMacOSXGetNSViewForDrawable(macDraw); - if (view) { -#endif - -#if TK_MAC_SYNCHRONOUS_DRAWING - // Already scrolled using XCopyArea() -#else - /* - * Get the scroll area in NSView coordinates (origin at bottom left). - */ - - NSRect bounds = [view bounds]; - NSRect viewSrcRect = NSMakeRect(macDraw->xOff + x, - bounds.size.height - height - (macDraw->yOff + y), - width, height); - /* - * Scroll the rectangle. - */ - - [view scrollRect:viewSrcRect by:NSMakeSize(dx, -dy)]; -#endif /* * Compute the damage region, using Tk coordinates (origin at top left). @@ -1252,11 +1224,7 @@ TkMacOSXSetupDrawingContext( if (!dc.context) { NSRect drawingBounds; dc.view = view; -#if TK_MAC_CGIMAGE_DRAWING dc.context = view.tkLayerBitmapContext; -#else - dc.context = GET_CGCONTEXT; -#endif if (dc.clipRgn) { CGRect clipBounds; CGAffineTransform t = { .a = 1, .b = 0, .c = 0, .d = -1, .tx = 0, @@ -1268,7 +1236,6 @@ TkMacOSXSetupDrawingContext( drawingBounds = [view bounds]; } -#if TK_MAC_SYNCHRONOUS_DRAWING // It seems this should be the only place to use addTkDirtyRect: // and that it should not be used elsewhere as a proxy to generate // Expose events, which will not work. @@ -1320,42 +1287,6 @@ TkMacOSXSetupDrawingContext( NSAppearance.currentAppearance = view.effectiveAppearance; #endif } -#else - /* - * We can only draw into the NSView which is the current focusView. - * When the current [NSView focusView] is nil, the CGContext for - * [NSGraphicsContext currentContext] is nil. Otherwise the current - * CGContext draws into the current focusView. An NSView is guaranteed - * to be the focusView when its drawRect or setFrame methods are - * running. Prior to OSX 10.14 it was also possible to call the - * lockFocus method to force an NSView to become the current focusView. - * But that method was deprecated in 10.14 and so is no longer used by - * Tk. Instead, if the view is not the current focusView then we add - * the drawing bounds to its dirty rectangle and return false. The - * part of the view inside the drawing bounds will get redrawn during - * the next call to its drawRect method. - */ - - if (view != [NSView focusView]) { - [view addTkDirtyRect:drawingBounds]; - canDraw = false; - goto end; - } - - /* - * Drawing will also fail when the view is the current focusView but - * the clipping rectangle set by drawRect does not contain the clipping - * region of our drawing context. (See bug [2a61eca3a8].) If part of - * the drawing bounds will be clipped then we draw whatever we can, but - * we also add the drawing bounds to the view's dirty rectangle so it - * will get redrawn in the next call to its drawRect method. - */ - - currentBounds = NSRectFromCGRect(CGContextGetClipBoundingBox(dc.context)); - if (!NSContainsRect(currentBounds, drawingBounds)) { - [view addTkDirtyRect:drawingBounds]; - } -#endif } /* @@ -1495,10 +1426,8 @@ TkMacOSXSetupDrawingContext( dc.clipRgn = NULL; } *dcPtr = dc; -#if TK_MAC_SYNCHRONOUS_DRAWING // The goal is to allow immediate drawing; canDraw == 0 should happen far less often. if (0) fprintf(stderr, "tkmacosxsdc canDraw %d\n", canDraw); -#endif return canDraw; } diff --git a/macosx/tkMacOSXImage.c b/macosx/tkMacOSXImage.c index 02630f19e1..87d12763fd 100644 --- a/macosx/tkMacOSXImage.c +++ b/macosx/tkMacOSXImage.c @@ -20,10 +20,7 @@ #include "xbytes.h" static CGImageRef CreateCGImageFromPixmap(Drawable pixmap); -static CGImageRef CreateCGImageFromDrawableRect( Drawable drawable, -#if TK_MAC_SYNCHRONOUS_DRAWING - int force_1x_scale, -#endif +static CGImageRef CreateCGImageFromDrawableRect( Drawable drawable, int force_1x_scale, int x, int y, unsigned int width, unsigned int height); /* Pixel formats @@ -646,9 +643,7 @@ int TkpPutRGBAImage( static CGImageRef CreateCGImageFromDrawableRect( Drawable drawable, -#if TK_MAC_SYNCHRONOUS_DRAWING int force_1x_scale, -#endif int x, int y, unsigned int width, @@ -657,9 +652,7 @@ CreateCGImageFromDrawableRect( MacDrawable *mac_drawable = (MacDrawable *)drawable; CGContextRef cg_context = NULL; CGImageRef cg_image = NULL, result = NULL; -#if TK_MAC_SYNCHRONOUS_DRAWING CGFloat scaleFactor = 1.0; -#endif if (mac_drawable->flags & TK_IS_PIXMAP) { cg_context = TkMacOSXGetCGContextForDrawable(drawable); CGContextRetain(cg_context); @@ -669,26 +662,9 @@ CreateCGImageFromDrawableRect( TkMacOSXDbgMsg("Invalid source drawable"); return NULL; } -#if TK_MAC_SYNCHRONOUS_DRAWING scaleFactor = view.layer.contentsScale; -#endif -#if TK_MAC_CGIMAGE_DRAWING cg_context = ((TKContentView *)view).tkLayerBitmapContext; CGContextRetain(cg_context); -#else - NSSize size = view.frame.size; - NSUInteger view_width = size.width, view_height = size.height; - NSUInteger bytesPerPixel = 4, - bytesPerRow = bytesPerPixel * view_width, - bitsPerComponent = 8; - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - cg_context = CGBitmapContextCreate(NULL, view_width, view_height, - bitsPerComponent, bytesPerRow, colorSpace, - kCGImageAlphaPremultipliedLast | - kCGBitmapByteOrder32Big); - CFRelease(colorSpace); - [view.layer renderInContext:cg_context]; -#endif } if (cg_context) { cg_image = CGBitmapContextCreateImage(cg_context); @@ -697,12 +673,9 @@ CreateCGImageFromDrawableRect( if (cg_image) { CGRect rect = CGRectMake(x + mac_drawable->xOff, y + mac_drawable->yOff, width, height); -#if TK_MAC_SYNCHRONOUS_DRAWING rect = CGRectApplyAffineTransform(rect, CGAffineTransformMakeScale(scaleFactor, scaleFactor)); -#endif result = CGImageCreateWithImageInRect(cg_image, rect); CGImageRelease(cg_image); -#if TK_MAC_SYNCHRONOUS_DRAWING if (force_1x_scale && (scaleFactor != 1.0)) { // See https://web.archive.org/web/20200219030756/http://blog.foundry376.com/2008/07/scaling-a-cgimage/#comment-200 // create context, keeping original image properties @@ -724,7 +697,6 @@ CreateCGImageFromDrawableRect( } CGImageRelease(cg_image); } -#endif } return result; } diff --git a/macosx/tkMacOSXInt.h b/macosx/tkMacOSXInt.h index 1a39d69090..29b20f71e9 100644 --- a/macosx/tkMacOSXInt.h +++ b/macosx/tkMacOSXInt.h @@ -155,21 +155,6 @@ typedef struct TkWindowPrivate MacDrawable; #define TK_LAYOUT_WITH_BASE_CHUNKS 1 #define TK_DRAW_IN_CONTEXT 1 -/* -* Experimental CGBitmapContext → CGImage → layer-backed NSView drawing -* (FIXME: should this go somewhere else?) -*/ -#define TK_MAC_CGIMAGE_DRAWING 1 - -/* - * Using an approach to allow Tk drawing routines to draw "synchronously" - * (rather than only when in drawRect:--although actually displaying results - * of drawing may still be done asynchronously) - */ -#if TK_MAC_CGIMAGE_DRAWING -#define TK_MAC_SYNCHRONOUS_DRAWING 1 -#endif - /* * Prototypes of internal procs not in the stubs table. */ diff --git a/macosx/tkMacOSXNotify.c b/macosx/tkMacOSXNotify.c index 9d01450092..b0410027e4 100644 --- a/macosx/tkMacOSXNotify.c +++ b/macosx/tkMacOSXNotify.c @@ -352,11 +352,6 @@ TkMacOSXDrawAllViews( if (dirtyCount) { continue; } -#if TK_MAC_CGIMAGE_DRAWING - // Layer-backed view: let NSView schedule updates -#else - //[[view layer] setNeedsDisplayInRect:[view tkDirtyRect]]; -#endif [view setNeedsDisplay:YES]; } } else { @@ -370,26 +365,6 @@ TkMacOSXDrawAllViews( untilDate:[NSDate distantPast] inMode:GetRunLoopMode(TkMacOSXGetModalSession()) dequeue:NO]; - for (NSWindow *window in [NSApp windows]) { - if ([[window contentView] isMemberOfClass:[TKContentView class]]) { - TKContentView *view = [window contentView]; - - /* - * If we did not run drawRect, we set needsDisplay back to NO. - * Note that if drawRect did run it may have added to Tk's dirty - * rect, due to attempts to draw outside of drawRect's dirty rect. - */ - - if ([view needsDisplay]) { -#if TK_MAC_CGIMAGE_DRAWING - // Should no longer ever need to setNeedsDisplay:NO - if (0) fprintf(stderr, "nD still set %p\n", view); -#else - [view setNeedsDisplay: NO]; -#endif - } - } - } } /* diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h index 8851f67726..94b8b3bac2 100644 --- a/macosx/tkMacOSXPrivate.h +++ b/macosx/tkMacOSXPrivate.h @@ -407,9 +407,7 @@ VISIBILITY_HIDDEN } @property Bool tkNeedsDisplay; @property NSRect tkDirtyRect; -#if TK_MAC_CGIMAGE_DRAWING @property CGContextRef tkLayerBitmapContext; -#endif @end @interface TKContentView(TKKeyEvent) @@ -422,9 +420,7 @@ VISIBILITY_HIDDEN - (void) clearTkDirtyRect; - (void) generateExposeEvents: (NSRect) rect; - (void) tkToolbarButton: (id) sender; -#if TK_MAC_CGIMAGE_DRAWING - (void) resetTkLayerBitmapContext; -#endif @end @interface NSWindow(TKWm) diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index 3c9ebc8527..1e9933ed8b 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -181,8 +181,6 @@ XMapWindow( TkMacOSXApplyWindowAttributes(winPtr, win); [win setExcludedFromWindowsMenu:NO]; [NSApp activateIgnoringOtherApps:initialized]; - // Not sure this does anything useful for TK_MAC_SYNCHRONOUS_DRAWING - // [view addTkDirtyRect: [view bounds]]; if (initialized) { if ([win canBecomeKeyWindow]) { [win makeKeyAndOrderFront:NSApp]; @@ -228,20 +226,15 @@ XMapWindow( */ TKContentView *view = [win contentView]; -#if TK_MAC_SYNCHRONOUS_DRAWING + /* * Do not rely on addTkDirtyRect: to generate Expose events * (though I’m not sure if this is the place to generate events; * or if using generateExposeEvents: is the best way; * what does XMapWindow() do on other platforms?) */ - // Probably only needs to use the widget bounds. + // Possibly this only needs to use the widget bounds. [view generateExposeEvents:[view bounds]]; -#else - // if (view != [NSView focusView]) { - // [view addTkDirtyRect:[view bounds]]; - // } -#endif /* * Generate VisibilityNotify events for window and all mapped children. @@ -378,18 +371,6 @@ XUnmapWindow( TkMacOSXInvalClipRgns((Tk_Window)parentPtr); TkMacOSXUpdateClipRgn(parentPtr); } -#if TK_MAC_SYNCHRONOUS_DRAWING - /* - * Anything need to be done here instead? - * (Not yet aware of reasoning behind [78a3bdc4454f] - * i.e. why the existing approach uses addTkDirtyRect: here) - */ -#else - // TKContentView *view = [win contentView]; - // if (view != [NSView focusView]) { - // [view addTkDirtyRect:[view bounds]]; - // } -#endif return Success; } @@ -1044,12 +1025,8 @@ InvalViewRect( break; case kHIShapeEnumerateRect: dirtyRect = NSRectFromCGRect(CGRectApplyAffineTransform(*rect, t)); -#if TK_MAC_SYNCHRONOUS_DRAWING // Cannot rely on addTkDirtyRect: to force redrawing. [view generateExposeEvents:dirtyRect]; -#else - // [view addTkDirtyRect:dirtyRect]; -#endif break; } return noErr; diff --git a/macosx/tkMacOSXTest.c b/macosx/tkMacOSXTest.c index 0293f77c80..f6a1f0c59d 100644 --- a/macosx/tkMacOSXTest.c +++ b/macosx/tkMacOSXTest.c @@ -151,22 +151,8 @@ MODULE_SCOPE Bool TkTestLogDisplay( Drawable drawable) { - MacDrawable *macWin = (MacDrawable *)drawable; - if (TK_MAC_SYNCHRONOUS_DRAWING) return True; // No checking focusView. - NSWindow *win = nil; - if (macWin->toplevel && macWin->toplevel->winPtr && - macWin->toplevel->winPtr->wmInfoPtr && - macWin->toplevel->winPtr->wmInfoPtr->window) { - win = macWin->toplevel->winPtr->wmInfoPtr->window; - } else if (macWin->winPtr && macWin->winPtr->wmInfoPtr && - macWin->winPtr->wmInfoPtr->window) { - win = macWin->winPtr->wmInfoPtr->window; - } - if (win) { - return ([win contentView] == [NSView focusView]); - } else { - return True; - } + (void) drawable; + return True; } /* diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 3906223bad..a170187f7e 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -957,30 +957,10 @@ ConfigureRestrictProc( { self = [super initWithFrame:frame]; if (self) { -#if TK_MAC_CGIMAGE_DRAWING - // Want layer-backed view, not layer-hosting -#else - /* - * The layer must exist before we set wantsLayer to YES. - */ - - self.layer = [CALayer layer]; -#endif self.wantsLayer = YES; self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay; self.layer.contentsGravity = self.layer.contentsAreFlipped ? kCAGravityTopLeft : kCAGravityBottomLeft; - -#if TK_MAC_CGIMAGE_DRAWING - // Want layer-backed view, not layer-hosting -#else - /* - * Nothing gets drawn at all if the layer does not have a delegate. - * Currently, we do not implement any methods of the delegate, however. - */ - - self.layer.delegate = (id) self; -#endif trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:(NSTrackingMouseEnteredAndExited | @@ -995,7 +975,6 @@ ConfigureRestrictProc( return self; } -#if TK_MAC_CGIMAGE_DRAWING - (BOOL) wantsUpdateLayer { return YES; @@ -1017,16 +996,6 @@ ConfigureRestrictProc( [self clearTkDirtyRect]; } } -#else -/* - * We will just use drawRect. - */ - -- (BOOL) wantsUpdateLayer -{ - return NO; -} -#endif #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 - (void) viewDidChangeBackingProperties @@ -1040,11 +1009,9 @@ ConfigureRestrictProc( */ self.layer.contentsScale = self.window.screen.backingScaleFactor; -#if TK_MAC_CGIMAGE_DRAWING [self resetTkLayerBitmapContext]; // need to redraw [self generateExposeEvents: [self bounds]]; -#endif } #endif @@ -1063,9 +1030,7 @@ ConfigureRestrictProc( -(void) setFrameSize: (NSSize)newsize { [super setFrameSize: newsize]; -#if TK_MAC_CGIMAGE_DRAWING [self resetTkLayerBitmapContext]; -#endif NSWindow *w = [self window]; TkWindow *winPtr = TkMacOSXGetTkWindow(w); Tk_Window tkwin = (Tk_Window)winPtr; @@ -1338,7 +1303,6 @@ static const char *const accentNames[] = { return [super validRequestorForSendType:sendType returnType:returnType]; } -#if TK_MAC_CGIMAGE_DRAWING -(void) resetTkLayerBitmapContext { static CGColorSpaceRef colorspace = NULL; if (colorspace == NULL) { @@ -1363,7 +1327,6 @@ static const char *const accentNames[] = { CGContextRelease(self.tkLayerBitmapContext); // will also need this in a destructor somewhere self.tkLayerBitmapContext = newCtx; } -#endif @end From 568e96ae33e853e356c161197e221532e0d9a4f6 Mon Sep 17 00:00:00 2001 From: culler Date: Wed, 5 Jun 2024 19:24:59 +0000 Subject: [PATCH 57/96] Fix crashes and slow resizing by preventing recursive calls to generateExposeEvents. --- macosx/tkMacOSXWindowEvent.c | 39 ++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index a170187f7e..082f812b9f 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -984,11 +984,12 @@ ConfigureRestrictProc( if (ctx) { /* - * Make a copy, probably using copy-on-write, of the CGImage - * currently being used for drawing, and assign the copy as the - * contents layer of the NSView. This will cause all drawing - * done since the last call to this function to now become - * visible. + * 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(ctx); self.layer.contents = (__bridge id) newImg; @@ -1114,10 +1115,25 @@ ConfigureRestrictProc( TkWindow *winPtr = TkMacOSXGetTkWindow([self window]); void *oldArg; Tk_RestrictProc *oldProc; + static int reentered = 0; + if (!winPtr) { return; } + if (reentered) { + /* + * When in liveResize an event loop gets run below to + * immediately process displayProcs while the resize is being + * done. Those can cause calls to this function, leading to + * crashes or very poor performance. The reentered flag is + * used to detect this. + */ + //fprintf(stderr, "Recursive call to generateExposeEvents\n"); + return; + } + reentered = 1; + /* * Generate Tk Expose events. All of these events will share the same * serial number. @@ -1133,20 +1149,13 @@ ConfigureRestrictProc( if ([self inLiveResize] && updatesNeeded) { /* - * Starting with OSX 10.14, which uses Core Animation to draw windows, - * all drawing must be done within the drawRect method. (The CGContext - * which draws to the backing CALayer is created by the NSView before - * calling drawRect, and destroyed when drawRect returns. Drawing done - * with the current CGContext outside of the drawRect method has no - * effect.) - * - * Fortunately, Tk schedules all drawing to be done while Tcl is idle. - * So to run any display procs which were scheduled by the expose - * events we process all idle events before returning. + * During a LiveResize we process all idle tasks generated by the + * expose events to redraw the window while it is being resized. */ while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} } + reentered = 0; } /* From 5493df5dff50798c087c108676ca372ccc5c86ee Mon Sep 17 00:00:00 2001 From: fvogel Date: Sun, 9 Jun 2024 19:59:50 +0000 Subject: [PATCH 58/96] Comments style. --- macosx/tkMacOSXWm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index 09a77103e9..228ee32bbd 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -1026,7 +1026,8 @@ TkWmDeadWindow( root_y = floor(TkMacOSXZeroScreenHeight() - mouse.y); int win_x, win_y; Tk_Window target = Tk_TopCoordsToWindow((Tk_Window) winPtr2, top_x, top_y, &win_x, &win_y); - /* A non-toplevel window can have a NULL parent while it is in the process of + /* + * A non-toplevel window can have a NULL parent while it is in the process of * being destroyed. We should not call Tk_UpdatePointer in that case. */ if (Tk_Parent(target) != NULL || Tk_IsTopLevel(target)) { From fe184a24838c9d2e19d239898218a2ef81f516d8 Mon Sep 17 00:00:00 2001 From: fvogel Date: Sun, 9 Jun 2024 20:00:38 +0000 Subject: [PATCH 59/96] Remove -verbose option from macOS workflow file. --- .github/workflows/mac-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mac-build.yml b/.github/workflows/mac-build.yml index 59b93d3872..3d8178e30f 100644 --- a/.github/workflows/mac-build.yml +++ b/.github/workflows/mac-build.yml @@ -49,7 +49,7 @@ jobs: } - name: Run Tests run: | - make TESTFLAGS="-verbose bpest" test | tee out.txt + make | tee out.txt nmatches=$( grep -c "Failed 0" out.txt ) if [ $nmatches -lt 4 ] then From c9218c7a9dd08f1c281c1ba82cb09cc2d7a9e3f7 Mon Sep 17 00:00:00 2001 From: fvogel Date: Sun, 9 Jun 2024 20:01:04 +0000 Subject: [PATCH 60/96] Typo in previous commit. --- .github/workflows/mac-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mac-build.yml b/.github/workflows/mac-build.yml index 3d8178e30f..7485ac7c36 100644 --- a/.github/workflows/mac-build.yml +++ b/.github/workflows/mac-build.yml @@ -49,7 +49,7 @@ jobs: } - name: Run Tests run: | - make | tee out.txt + make test | tee out.txt nmatches=$( grep -c "Failed 0" out.txt ) if [ $nmatches -lt 4 ] then From bbbd008273a2c030840742cac6b09d596d66ee59 Mon Sep 17 00:00:00 2001 From: fvogel Date: Sun, 9 Jun 2024 20:08:52 +0000 Subject: [PATCH 61/96] Rename processevents --> testprocessevents to conform to the widely used convention that commands specific to the test suite start with 'test'. --- generic/tkTest.c | 14 +++++++------- tests/event.test | 16 ++++++++-------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/generic/tkTest.c b/generic/tkTest.c index bd6dde3e3f..85dd664e7c 100644 --- a/generic/tkTest.c +++ b/generic/tkTest.c @@ -147,7 +147,7 @@ typedef struct TrivialCommandHeader { static int ImageObjCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj * const objv[]); -static int ProcessEventsObjCmd(ClientData dummy, +static int TestProcessEventsObjCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj * const objv[]); static int TestbitmapObjCmd(ClientData dummy, @@ -250,7 +250,7 @@ Tktest_Init( return TCL_ERROR; } - Tcl_CreateObjCommand(interp, "processevents", ProcessEventsObjCmd, NULL, NULL); + Tcl_CreateObjCommand(interp, "testprocessevents", TestProcessEventsObjCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "square", SquareObjCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "testbitmap", TestbitmapObjCmd, (ClientData) Tk_MainWindow(interp), NULL); @@ -1686,12 +1686,12 @@ ImageDelete( /* *---------------------------------------------------------------------- * - * ProcessEventsObjCmd -- + * TestProcessEventsObjCmd -- * - * This function implements the "processevents" command which processes + * This function implements the "testprocessevents" command which processes * all queued events of a type specified by one of the arguments to the - * command. Currently the supported arguments are leave, enter, and - * motion. Others could be added if needed. + * command. Currently the supported arguments are leave, enter, motion, + * and expose. Others could be added if needed. * * Results: * A standard Tcl result. @@ -1716,7 +1716,7 @@ CrossingRestrictProc( return TK_DEFER_EVENT; } -static int ProcessEventsObjCmd( +static int TestProcessEventsObjCmd( ClientData dummy, Tcl_Interp *interp, int objc, diff --git a/tests/event.test b/tests/event.test index c37fc67880..b1c7931db2 100644 --- a/tests/event.test +++ b/tests/event.test @@ -937,7 +937,7 @@ test event-9.11 {pointer window container = parent} -setup { bind all {append result " %d %W|"} destroy .one.f1.f2 if {[tk windowingsystem] ne "x11"} { - processevents enter leave + testprocessevents enter leave } else { update } @@ -965,7 +965,7 @@ test event-9.12 {pointer window container != parent} -setup { bind all {append result " %d %W|"} destroy .one.g if {[tk windowingsystem] ne "x11"} { - processevents enter leave + testprocessevents enter leave } else { update } @@ -992,7 +992,7 @@ test event-9.13 {pointer window is a toplevel, toplevel destination} -setup { destroy .two waitForWindowEvent .one if {[tk windowingsystem] ne "x11"} { - processevents enter leave + testprocessevents enter leave } else { update } @@ -1020,7 +1020,7 @@ test event-9.14 {pointer window is a toplevel, tk internal destination} -setup { bind all {append result " %d %W|"} destroy .two waitForWindowEvent .one.f1.f2 - processevents enter leave + testprocessevents enter leave set result } -cleanup { bind all {} @@ -1068,7 +1068,7 @@ test event-9.16 {Successive destructions (pointer window + parent), single gener bind all {append result " %d %W|"} destroy .one.f1 if {[tk windowingsystem] ne "x11"} { - processevents enter leave + testprocessevents enter leave } else { update } @@ -1098,7 +1098,7 @@ test event-9.17 {Successive destructions (pointer window + parent), separate cro update; # make sure window is gone destroy .one.f1 update; # make sure window is gone - if {[tk windowingsystem] ne "x11"} {processevents enter leave} + if {[tk windowingsystem] ne "x11"} {testprocessevents enter leave} set result } -cleanup { bind all {} @@ -1121,7 +1121,7 @@ test event-9.18 {Successive destructions (pointer window + ancestors including i bind all {append result " %d %W|"} destroy .two waitForWindowEvent .one - if {[tk windowingsystem] ne "x11"} {processevents enter leave} + if {[tk windowingsystem] ne "x11"} {testprocessevents enter leave} set result } -cleanup { bind all {} @@ -1152,7 +1152,7 @@ test event-9.19 {Successive destructions (pointer window + ancestors including i destroy .three waitForWindowEvent .two.f1.f2 update idletasks; #finish destroying .two - if {[tk windowingsystem] ne "x11"} {processevents enter leave} + if {[tk windowingsystem] ne "x11"} {testprocessevents enter leave} set result } -cleanup { bind all {} From f05a10a3aca5c7affc50789a2a9a2cae5bb31244 Mon Sep 17 00:00:00 2001 From: fvogel Date: Sun, 9 Jun 2024 20:21:07 +0000 Subject: [PATCH 62/96] Document parameters of TestProcessEventsObjCmd. --- generic/tkTest.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/generic/tkTest.c b/generic/tkTest.c index 85dd664e7c..dbf572e84c 100644 --- a/generic/tkTest.c +++ b/generic/tkTest.c @@ -1717,10 +1717,10 @@ CrossingRestrictProc( } static int TestProcessEventsObjCmd( - ClientData dummy, - Tcl_Interp *interp, - int objc, - Tcl_Obj * const objv[]) + TCL_UNUSED(ClientData), /* Main window for application. */ + Tcl_Interp* interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj* const objv[]) /* Argument objects. */ { ClientData oldArg; Tk_RestrictProc *oldProc; From d0b47a6ee79bdf4f9b8e40bad1c35c1b95266734 Mon Sep 17 00:00:00 2001 From: fvogel Date: Sun, 9 Jun 2024 20:25:29 +0000 Subject: [PATCH 63/96] TCL_UNUSED() parameter in SendEnterLeaveForDestroy() on Linux. --- generic/tkWindow.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/generic/tkWindow.c b/generic/tkWindow.c index 85b5a0bd30..589a930ab8 100644 --- a/generic/tkWindow.c +++ b/generic/tkWindow.c @@ -1334,10 +1334,10 @@ Tk_CreateWindowFromPath( *-------------------------------------------------------------- */ +#if defined(MAC_OSX_TK) || defined(_WIN32) static void SendEnterLeaveForDestroy( Tk_Window tkwin) { -#if defined(MAC_OSX_TK) || defined(_WIN32) int x, y; unsigned int state; Tk_Window pointerWin; @@ -1359,8 +1359,13 @@ static void SendEnterLeaveForDestroy( if (pointerWin && (tkwin == Tk_Parent(pointerWin))) { Tk_UpdatePointer(Tk_Parent(tkwin), x, y, state); } -#endif } +#else +static void SendEnterLeaveForDestroy( + TCL_UNUSED(Tk_Window) +{ +} +#endif void Tk_DestroyWindow( From 3f0a126393198d3f6e0dff58e479ee9cab5b213d Mon Sep 17 00:00:00 2001 From: fvogel Date: Sun, 9 Jun 2024 20:29:38 +0000 Subject: [PATCH 64/96] Add parenthesis that was missing from previous commit. --- generic/tkWindow.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generic/tkWindow.c b/generic/tkWindow.c index 589a930ab8..fa3885d14d 100644 --- a/generic/tkWindow.c +++ b/generic/tkWindow.c @@ -1362,7 +1362,7 @@ static void SendEnterLeaveForDestroy( } #else static void SendEnterLeaveForDestroy( - TCL_UNUSED(Tk_Window) + TCL_UNUSED(Tk_Window)) { } #endif From db062abb6620c7df36d50b0a5d5cb7a14b924a2c Mon Sep 17 00:00:00 2001 From: culler Date: Sun, 9 Jun 2024 21:30:28 +0000 Subject: [PATCH 65/96] Deal with artifacts when resizing as well as updates after a window is destroyed. --- macosx/tkMacOSXDraw.c | 6 ++-- macosx/tkMacOSXSubwindows.c | 57 ++++++++++++++++++++++++------------ macosx/tkMacOSXWindowEvent.c | 20 +++++++++---- macosx/tkMacOSXWm.c | 2 ++ 4 files changed, 58 insertions(+), 27 deletions(-) diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index 881a635592..3435ebeb1f 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -1207,14 +1207,14 @@ TkMacOSXSetupDrawingContext( * Intersect the drawable's clipping region with the region stored in the * X GC. If the resulting region is empty, don't do any drawing. */ - +//#if 0 // disable clipping (almost works, but windows can open up blank) dc.clipRgn = TkMacOSXGetClipRgn(d); ClipToGC(d, gc, &dc.clipRgn); if (dc.clipRgn && HIShapeIsEmpty(dc.clipRgn)) { canDraw = false; goto end; } - +//#endif //disable clipping /* * If the drawable already has a CGContext, use it. Otherwise, we must be * drawing to a window and we use the current context of its ContentView. @@ -1311,6 +1311,7 @@ TkMacOSXSetupDrawingContext( }; CGContextConcatCTM(dc.context, t); } +//#if 0 // disable clipping if (dc.clipRgn) { #ifdef TK_MAC_DEBUG_DRAWING @@ -1356,6 +1357,7 @@ TkMacOSXSetupDrawingContext( CGContextClipToRect(dc.context, r); } } +//#endif //disable clipping if (gc) { static const CGLineCap cgCap[] = { diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index 1e9933ed8b..229253b443 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -56,6 +56,9 @@ XDestroyWindow( Window window) /* Window. */ { MacDrawable *macWin = (MacDrawable *)window; + // fprintf(stderr, "XDestroyWindow: %s with parent %s\n", + // Tk_PathName(macWin->winPtr), + // Tk_PathName(macWin->winPtr->parentPtr)); /* * Remove any dangling pointers that may exist if the window we are @@ -67,9 +70,8 @@ XDestroyWindow( macWin->toplevel->referenceCount--; if (!Tk_IsTopLevel(macWin->winPtr)) { - TkMacOSXInvalidateWindow(macWin, TK_PARENT_WINDOW); if (macWin->winPtr->parentPtr != NULL) { - TkMacOSXInvalClipRgns((Tk_Window)macWin->winPtr->parentPtr); + TkMacOSXInvalidateWindow(macWin, TK_PARENT_WINDOW); } if (macWin->visRgn) { CFRelease(macWin->visRgn); @@ -206,6 +208,8 @@ XMapWindow( * the window. */ + // We could add a TK_CONTAINER_WINDOW flag and have + // TkMacOSXInvalidateWindow invalidate the clip regions. TkMacOSXInvalClipRgns(contWinPtr); TkMacOSXInvalidateWindow(macWin, TK_PARENT_WINDOW); } @@ -358,18 +362,19 @@ XUnmapWindow( } else { /* - * Rebuild the visRgn clip region for the parent so it will be allowed + * Rebuild the clip regions for the parent so it will be allowed * to draw in the space from which this subwindow was removed and then * redraw the window. */ - if (parentPtr && parentPtr->privatePtr->visRgn) { - TkMacOSXInvalidateViewRegion( - TkMacOSXGetNSViewForDrawable(parentPtr->window), - parentPtr->privatePtr->visRgn); - } - TkMacOSXInvalClipRgns((Tk_Window)parentPtr); - TkMacOSXUpdateClipRgn(parentPtr); + TkMacOSXInvalidateWindow(macWin, TK_PARENT_WINDOW); + // if (parentPtr && parentPtr->privatePtr->visRgn) { + // TkMacOSXInvalidateViewRegion( + // TkMacOSXGetNSViewForDrawable(parentPtr->window), + // parentPtr->privatePtr->visRgn); + // } + //TkMacOSXInvalClipRgns((Tk_Window)parentPtr); + //TkMacOSXUpdateClipRgn(parentPtr); } return Success; } @@ -739,8 +744,9 @@ XConfigureWindow( NSView *view = TkMacOSXGetNSViewForDrawable(macWin); if (view) { - TkMacOSXInvalClipRgns((Tk_Window)winPtr->parentPtr); - TkpRedrawWidget((Tk_Window)winPtr); + TkMacOSXInvalidateWindow(macWin, TK_PARENT_WINDOW); + //TkMacOSXInvalClipRgns((Tk_Window)winPtr->parentPtr); + //TkpRedrawWidget((Tk_Window)winPtr); } } @@ -988,6 +994,11 @@ TkMacOSXVisableClipRgn( return (Region) HIShapeCreateMutableCopy(winPtr->privatePtr->visRgn); } +#if 0 +//This code is not currently used. But it shows how to iterate over the +//rectangles in a region described by an HIShape. Probably we want to +//replace the current dirtyRect by such a region. + /* *---------------------------------------------------------------------- * @@ -1026,9 +1037,12 @@ InvalViewRect( case kHIShapeEnumerateRect: dirtyRect = NSRectFromCGRect(CGRectApplyAffineTransform(*rect, t)); // Cannot rely on addTkDirtyRect: to force redrawing. - [view generateExposeEvents:dirtyRect]; + //MC This is the only place where the rect is not the view bounds. + //And it kills liveResize. + //[view generateExposeEvents:dirtyRect]; break; } + [view generateExposeEvents:[view bounds]]; return noErr; } @@ -1043,19 +1057,22 @@ TkMacOSXInvalidateViewRegion( InvalViewRect, view); } } +#endif /* *---------------------------------------------------------------------- * * TkMacOSXInvalidateWindow -- * - * This function invalidates a window and (optionally) its children. + * This stub function redraws the part of the toplevel window + * covered by a given Tk window. (Except currently it redraws + * the entire toplevel.) * * Results: * None. * * Side effects: - * Damage is created. + * The window is redrawn. * *---------------------------------------------------------------------- */ @@ -1069,11 +1086,13 @@ TkMacOSXInvalidateWindow( #ifdef TK_MAC_DEBUG_CLIP_REGIONS TkMacOSXDbgMsg("%s", macWin->winPtr->pathName); #endif - if (macWin->flags & TK_CLIP_INVALID) { - TkMacOSXUpdateClipRgn(macWin->winPtr); + TKContentView *view = (TKContentView *)TkMacOSXGetNSViewForDrawable(macWin); + TkMacOSXInvalClipRgns(macWin->winPtr); + if (flag == TK_PARENT_WINDOW){ + TkMacOSXInvalClipRgns(macWin->winPtr->parentPtr); } - TkMacOSXInvalidateViewRegion(TkMacOSXGetNSViewForDrawable(macWin), - (flag == TK_WINDOW_ONLY) ? macWin->visRgn : macWin->aboveVisRgn); + // Here we should probably be using the damage region. + [view generateExposeEvents:[view bounds]]; } /* diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 082f812b9f..bd5e2e6431 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -1061,7 +1061,7 @@ ConfigureRestrictProc( * Disable Tk drawing until the window has been completely configured. */ - TkMacOSXSetDrawingEnabled(winPtr, 0); + //TkMacOSXSetDrawingEnabled(winPtr, 0); /* * Generate and handle a ConfigureNotify event for the new size. @@ -1072,18 +1072,25 @@ ConfigureRestrictProc( oldProc = Tk_RestrictEvents(ConfigureRestrictProc, NULL, &oldArg); Tk_RestrictEvents(oldProc, oldArg, &oldArg); + /* + * To make the reconfiguration actually happen we need to process + * idle tasks generated when processing the ConfigureNotify events. + */ + + while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} + /* * Now that Tk has configured all subwindows, create the clip regions. */ TkMacOSXInvalClipRgns(tkwin); TkMacOSXUpdateClipRgn(winPtr); - TkMacOSXSetDrawingEnabled(winPtr, 1); + //TkMacOSXSetDrawingEnabled(winPtr, 1); /* * Redraw the entire content view. */ - + if ([self inLiveResize]) { [self viewDidChangeBackingProperties]; [self setNeedsDisplay:YES]; @@ -1098,6 +1105,7 @@ ConfigureRestrictProc( */ [NSApp _unlockAutoreleasePool]; + } } @@ -1116,7 +1124,7 @@ ConfigureRestrictProc( void *oldArg; Tk_RestrictProc *oldProc; static int reentered = 0; - + if (!winPtr) { return; } @@ -1129,11 +1137,11 @@ ConfigureRestrictProc( * crashes or very poor performance. The reentered flag is * used to detect this. */ - //fprintf(stderr, "Recursive call to generateExposeEvents\n"); + // fprintf(stderr, "Recursive call to generateExposeEvents\n"); return; } reentered = 1; - + /* * Generate Tk Expose events. All of these events will share the same * serial number. diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index 3311ded3db..4d5ea7e9e0 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -291,6 +291,8 @@ static void syncLayout(NSWindow *macWindow) contentRect.size.width; wmPtr->parentHeight = winPtr->changes.height + frameRect.size.height - contentRect.size.height; + TkMacOSXInvalClipRgns((Tk_Window)winPtr); + TkMacOSXUpdateClipRgn(winPtr); } } #endif From 06a4ba5407ebd451d8ab5d56bb06fad34f24766f Mon Sep 17 00:00:00 2001 From: culler Date: Sun, 9 Jun 2024 22:00:24 +0000 Subject: [PATCH 66/96] Fix crash in test suite. --- macosx/tkMacOSXSubwindows.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index 229253b443..0c4e4e4267 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -317,7 +317,6 @@ XUnmapWindow( { MacDrawable *macWin = (MacDrawable *)window; TkWindow *winPtr = macWin->winPtr; - TkWindow *parentPtr = winPtr->parentPtr; NSWindow *win = TkMacOSXGetNSWindowForDrawable(window); if (!window) { @@ -1087,9 +1086,12 @@ TkMacOSXInvalidateWindow( TkMacOSXDbgMsg("%s", macWin->winPtr->pathName); #endif TKContentView *view = (TKContentView *)TkMacOSXGetNSViewForDrawable(macWin); - TkMacOSXInvalClipRgns(macWin->winPtr); - if (flag == TK_PARENT_WINDOW){ - TkMacOSXInvalClipRgns(macWin->winPtr->parentPtr); + TkWindow *winPtr = macWin->winPtr; + Tk_Window tkwin = (Tk_Window) winPtr; + Tk_Window parent = (Tk_Window) winPtr->parentPtr; + TkMacOSXInvalClipRgns(tkwin); + if ((flag == TK_PARENT_WINDOW) && parent){ + TkMacOSXInvalClipRgns(parent); } // Here we should probably be using the damage region. [view generateExposeEvents:[view bounds]]; From c2f33a40bb917cd5394b6e5ea77d14564e7003c8 Mon Sep 17 00:00:00 2001 From: culler Date: Tue, 11 Jun 2024 15:20:31 +0000 Subject: [PATCH 67/96] Deal with some testing issues. --- macosx/tkMacOSXSubwindows.c | 9 ++-- macosx/tkMacOSXWindowEvent.c | 6 ++- macosx/tkMacOSXWm.c | 85 ++++++++++++++++++++++-------------- tests/focus.test | 6 +-- tests/image.test | 4 -- tests/textDisp.test | 1 + tests/unixWm.test | 8 +--- 7 files changed, 68 insertions(+), 51 deletions(-) diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index 2ba8568b64..8cfdaf2f0c 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -262,7 +262,8 @@ XMapWindow( * * NotifyVisibility -- * - * Recursively called helper proc for XMapWindow(). + * Helper for XMapWindow(). Generates VisibilityNotify events + * for the window and all of its descendants. * * Results: * None. @@ -1123,17 +1124,19 @@ Tk_MacOSXGetNSWindowForDrawable( if (!macWin || macWin->flags & TK_IS_PIXMAP) { result = nil; + } else if (macWin->toplevel && macWin->toplevel->winPtr && macWin->toplevel->winPtr->wmInfoPtr && macWin->toplevel->winPtr->wmInfoPtr->window) { result = macWin->toplevel->winPtr->wmInfoPtr->window; + } else if (macWin->winPtr && macWin->winPtr->wmInfoPtr && macWin->winPtr->wmInfoPtr->window) { result = macWin->winPtr->wmInfoPtr->window; + } else if (macWin->toplevel && (macWin->toplevel->flags & TK_EMBEDDED)) { TkWindow *contWinPtr = (TkWindow *)Tk_GetOtherWindow((Tk_Window)macWin->toplevel->winPtr); - - if (contWinPtr) { + if (contWinPtr && contWinPtr->privatePtr) { result = TkMacOSXGetNSWindowForDrawable((Drawable)contWinPtr->privatePtr); } } diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index bd5ea2d93d..6448732475 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -932,6 +932,7 @@ Tk_MacOSXIsAppInFront(void) * Restrict event processing to Expose events. */ +#if 0 static Tk_RestrictAction ExposeRestrictProc( void *arg, @@ -940,6 +941,7 @@ ExposeRestrictProc( return (eventPtr->type==Expose && eventPtr->xany.serial==PTR2UINT(arg) ? TK_PROCESS_EVENT : TK_DEFER_EVENT); } +#endif /* * Restrict event processing to ConfigureNotify events. @@ -1123,8 +1125,8 @@ ConfigureRestrictProc( int updatesNeeded; CGRect updateBounds; TkWindow *winPtr = TkMacOSXGetTkWindow([self window]); - void *oldArg; - Tk_RestrictProc *oldProc; + //void *oldArg; + //Tk_RestrictProc *oldProc; static int reentered = 0; if (!winPtr) { diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index c01d5060b5..c1c7f926e9 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -1123,10 +1123,11 @@ TkWmDeadWindow( TkWindow *winPtr2; NSWindow *w; WmInfo *wmPtr = winPtr->wmInfoPtr, *wmPtr2; - TKWindow *deadNSWindow = (TKWindow *)TkMacOSXGetNSWindowForDrawable( - Tk_WindowId(winPtr)); - if (deadNSWindow == NULL) { - return; + TKWindow *deadNSWindow = NULL; + if (Tk_WindowId(winPtr) == None) { + fprintf(stderr, "TkWmDeadWindow: no window id\n"); + } else { + deadNSWindow = (TKWindow *)TkMacOSXGetNSWindowForDrawable(Tk_WindowId(winPtr)); } /* *If the dead window is a transient, remove it from the container's list. @@ -2416,8 +2417,7 @@ WmDeiconifyCmd( Tcl_Obj *const objv[]) /* Argument objects. */ { WmInfo *wmPtr = winPtr->wmInfoPtr; - NSWindow *win = TkMacOSXGetNSWindowForDrawable(winPtr->window); - + NSWindow *win = nil; if (objc != 3) { Tcl_WrongNumArgs(interp, 2, objv, "window"); return TCL_ERROR; @@ -2436,11 +2436,17 @@ WmDeiconifyCmd( return TCL_ERROR; } + if (winPtr->window) { + win = TkMacOSXGetNSWindowForDrawable(winPtr->window); + } TkpWmSetState(winPtr, TkMacOSXIsWindowZoomed(winPtr) ? ZoomState : NormalState); - [win setExcludedFromWindowsMenu:NO]; - TkMacOSXApplyWindowAttributes(winPtr, win); - [win orderFront:NSApp]; + if (win) { + [win setExcludedFromWindowsMenu:NO]; + TkMacOSXApplyWindowAttributes(winPtr, win); + [win orderFront:NSApp]; + [[win contentView] setNeedsDisplay:YES]; + } if (wmPtr->icon) { Tk_UnmapWindow((Tk_Window)wmPtr->icon); } @@ -2465,7 +2471,6 @@ WmDeiconifyCmd( } } - [[win contentView] setNeedsDisplay:YES]; Tcl_DoWhenIdle(TkMacOSXDrawAllViews, NULL); return TCL_OK; } @@ -2657,11 +2662,13 @@ WmGeometryCmd( Tcl_Obj *const objv[]) /* Argument objects. */ { WmInfo *wmPtr = winPtr->wmInfoPtr; - NSWindow *win = TkMacOSXGetNSWindowForDrawable(winPtr->window); + NSWindow *win = nil; char xSign = '+', ySign = '+'; int width, height, x = wmPtr->x, y= wmPtr->y; char *argv3; - + if (winPtr && winPtr->window) { + win = TkMacOSXGetNSWindowForDrawable(winPtr->window); + } if ((objc != 3) && (objc != 4)) { Tcl_WrongNumArgs(interp, 2, objv, "window ?newGeometry?"); return TCL_ERROR; @@ -3407,18 +3414,23 @@ WmIconwindowCmd( return TCL_ERROR; } if (wmPtr->icon != NULL) { + NSWindow *win = nil; TkWindow *oldIcon = (TkWindow *)wmPtr->icon; - WmInfo *wmPtr3 = oldIcon->wmInfoPtr; - NSWindow *win = TkMacOSXGetNSWindowForDrawable(oldIcon->window); - + if (winPtr && winPtr->window) { + win = TkMacOSXGetNSWindowForDrawable(winPtr->window); + } /* * The old icon should be withdrawn. */ - - TkpWmSetState(oldIcon, WithdrawnState); + if (oldIcon) { + WmInfo *wmPtr3 = oldIcon->wmInfoPtr; + TkpWmSetState(oldIcon, WithdrawnState); + if (wmPtr3) { + wmPtr3->iconFor = NULL; + } + } [win orderOut:NSApp]; - [win setExcludedFromWindowsMenu:YES]; - wmPtr3->iconFor = NULL; + [win setExcludedFromWindowsMenu:YES]; } Tk_MakeWindowExist(tkwin2); wmPtr->hints.icon_window = Tk_WindowId(tkwin2); @@ -3652,8 +3664,12 @@ WmOverrideredirectCmd( { Bool boolValue; XSetWindowAttributes atts; - TKWindow *win = (TKWindow *)TkMacOSXGetNSWindowForDrawable(winPtr->window); + NSWindow *win = nil; + if (winPtr && winPtr->window) { + win = TkMacOSXGetNSWindowForDrawable(winPtr->window); + } + if ((objc != 3) && (objc != 4)) { Tcl_WrongNumArgs(interp, 2, objv, "window ?boolean?"); return TCL_ERROR; @@ -6098,7 +6114,7 @@ TkMacOSXGetXWindow( * void*. * * Results: - * A Tk_Window, or NULL if the NSWindow is not associated with + * A Tk_Window, or None if the NSWindow is not associated with * any Tk window. * * Side effects: @@ -6114,13 +6130,12 @@ Tk_MacOSXGetTkWindow( Window window = None; if ([(NSWindow *)w respondsToSelector: @selector (tkWindow)]) { window = [(TKWindow *)w tkWindow]; - } - if (window) { TkDisplay *dispPtr = TkGetDisplayList(); - return Tk_IdToWindow(dispPtr->display, window); - } else { - return NULL; + if (window && dispPtr && dispPtr->display) { + return Tk_IdToWindow(dispPtr->display, window); + } } + return NULL; } /* @@ -6145,7 +6160,10 @@ MODULE_SCOPE int TkMacOSXIsWindowZoomed( TkWindow *winPtr) { - NSWindow *macWindow = TkMacOSXGetNSWindowForDrawable(winPtr->window); + NSWindow *macWindow = nil; + if (winPtr && winPtr->window) { + macWindow = TkMacOSXGetNSWindowForDrawable(winPtr->window); + } return [macWindow isZoomed]; } @@ -6874,11 +6892,13 @@ TkMacOSXMakeRealWindowExist( void TkpRedrawWidget(Tk_Window tkwin) { TkWindow *winPtr = (TkWindow *)tkwin; - NSWindow *w; + NSWindow *w = nil; Rect tkBounds; NSRect bounds; - w = TkMacOSXGetNSWindowForDrawable(winPtr->window); + if (winPtr && winPtr->window) { + w = TkMacOSXGetNSWindowForDrawable(winPtr->window); + } if (w) { TKContentView *view = [w contentView]; TkMacOSXWinBounds(winPtr, &tkBounds); @@ -7010,14 +7030,15 @@ TkpWmSetState( * or WithdrawnState. */ { WmInfo *wmPtr = winPtr->wmInfoPtr; - NSWindow *macWin; + NSWindow *macWin = nil; wmPtr->hints.initial_state = state; if (wmPtr->flags & WM_NEVER_MAPPED) { goto setStateEnd; } - - macWin = TkMacOSXGetNSWindowForDrawable(winPtr->window); + if (winPtr && winPtr->window) { + macWin = TkMacOSXGetNSWindowForDrawable(winPtr->window); + } /* * Make sure windows are updated before the state change. As an exception, diff --git a/tests/focus.test b/tests/focus.test index 03563bce8a..17cdd58b5f 100644 --- a/tests/focus.test +++ b/tests/focus.test @@ -43,10 +43,8 @@ proc focusSetupAlt {} { # the X server and possibly also the window manager. proc focusClear {} { - global x; - after 200 {set x 1} - tkwait variable x - dobg {focus -force .; update} + dobg {after 200; focus -force .; update} + after 400 update } diff --git a/tests/image.test b/tests/image.test index c7b6511ff9..cdac20d6da 100644 --- a/tests/image.test +++ b/tests/image.test @@ -370,10 +370,6 @@ test image-9.1 {Tk_ImageChanged procedure} -constraints testImageType -setup { foo changed 5 6 7 8 30 15 update idletasks update - # On MacOS we need to wait for the test image display procedure to run. - while {"timed out" ni $x && [lindex $x end 1] ne "display"} { - vwait x - } after cancel $timer return $x } -cleanup { diff --git a/tests/textDisp.test b/tests/textDisp.test index 7583bc4935..f5ee1ef34e 100644 --- a/tests/textDisp.test +++ b/tests/textDisp.test @@ -1123,6 +1123,7 @@ test textDisp-6.10 {DisplayText, redisplay embedded windows after scroll} {aqua} .t window create end -create { button %W.button_three -text "Button 3"} update + set tk_textEmbWinDisplay {} .t delete 2.0 3.0 update list $tk_textEmbWinDisplay diff --git a/tests/unixWm.test b/tests/unixWm.test index 0a86082a70..b47fb91161 100644 --- a/tests/unixWm.test +++ b/tests/unixWm.test @@ -642,8 +642,6 @@ test unixWm-16.1 {Tk_WmCmd procedure, "deiconify" option} unix { test unixWm-16.2 {Tk_WmCmd procedure, "deiconify" option} unix { destroy .icon toplevel .icon -width 50 -height 50 -bg red - # calling update here prevents a crash in 16.3 on macOS - update wm iconwindow .t .icon set result [list [catch {wm deiconify .icon} msg] $msg] destroy .icon @@ -1355,12 +1353,8 @@ test unixWm-38.2 {Tk_WmCmd procedure, "withdraw" option} unix { test unixWm-38.3 {Tk_WmCmd procedure, "withdraw" option} unix { set result {} wm withdraw .t - #added to avoid a crash on macOS - update lappend result [wm state .t] [winfo ismapped .t] wm deiconify .t - #added to avoid a crash on macOS - update lappend result [wm state .t] [winfo ismapped .t] } {withdrawn 0 normal 1} @@ -2091,11 +2085,13 @@ test unixWm-51.6 {TkWmRestackToplevel procedure, window to be stacked isn't mapp test unixWm-51.7 {TkWmRestackToplevel procedure, other window isn't mapped} {unix failsOnXQuarz} { foreach w {.t .t2 .t3} { destroy $w + update toplevel $w -width 200 -height 200 -bg green tkwait visibility $w wm geometry $w +100+100 update } + update raise .t .t2 restackDelay update From fe6afe81ccdf77fa5a5f070d92766a23b58c5022 Mon Sep 17 00:00:00 2001 From: culler Date: Tue, 11 Jun 2024 16:06:16 +0000 Subject: [PATCH 68/96] Schedule a CI run for this branch. --- .github/workflows/mac-build.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/mac-build.yml b/.github/workflows/mac-build.yml index 2462a1737c..1b89d60103 100644 --- a/.github/workflows/mac-build.yml +++ b/.github/workflows/mac-build.yml @@ -2,18 +2,14 @@ name: macOS on: push: branches: - - "main" - - "core-8-branch" - - "core-8-6-branch" - tags: - - "core-**" + - "cgimage_with_crossing" permissions: contents: read env: ERROR_ON_FAILURES: 1 jobs: xcode: - runs-on: macos-11 + runs-on: macos-14 defaults: run: shell: bash @@ -57,7 +53,7 @@ jobs: fi timeout-minutes: 30 clang: - runs-on: macos-11 + runs-on: macos-14 strategy: matrix: symbols: From 218f3320e0f1e48e5644aa8199479f0bb1360159 Mon Sep 17 00:00:00 2001 From: culler Date: Tue, 11 Jun 2024 16:09:56 +0000 Subject: [PATCH 69/96] Workflows for linux and Windows too. --- .github/workflows/linux-build.yml | 6 +----- .github/workflows/win-build.yml | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml index ee5d2e7df7..6312a912fc 100644 --- a/.github/workflows/linux-build.yml +++ b/.github/workflows/linux-build.yml @@ -2,11 +2,7 @@ name: Linux on: push: branches: - - "main" - - "core-8-branch" - - "core-8-6-branch" - tags: - - "core-**" + - "cgimage_with_crossing" permissions: contents: read defaults: diff --git a/.github/workflows/win-build.yml b/.github/workflows/win-build.yml index 8e902566ab..4a46419088 100644 --- a/.github/workflows/win-build.yml +++ b/.github/workflows/win-build.yml @@ -2,11 +2,7 @@ name: Windows on: push: branches: - - "main" - - "core-8-branch" - - "core-8-6-branch" - tags: - - "core-**" + - "cgimage_with_crossing" permissions: contents: read env: From 31e1c7993caffba710f061daef4aaa36b89845b8 Mon Sep 17 00:00:00 2001 From: culler Date: Thu, 13 Jun 2024 02:31:46 +0000 Subject: [PATCH 70/96] Make place-14.1 and textDisp-10.6 pass. --- macosx/tkMacOSXWindowEvent.c | 31 ++++++++++++++++++++----------- tests/textDisp.test | 11 ++++++++--- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 6448732475..3681d609fb 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -932,7 +932,6 @@ Tk_MacOSXIsAppInFront(void) * Restrict event processing to Expose events. */ -#if 0 static Tk_RestrictAction ExposeRestrictProc( void *arg, @@ -941,7 +940,6 @@ ExposeRestrictProc( return (eventPtr->type==Expose && eventPtr->xany.serial==PTR2UINT(arg) ? TK_PROCESS_EVENT : TK_DEFER_EVENT); } -#endif /* * Restrict event processing to ConfigureNotify events. @@ -1122,14 +1120,15 @@ ConfigureRestrictProc( - (void) generateExposeEvents: (NSRect) rect { - int updatesNeeded; CGRect updateBounds; TkWindow *winPtr = TkMacOSXGetTkWindow([self window]); - //void *oldArg; - //Tk_RestrictProc *oldProc; + void *oldArg; + Tk_RestrictProc *oldProc; static int reentered = 0; - if (!winPtr) { + if (!winPtr || + (winPtr->flags & (TK_ALREADY_DEAD)) || + !Tk_IsMapped(winPtr)) { return; } @@ -1141,7 +1140,7 @@ ConfigureRestrictProc( * crashes or very poor performance. The reentered flag is * used to detect this. */ - // fprintf(stderr, "Recursive call to generateExposeEvents\n"); + //fprintf(stderr, "Recursive call to generateExposeEvents\n"); return; } reentered = 1; @@ -1157,16 +1156,26 @@ ConfigureRestrictProc( } updateBounds.origin.y = ([self bounds].size.height - updateBounds.origin.y - updateBounds.size.height); - updatesNeeded = GenerateUpdates(&updateBounds, winPtr); - if ([self inLiveResize] && updatesNeeded) { + if ( GenerateUpdates(&updateBounds, winPtr)) { + /* + * Use the ExposeRestrictProc to process the expose events we just + * generated. This will create idle drawing tasks, which we handle + * before we return in the case of a live resize. + */ + unsigned int serial = LastKnownRequestProcessed(Tk_Display(winPtr)); + oldProc = Tk_RestrictEvents(ExposeRestrictProc, UINT2PTR(serial), &oldArg); + while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS|TCL_DONT_WAIT)) {}; + Tk_RestrictEvents(oldProc, NULL, &oldArg); /* * During a LiveResize we process all idle tasks generated by the * expose events to redraw the window while it is being resized. */ - - while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} + if ([self inLiveResize]) { + while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} + } } + reentered = 0; } diff --git a/tests/textDisp.test b/tests/textDisp.test index f5ee1ef34e..ffb2b2acbb 100644 --- a/tests/textDisp.test +++ b/tests/textDisp.test @@ -1107,21 +1107,26 @@ test textDisp-6.9 {DisplayText, horizontal scrollbar updates} { set scrollInfo } [list 0.0 [expr {4.0/11}]] test textDisp-6.10 {DisplayText, redisplay embedded windows after scroll} {aqua} { + # For this test to pass line 8 must be out of the text widget. + # With macOS 14 this requires making the buttons a little larger. + # So we set the pady option. This may depend on the OS version. .t configure -wrap char + update .t delete 1.0 end + update .t insert 1.0 "Line 1" foreach i {2 3 4} { .t insert end "\nLine $i" } .t insert end "\n" .t window create end -create { - button %W.button_one -text "Button 1"} + button %W.button_one -text "Button 1" -pady 5} .t insert end "\nLine 6\n" .t window create end -create { - button %W.button_two -text "Button 2"} + button %W.button_two -text "Button 2" -pady 5} .t insert end "\nLine 8\n" .t window create end -create { - button %W.button_three -text "Button 3"} + button %W.button_three -text "Button 3" -pady 5} update set tk_textEmbWinDisplay {} .t delete 2.0 3.0 From 55453902cf3de34e45799dbc6fc7f59f6151ef56 Mon Sep 17 00:00:00 2001 From: culler Date: Thu, 13 Jun 2024 03:41:50 +0000 Subject: [PATCH 71/96] Try to guard against crashes in TkpChangeFocus --- macosx/tkMacOSXWindowEvent.c | 1 - macosx/tkMacOSXWm.c | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 3681d609fb..d99c06b8c3 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -1175,7 +1175,6 @@ ConfigureRestrictProc( while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} } } - reentered = 0; } diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index c1c7f926e9..857524cc5d 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -7225,7 +7225,10 @@ TkpChangeFocus( * didn't originally belong to topLevelPtr's * application. */ { - if (winPtr->atts.override_redirect) { + if (!winPtr || + (winPtr->flags & TK_ALREADY_DEAD) || + !Tk_IsMapped(winPtr) || + winPtr->atts.override_redirect) { return 0; } From 5daf3d269c59e298355817203585c172cf488d07 Mon Sep 17 00:00:00 2001 From: culler Date: Sat, 15 Jun 2024 03:12:12 +0000 Subject: [PATCH 72/96] Edit comments. Possible fix for crashes with TkTables. --- macosx/tkMacOSXDraw.c | 17 +++++++++++++++++ macosx/tkMacOSXWindowEvent.c | 5 ++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index 3435ebeb1f..0e0a35e20d 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -1078,6 +1078,23 @@ XFillArcs( * Results: * Returns 0 if the scroll generated no additional damage. Otherwise, sets * the region that needs to be repainted after scrolling and returns 1. + * When drawRect was in use, this function used the now deprecated + * scrollRect method of NSView. With the current updateLayer + * implementation, using a CGImage as the view's backing layer, we are + * able to use XCopyArea. But both implementations are incomplete. + * They return a damage area which is just the source rectangle minus + * destination rectangle. Other platforms, e.g. Windows, where + * this function is essentially provided by the windowing system, + * are able to add to the damage region the bounding rectangles of + * all subwindows which meet the source rectangle, even if they are + * contained in the destination rectangle. The information needed + * to do that is not available in this module, as far as I know. + * + * In fact, the Text widget is the only one which calls this + * function, and textDisp.c compensates for this defect by using + * macOS-specific code. This is possible because access to the + * list of all embedded windows in a Text widget is available in + * that module. * * Side effects: * Scrolls the bits in the window. diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index d99c06b8c3..c7320d480b 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -1077,9 +1077,12 @@ ConfigureRestrictProc( /* * To make the reconfiguration actually happen we need to process * idle tasks generated when processing the ConfigureNotify events. + * We also process timer events - without that there were crashes + * when embedded windows in a Text widget were mapped for the first + * time during a live resize. This is unexplained. */ - while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} + while (Tcl_DoOneEvent(TCL_TIMER_EVENTS|TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} /* * Now that Tk has configured all subwindows, create the clip regions. From 5175b17dce9102ec83c6f8a3db45ac1a0725a01c Mon Sep 17 00:00:00 2001 From: culler Date: Tue, 18 Jun 2024 12:17:43 +0000 Subject: [PATCH 73/96] Remove inner loop in setFrameSize, tame the chaos with UpdateClipRgn, deal with the artifacts later. --- macosx/tkMacOSXDraw.c | 12 ++++++++++-- macosx/tkMacOSXSubwindows.c | 4 +++- macosx/tkMacOSXWindowEvent.c | 21 +-------------------- macosx/tkMacOSXWm.c | 1 - 4 files changed, 14 insertions(+), 24 deletions(-) diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index 0e0a35e20d..66140fe94d 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -1538,10 +1538,13 @@ TkMacOSXGetClipRgn( } if (macDraw->drawRgn) { + // The drawRgn is the visRgn intersected with a rectangle which + // may be smaller than the widget bounds. clipRgn = HIShapeCreateCopy(macDraw->drawRgn); } else if (macDraw->visRgn) { clipRgn = HIShapeCreateCopy(macDraw->visRgn); } + // A NULL clipRgn does not allow any drawing at all. return clipRgn; } @@ -1551,7 +1554,9 @@ TkMacOSXGetClipRgn( * Tk_ClipDrawableToRect -- * * Clip all drawing into the drawable d to the given rectangle. If width - * or height are negative, reset to no clipping. + * or height are negative, reset to no clipping.bThis is called by the + * Text widget to display each DLine, and by the Canvas widget when it + * is updating a sub rectangle in the canvas. * * Results: * None. @@ -1580,7 +1585,10 @@ Tk_ClipDrawableToRect( width, height); HIShapeRef drawRgn = HIShapeCreateWithRect(&clipRect); - if (macDraw->winPtr && macDraw->flags & TK_CLIP_INVALID) { + // When drawing a Text widget we can reuse the + // clipping region for different DLines, so we don't want to + // update unless necessary. + if (macDraw->winPtr && (macDraw->flags & TK_CLIP_INVALID)) { TkMacOSXUpdateClipRgn(macDraw->winPtr); } if (macDraw->visRgn) { diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index 8cfdaf2f0c..d571d8a3c8 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -367,7 +367,8 @@ XUnmapWindow( * redraw the window. */ - TkMacOSXInvalidateWindow(macWin, TK_PARENT_WINDOW); + TkMacOSXInvalClipRgns((Tk_Window)winPtr->parentPtr); + //TkMacOSXInvalidateWindow(macWin, TK_PARENT_WINDOW); // if (parentPtr && parentPtr->privatePtr->visRgn) { // TkMacOSXInvalidateViewRegion( // TkMacOSXGetNSViewForDrawable(parentPtr->window), @@ -966,6 +967,7 @@ TkMacOSXUpdateClipRgn( } } +// Unused and misspelled stub function /* *---------------------------------------------------------------------- * diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index c247573070..603056425a 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -1072,28 +1072,9 @@ ConfigureRestrictProc( TkGenWMConfigureEvent(tkwin, Tk_X(tkwin), Tk_Y(tkwin), width, height, TK_SIZE_CHANGED | TK_MACOSX_HANDLE_EVENT_IMMEDIATELY); oldProc = Tk_RestrictEvents(ConfigureRestrictProc, NULL, &oldArg); + while (Tcl_DoOneEvent(TCL_WINDOW_EVENTS|TCL_DONT_WAIT)) {} Tk_RestrictEvents(oldProc, oldArg, &oldArg); - /* - * To make the reconfiguration actually happen we need to process idle - * tasks generated when processing the ConfigureNotify events. We also - * process timer events because the Text widget updates line metrics - * asynchronously using timer tasks. Skipping those updates can (but - * shouldn't) cause crashes when resizing a complex Text widget with - * embedded windows. The crash occurs if an embedded window is mapped - * for the first time while the window is being resized. - */ - - while (Tcl_DoOneEvent(TCL_TIMER_EVENTS|TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} - - /* - * Now that Tk has configured all subwindows, create the clip regions. - */ - - TkMacOSXInvalClipRgns(tkwin); - TkMacOSXUpdateClipRgn(winPtr); - //TkMacOSXSetDrawingEnabled(winPtr, 1); - /* * Redraw the entire content view. */ diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index 857524cc5d..dfa5701a66 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -292,7 +292,6 @@ static void syncLayout(NSWindow *macWindow) wmPtr->parentHeight = winPtr->changes.height + frameRect.size.height - contentRect.size.height; TkMacOSXInvalClipRgns((Tk_Window)winPtr); - TkMacOSXUpdateClipRgn(winPtr); } } #endif From cc10cafedd0c8000ace204e0cdd38bd24a8030d3 Mon Sep 17 00:00:00 2001 From: culler Date: Wed, 19 Jun 2024 14:30:26 +0000 Subject: [PATCH 74/96] Clean up setFrameSize. --- macosx/tkMacOSXWindowEvent.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 603056425a..0cb15c1d43 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -1038,13 +1038,6 @@ ConfigureRestrictProc( TkWindow *winPtr = TkMacOSXGetTkWindow(w); Tk_Window tkwin = (Tk_Window)winPtr; - if (![self inLiveResize] && - [w respondsToSelector: @selector (tkLayoutChanged)]) { - [(TKWindow *)w tkLayoutChanged]; - [self generateExposeEvents:[self bounds]]; - - } - if (winPtr) { unsigned int width = (unsigned int)newsize.width; unsigned int height=(unsigned int)newsize.height; @@ -1079,13 +1072,12 @@ ConfigureRestrictProc( * Redraw the entire content view. */ + if ([w respondsToSelector: @selector (tkLayoutChanged)]) { + [(TKWindow *)w tkLayoutChanged]; + [self generateExposeEvents:[self bounds]]; + } if ([self inLiveResize]) { [self viewDidChangeBackingProperties]; - [self setNeedsDisplay:YES]; - } else { - if ([w respondsToSelector: @selector (tkLayoutChanged)]) { - [(TKWindow *)w tkLayoutChanged]; - } } /* @@ -1095,6 +1087,8 @@ ConfigureRestrictProc( [NSApp _unlockAutoreleasePool]; } + [self setNeedsDisplay:YES]; + } /* From 321e71e5ce35e259f00d9405da28983fa9b820ea Mon Sep 17 00:00:00 2001 From: culler Date: Wed, 19 Jun 2024 18:27:07 +0000 Subject: [PATCH 75/96] A little more clean-up. --- macosx/tkMacOSXWindowEvent.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 0cb15c1d43..cfb46aa031 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -1014,7 +1014,7 @@ ConfigureRestrictProc( self.layer.contentsScale = self.window.screen.backingScaleFactor; [self resetTkLayerBitmapContext]; // need to redraw - [self generateExposeEvents: [self bounds]]; + [self generateExposeEvents: self.bounds]; } #endif @@ -1068,16 +1068,24 @@ ConfigureRestrictProc( while (Tcl_DoOneEvent(TCL_WINDOW_EVENTS|TCL_DONT_WAIT)) {} Tk_RestrictEvents(oldProc, oldArg, &oldArg); - /* - * Redraw the entire content view. - */ if ([w respondsToSelector: @selector (tkLayoutChanged)]) { [(TKWindow *)w tkLayoutChanged]; - [self generateExposeEvents:[self bounds]]; } + + /* + * Reset the cgimage layer and redraw the entire content view. + */ + + [self viewDidChangeBackingProperties]; + + /* + * In live resize we seem to need to draw a second time to + * avoid artifacts. + */ + if ([self inLiveResize]) { - [self viewDidChangeBackingProperties]; + [self generateExposeEvents:self.bounds]; } /* @@ -1087,6 +1095,7 @@ ConfigureRestrictProc( [NSApp _unlockAutoreleasePool]; } + // Schedule a redisplay of the view [self setNeedsDisplay:YES]; } From 716977c2dc0bb759c929da0f34a0a20b4eb967ad Mon Sep 17 00:00:00 2001 From: culler Date: Wed, 19 Jun 2024 21:29:24 +0000 Subject: [PATCH 76/96] Remove duplicate calls to reset the cgimage backing layer. --- macosx/tkMacOSXWindowEvent.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index cfb46aa031..2c16cf80c0 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -1033,7 +1033,6 @@ ConfigureRestrictProc( -(void) setFrameSize: (NSSize)newsize { [super setFrameSize: newsize]; - [self resetTkLayerBitmapContext]; NSWindow *w = [self window]; TkWindow *winPtr = TkMacOSXGetTkWindow(w); Tk_Window tkwin = (Tk_Window)winPtr; @@ -1045,19 +1044,12 @@ ConfigureRestrictProc( Tk_RestrictProc *oldProc; /* - * This can be called from outside the Tk event loop. Since it calls - * Tcl_DoOneEvent, we need to make sure we don't clobber the - * AutoreleasePool set up by the caller. + * This function can be re-entered. So we need to make sure we don't + * clobber any AutoreleasePool set up by the caller. */ [NSApp _lockAutoreleasePool]; - /* - * Disable Tk drawing until the window has been completely configured. - */ - - //TkMacOSXSetDrawingEnabled(winPtr, 0); - /* * Generate and handle a ConfigureNotify event for the new size. */ @@ -1069,6 +1061,10 @@ ConfigureRestrictProc( Tk_RestrictEvents(oldProc, oldArg, &oldArg); + /* + * Update Tk's window data for the new size. + */ + if ([w respondsToSelector: @selector (tkLayoutChanged)]) { [(TKWindow *)w tkLayoutChanged]; } @@ -1095,7 +1091,11 @@ ConfigureRestrictProc( [NSApp _unlockAutoreleasePool]; } - // Schedule a redisplay of the view + + /* + * Schedule a redisplay of the view. + */ + [self setNeedsDisplay:YES]; } From cb90cb6add00df5bec77b2625f20b3302e3a48ed Mon Sep 17 00:00:00 2001 From: culler Date: Sat, 22 Jun 2024 02:29:26 +0000 Subject: [PATCH 77/96] Make setFrameSize a tiny bit more efficient. --- macosx/tkMacOSXWindowEvent.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 2c16cf80c0..6c3f07acb6 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -982,9 +982,9 @@ ConfigureRestrictProc( return YES; } - (void) updateLayer { - CGContextRef ctx = self.tkLayerBitmapContext; + CGContextRef context = self.tkLayerBitmapContext; - if (ctx) { + if (context) { /* * Create a CGImage by copying (probably using copy-on-write) the * bitmap data of the CGBitmapContext that we have been using for @@ -993,7 +993,8 @@ ConfigureRestrictProc( * layer. This will cause all drawing done since the last call to this * function to become visible. */ - CGImageRef newImg = CGBitmapContextCreateImage(ctx); + + CGImageRef newImg = CGBitmapContextCreateImage(context); self.layer.contents = (__bridge id) newImg; CGImageRelease(newImg); // will quickly leak memory if this is missing [self clearTkDirtyRect]; @@ -1032,19 +1033,24 @@ ConfigureRestrictProc( -(void) setFrameSize: (NSSize)newsize { + NSSize oldsize = self.bounds.size; [super setFrameSize: newsize]; + if (newsize.width == 1 && newsize.height == 1 || + oldsize.width == 0 && oldsize.height == 0) { + return; + } NSWindow *w = [self window]; TkWindow *winPtr = TkMacOSXGetTkWindow(w); Tk_Window tkwin = (Tk_Window)winPtr; if (winPtr) { - unsigned int width = (unsigned int)newsize.width; - unsigned int height=(unsigned int)newsize.height; + unsigned int width = (unsigned int) newsize.width; + unsigned int height= (unsigned int) newsize.height; void *oldArg; Tk_RestrictProc *oldProc; /* - * This function can be re-entered. So we need to make sure we don't + * This function can be re-entered, so we need to make sure we don't * clobber any AutoreleasePool set up by the caller. */ @@ -1064,7 +1070,7 @@ ConfigureRestrictProc( /* * Update Tk's window data for the new size. */ - + if ([w respondsToSelector: @selector (tkLayoutChanged)]) { [(TKWindow *)w tkLayoutChanged]; } @@ -1079,7 +1085,7 @@ ConfigureRestrictProc( * In live resize we seem to need to draw a second time to * avoid artifacts. */ - + if ([self inLiveResize]) { [self generateExposeEvents:self.bounds]; } @@ -1093,11 +1099,10 @@ ConfigureRestrictProc( } /* - * Schedule a redisplay of the view. + * Request a call to updateLayer. */ [self setNeedsDisplay:YES]; - } /* From a1b9917ea6b5178d4e212d172d52f59a6359e31e Mon Sep 17 00:00:00 2001 From: culler Date: Sat, 22 Jun 2024 04:35:36 +0000 Subject: [PATCH 78/96] Even small speedups cause test failures. --- tests/unixWm.test | 1 + tests/window.test | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/unixWm.test b/tests/unixWm.test index b47fb91161..4d2dad55ff 100644 --- a/tests/unixWm.test +++ b/tests/unixWm.test @@ -1998,6 +1998,7 @@ test unixWm-50.9 {Tk_CoordsToWindow procedure, unmapped windows} {unix failsOnUb set result [list [winfo containing 100 100]] wm iconify .t2 update + restackDelay lappend result [winfo containing 100 100] } {.t2 .t} test unixWm-50.10 {Tk_CoordsToWindow procedure, unmapped windows} unix { diff --git a/tests/window.test b/tests/window.test index 8a56d5a0ea..892ceea64c 100644 --- a/tests/window.test +++ b/tests/window.test @@ -11,7 +11,8 @@ tcltest::configure {*}$argv tcltest::loadTestedCommands namespace import ::tk::test::loadTkCommand update - +# Move the mouse out of the way for window-2.1 +event generate {} -warp 1 -x 640 -y 10 # XXX This file is woefully incomplete. Right now it only tests # a few parts of a few procedures in tkWindow.c From fc4199e50686faf5dd37455e4a9313b137707f82 Mon Sep 17 00:00:00 2001 From: culler Date: Sat, 22 Jun 2024 16:16:37 +0000 Subject: [PATCH 79/96] Try adding an event loop to run idle tasks in updateLayer. --- macosx/tkMacOSXWindowEvent.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 6c3f07acb6..5d0121deb7 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -983,7 +983,6 @@ ConfigureRestrictProc( } - (void) updateLayer { CGContextRef context = self.tkLayerBitmapContext; - if (context) { /* * Create a CGImage by copying (probably using copy-on-write) the @@ -997,6 +996,12 @@ ConfigureRestrictProc( 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. + */ + + while(Tcl_DoOneEvent(TCL_IDLE_EVENTS)){} [self clearTkDirtyRect]; } } @@ -1035,8 +1040,8 @@ ConfigureRestrictProc( { NSSize oldsize = self.bounds.size; [super setFrameSize: newsize]; - if (newsize.width == 1 && newsize.height == 1 || - oldsize.width == 0 && oldsize.height == 0) { + if ((newsize.width == 1 && newsize.height == 1) || + (oldsize.width == 0 && oldsize.height == 0)) { return; } NSWindow *w = [self window]; From d5e7bdcdc2200b4a3a52846c1ab19dd0232dc7a3 Mon Sep 17 00:00:00 2001 From: culler Date: Sat, 22 Jun 2024 19:13:38 +0000 Subject: [PATCH 80/96] Try to ensure that clipping regions are ready before filling a frame; clean up unixWm.test --- generic/tkFrame.c | 2 ++ macosx/tkMacOSXWm.c | 12 +++++----- tests/unixWm.test | 54 ++++++++++++++------------------------------- 3 files changed, 24 insertions(+), 44 deletions(-) diff --git a/generic/tkFrame.c b/generic/tkFrame.c index 32f89f2e74..c0d45e86ef 100644 --- a/generic/tkFrame.c +++ b/generic/tkFrame.c @@ -1474,6 +1474,8 @@ DisplayFrame( Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin)); #else pixmap = Tk_WindowId(tkwin); + Tk_ClipDrawableToRect(Tk_Display(tkwin), pixmap, 0, 0, + Tk_Width(tkwin), Tk_Height(tkwin)); #endif /* TK_NO_DOUBLE_BUFFERING */ if (framePtr->type != TYPE_LABELFRAME) { diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index dfa5701a66..48e8d1a843 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -819,28 +819,26 @@ FrontWindowAtPoint( int y) { NSPoint p = NSMakePoint(x, TkMacOSXZeroScreenHeight() - y); - NSArray *windows = [NSApp orderedWindows]; - TkWindow *winPtr = NULL; - for (NSWindow *w in windows) { - winPtr = TkMacOSXGetTkWindow(w); + for (NSWindow *w in [NSApp orderedWindows]) { + TkWindow *winPtr = TkMacOSXGetTkWindow(w); if (winPtr) { NSRect windowFrame = [w frame]; - NSRect contentFrame = [w frame]; + NSRect contentFrame = windowFrame; - contentFrame.size.height = [[w contentView] frame].size.height; /* * For consistency with other platforms, points in the * title bar are not considered to be contained in the * window. */ + contentFrame.size.height = [[w contentView] frame].size.height; if (NSMouseInRect(p, contentFrame, NO)) { return winPtr; } else if (NSMouseInRect(p, windowFrame, NO)) { /* * The pointer is in the title bar of the highest NSWindow - * containing it, and therefore is should not be considered + * containing it, and therefore it should not be considered * to be contained in any Tk window. */ return NULL; diff --git a/tests/unixWm.test b/tests/unixWm.test index 4d2dad55ff..b5cafa161e 100644 --- a/tests/unixWm.test +++ b/tests/unixWm.test @@ -16,18 +16,6 @@ namespace import -force ::tk::test:loadTkCommand testConstraint failsOnUbuntu [expr {![info exists ::env(CI)] || ![string match Linux $::tcl_platform(os)]}] testConstraint failsOnXQuarz [expr {$tcl_platform(os) ne "Darwin" || [tk windowingsystem] ne "x11" }] -# Starting with macOS Ventura it became necessary to wait for windows to be restacked -# or to be raised after creation. - -if {[tk windowingsystem] eq "aqua"} { - proc restackDelay {} { - after 200; - update idletasks - } -} else { - proc restackDelay {} {} -} - proc sleep ms { global x after $ms {set x 1} @@ -1839,14 +1827,12 @@ test unixWm-50.2 {Tk_CoordsToWindow procedure, finding a toplevel, y-coords and tkwait visibility .t wm geom .t +100+100 update - restackDelay toplevel .t2 -width 200 -height 100 -bg blue wm overrideredirect .t2 1 tkwait visibility .t2 wm geom .t2 +200+200 update raise .t2 - restackDelay set x [winfo rootx .t] set y [winfo rooty .t] set y2 [winfo rooty .t2] @@ -1860,11 +1846,10 @@ test unixWm-50.2 {Tk_CoordsToWindow procedure, finding a toplevel, y-coords and [winfo containing [expr $x +200] [expr $y + 450]] } {{} {} .t .t .t2 .t2 .t {}} test unixWm-50.3 { - Tk_CoordsToWindow procedure, finding a toplevel with embedding + Tk_CoordsToWindow procedure, finding a toplevel with embedding } tempNotWin { deleteWindows catch {interp delete child} - toplevel .t -width 300 -height 400 -bg blue wm geom .t +100+100 frame .t.f -container 1 -bg red @@ -1893,7 +1878,6 @@ test unixWm-50.3 { } {{} .x .t .t.f} test unixWm-50.4 {Tk_CoordsToWindow procedure, window in other application} unix { destroy .t - catch {interp delete child} toplevel .t -width 200 -height 200 -bg green tkwait visibility .t @@ -1902,9 +1886,8 @@ test unixWm-50.4 {Tk_CoordsToWindow procedure, window in other application} unix interp create child load {} Tk child child eval {wm geometry . 200x200+100+100; update} - restackDelay set result [list [winfo containing 200 200] \ - [child eval {winfo containing 200 200}]] + [child eval {winfo containing 200 200}]] interp delete child set result } {{} .} @@ -1984,22 +1967,24 @@ test unixWm-50.8 {Tk_CoordsToWindow procedure, more basics} unix { test unixWm-50.9 {Tk_CoordsToWindow procedure, unmapped windows} {unix failsOnUbuntu failsOnXQuarz} { destroy .t destroy .t2 + update toplevel .t -width 200 -height 200 -bg green tkwait visibility .t - update - wm geometry .t +0+0 + wm geometry .t +20+20 update toplevel .t2 -width 200 -height 200 -bg red tkwait visibility .t2 update - wm geometry .t2 +0+0 + wm geometry .t2 +20+20 update - restackDelay - set result [list [winfo containing 100 100]] - wm iconify .t2 +# set temp [winfo containing 120 120] +# unset temp + set result [list [winfo containing 120 120]] + destroy .t2 update - restackDelay - lappend result [winfo containing 100 100] +# set temp [winfo containing 120 120] +# unset temp + lappend result [winfo containing 120 120] } {.t2 .t} test unixWm-50.10 {Tk_CoordsToWindow procedure, unmapped windows} unix { destroy .t @@ -2077,7 +2062,6 @@ test unixWm-51.6 {TkWmRestackToplevel procedure, window to be stacked isn't mapp tkwait visibility .t wm geometry .t +0+0 update - restackDelay destroy .t2 toplevel .t2 -width 200 -height 200 -bg red # This test assumes that .t2 is not mapped yet, but that is not really guaranteed. @@ -2094,12 +2078,10 @@ test unixWm-51.7 {TkWmRestackToplevel procedure, other window isn't mapped} {uni } update raise .t .t2 - restackDelay update set result [list [winfo containing 200 200]] lower .t3 - restackDelay - sleep 10 + update lappend result [winfo containing 200 200] } {.t3 .t} test unixWm-51.8 {TkWmRestackToplevel procedure, overrideredirect windows} unix { @@ -2113,8 +2095,7 @@ test unixWm-51.8 {TkWmRestackToplevel procedure, overrideredirect windows} unix wm overrideredirect .t2 1 wm geometry .t2 +0+0 tkwait visibility .t2 - restackDelay - + # Need to use vrootx and vrooty to make tests work correctly with # virtual root window measures managers: overrideredirect windows # come up at (0,0) in display coordinates, not virtual root @@ -2124,15 +2105,14 @@ test unixWm-51.8 {TkWmRestackToplevel procedure, overrideredirect windows} unix set y [expr 100-[winfo vrooty .]] set result [list [winfo containing $x $y]] raise .t - restackDelay lappend result [winfo containing $x $y] raise .t2 - restackDelay lappend result [winfo containing $x $y] } {.t2 .t .t2} # The mac won't put an overrideredirect window above the root, if {[tk windowingsystem] eq "aqua"} { wm withdraw . + update } test unixWm-51.9 {TkWmRestackToplevel procedure, other window overrideredirect} unix { foreach w {.t .t2 .t3} { @@ -2145,7 +2125,6 @@ test unixWm-51.9 {TkWmRestackToplevel procedure, other window overrideredirect} update } lower .t3 .t2 - restackDelay update # Need to use vrootx and vrooty to make tests work correctly with @@ -2157,11 +2136,12 @@ test unixWm-51.9 {TkWmRestackToplevel procedure, other window overrideredirect} set y [expr 100-[winfo vrooty .]] set result [list [winfo containing $x $y]] lower .t2 - restackDelay + update lappend result [winfo containing $x $y] } {.t2 .t3} if {[tk windowingsystem] eq "aqua"} { wm deiconify . + update } test unixWm-51.10 {TkWmRestackToplevel procedure, don't move window that's already in the right place} unix { makeToplevels From 17b7b11ec3b9a124f9c77e580aa2622f1ba2111a Mon Sep 17 00:00:00 2001 From: culler Date: Sat, 22 Jun 2024 19:27:30 +0000 Subject: [PATCH 81/96] Add a change in unixWm.test that was accidentally omitted. --- tests/unixWm.test | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/unixWm.test b/tests/unixWm.test index b5cafa161e..5e6dd76ee3 100644 --- a/tests/unixWm.test +++ b/tests/unixWm.test @@ -1977,13 +1977,9 @@ test unixWm-50.9 {Tk_CoordsToWindow procedure, unmapped windows} {unix failsOnUb update wm geometry .t2 +20+20 update -# set temp [winfo containing 120 120] -# unset temp set result [list [winfo containing 120 120]] destroy .t2 update -# set temp [winfo containing 120 120] -# unset temp lappend result [winfo containing 120 120] } {.t2 .t} test unixWm-50.10 {Tk_CoordsToWindow procedure, unmapped windows} unix { From 0a40837d772655d97d4389a5d75af6490c270291 Mon Sep 17 00:00:00 2001 From: culler Date: Sat, 22 Jun 2024 22:52:14 +0000 Subject: [PATCH 82/96] Fix the simple black window bug. --- macosx/tkMacOSXDraw.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index 66140fe94d..2a9a684382 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -1228,7 +1228,12 @@ TkMacOSXSetupDrawingContext( dc.clipRgn = TkMacOSXGetClipRgn(d); ClipToGC(d, gc, &dc.clipRgn); if (dc.clipRgn && HIShapeIsEmpty(dc.clipRgn)) { + /* + * Things are probably not set up for drawing yet. Request a call to + * updateLayer and return failure. + */ canDraw = false; + [view setNeedsDisplay:YES]; goto end; } //#endif //disable clipping From c6c6608c7820a8d76be8769ea476ad29f2eb1760 Mon Sep 17 00:00:00 2001 From: culler Date: Sun, 23 Jun 2024 18:12:34 +0000 Subject: [PATCH 83/96] Clean up: compiler warnings; stop setting tkDirtyRect, since we aren't using it. --- macosx/tkMacOSXColor.c | 16 ++++++++++------ macosx/tkMacOSXDraw.c | 18 +++++------------- macosx/tkMacOSXNotify.c | 11 ++++++++--- macosx/tkMacOSXSubwindows.c | 2 -- macosx/tkMacOSXWindowEvent.c | 4 ++-- macosx/tkMacOSXWm.c | 14 ++++++++++---- 6 files changed, 35 insertions(+), 30 deletions(-) diff --git a/macosx/tkMacOSXColor.c b/macosx/tkMacOSXColor.c index cd42be8e21..540d01be53 100644 --- a/macosx/tkMacOSXColor.c +++ b/macosx/tkMacOSXColor.c @@ -655,13 +655,13 @@ TkpGetColor( CGFloat rgba[4]; #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 if (@available(macOS 10.14, *)) { - NSAppearance *windowAppearance; + NSAppearance *appearance; if (view) { - windowAppearance = [view effectiveAppearance]; + appearance = [view effectiveAppearance]; } else { - windowAppearance = [NSApp effectiveAppearance]; + appearance = [NSApp effectiveAppearance]; } - if ([windowAppearance name] == NSAppearanceNameDarkAqua) { + if ([appearance name] == NSAppearanceNameDarkAqua) { colormap = darkColormap; } else { colormap = lightColormap; @@ -669,13 +669,17 @@ TkpGetColor( if (@available(macOS 11.0, *)) { #if MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 CGFloat *rgbaPtr = rgba; - [windowAppearance performAsCurrentDrawingAppearance:^{ + [appearance performAsCurrentDrawingAppearance:^{ GetRGBA(entry, p.ulong, rgbaPtr); }]; #endif } else { #if MAC_OS_X_VERSION_MIN_REQUIRED < 110000 - NSAppearance *savedAppearance = [NSAppearance currentAppearance]; + if (@available(macOS 11.0, *)) { + NSAppearance *savedAppearance = [NSAppearance currentDrawingAppearance]; + } else { + NSAppearance *savedAppearance = [NSAppearance currentAppearance]; + } [NSAppearance setCurrentAppearance:windowAppearance]; GetRGBA(entry, p.ulong, rgba); [NSAppearance setCurrentAppearance:savedAppearance]; diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index 2a9a684382..b9b0bcf057 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -1244,7 +1244,6 @@ TkMacOSXSetupDrawingContext( dc.context = TkMacOSXGetCGContextForDrawable(d); if (!dc.context) { - NSRect drawingBounds; dc.view = view; dc.context = view.tkLayerBitmapContext; if (dc.clipRgn) { @@ -1253,16 +1252,14 @@ TkMacOSXSetupDrawingContext( .ty = [view bounds].size.height}; HIShapeGetBounds(dc.clipRgn, &clipBounds); clipBounds = CGRectApplyAffineTransform(clipBounds, t); - drawingBounds = NSRectFromCGRect(clipBounds); - } else { - drawingBounds = [view bounds]; } - // It seems this should be the only place to use addTkDirtyRect: - // and that it should not be used elsewhere as a proxy to generate - // Expose events, which will not work. + /* + * Mark the view as needing to be redisplayed, since we are drawing + * to its backing layer. + */ - [view addTkDirtyRect:drawingBounds]; + [view setTkNeedsDisplay:YES]; /* * Workaround for an Apple bug. @@ -1488,11 +1485,6 @@ TkMacOSXRestoreDrawingContext( CFRelease(dcPtr->clipRgn); dcPtr->clipRgn = NULL; } -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 - if (@available(macOS 10.14, *)) { - //NSAppearance.currentAppearance = [dcPtr->view effectiveAppearance]; - } -#endif #ifdef TK_MAC_DEBUG bzero(dcPtr, sizeof(TkMacOSXDrawingContext)); diff --git a/macosx/tkMacOSXNotify.c b/macosx/tkMacOSXNotify.c index b0410027e4..5530e22291 100644 --- a/macosx/tkMacOSXNotify.c +++ b/macosx/tkMacOSXNotify.c @@ -310,12 +310,12 @@ TkMacOSXNotifyExitHandler( * 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 + * is undocumented, namely that it sometimes blocks and calls updateLayer * 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. The * only times when any NSViews have the needsDisplay property set to YES - * are during execution of this function or in the addTkDirtyRect method + * are during execution of this function or in the setFrameSize method * of TKContentView. * * The reason for running this function as an idle task is to try to @@ -342,7 +342,7 @@ void TkMacOSXDrawAllViews( void *clientData) { - int count = 0, *dirtyCount = (int *)clientData; + int count = 0, *dirtyCount = (int *)clientData; for (NSWindow *window in [NSApp windows]) { if ([[window contentView] isMemberOfClass:[TKContentView class]]) { @@ -361,6 +361,11 @@ TkMacOSXDrawAllViews( if (dirtyCount) { *dirtyCount = count; } + + /* + * Trigger calls to updateLayer methods for the views flagged above. + */ + [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:GetRunLoopMode(TkMacOSXGetModalSession()) diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index d571d8a3c8..150c7564e2 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -746,8 +746,6 @@ XConfigureWindow( if (view) { TkMacOSXInvalidateWindow(macWin, TK_PARENT_WINDOW); - //TkMacOSXInvalClipRgns((Tk_Window)winPtr->parentPtr); - //TkpRedrawWidget((Tk_Window)winPtr); } } diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 5d0121deb7..cffd1c5b13 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -244,7 +244,7 @@ extern NSString *NSWindowDidOrderOffScreenNotification; [view viewDidChangeEffectiveAppearance]; } #endif - [view addTkDirtyRect:[view bounds]]; + [view setTkNeedsDisplay:YES]; Tcl_CancelIdleCall(TkMacOSXDrawAllViews, NULL); Tcl_DoWhenIdle(TkMacOSXDrawAllViews, NULL); } @@ -1002,7 +1002,7 @@ ConfigureRestrictProc( */ while(Tcl_DoOneEvent(TCL_IDLE_EVENTS)){} - [self clearTkDirtyRect]; + [self setTkNeedsDisplay:NO]; } } diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index 48e8d1a843..647c2fddb2 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -2442,7 +2442,7 @@ WmDeiconifyCmd( [win setExcludedFromWindowsMenu:NO]; TkMacOSXApplyWindowAttributes(winPtr, win); [win orderFront:NSApp]; - [[win contentView] setNeedsDisplay:YES]; + [[win contentView] setTkNeedsDisplay:YES]; } if (wmPtr->icon) { Tk_UnmapWindow((Tk_Window)wmPtr->icon); @@ -6873,9 +6873,11 @@ TkMacOSXMakeRealWindowExist( * * TkpRedrawWidget -- * - * Mark the bounding rectangle of this widget as needing display so the - * widget will be drawn by [NSView drawRect:]. If this is called within - * the drawRect method, do nothing. + * This is a stub called only from tkTextDisp.c. It was introduced + * to deal with an issue in macOS 10.14 and is not needed + * even for that OS with updateLayer in use. It would add the widget bounds + * to the dirtyRect, which is not currently used, and set the + * TkNeedsDisplay flag. Now it is a no-op. * * Results: * None. @@ -6888,6 +6890,8 @@ TkMacOSXMakeRealWindowExist( void TkpRedrawWidget(Tk_Window tkwin) { + (void) tkwin; +#if 0 TkWindow *winPtr = (TkWindow *)tkwin; NSWindow *w = nil; Rect tkBounds; @@ -6904,7 +6908,9 @@ TkpRedrawWidget(Tk_Window tkwin) { tkBounds.right - tkBounds.left, tkBounds.bottom - tkBounds.top); [view addTkDirtyRect:bounds]; + [view setNeedsDisplay:YES]; } +#endif } From 134d97fa9a856b82572d2eace06f84e69b04ef81 Mon Sep 17 00:00:00 2001 From: culler Date: Sun, 23 Jun 2024 21:55:38 +0000 Subject: [PATCH 84/96] Revert tkMacOSXColor.c. We'll live with the warnings for now. --- macosx/tkMacOSXColor.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/macosx/tkMacOSXColor.c b/macosx/tkMacOSXColor.c index 540d01be53..cd42be8e21 100644 --- a/macosx/tkMacOSXColor.c +++ b/macosx/tkMacOSXColor.c @@ -655,13 +655,13 @@ TkpGetColor( CGFloat rgba[4]; #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 if (@available(macOS 10.14, *)) { - NSAppearance *appearance; + NSAppearance *windowAppearance; if (view) { - appearance = [view effectiveAppearance]; + windowAppearance = [view effectiveAppearance]; } else { - appearance = [NSApp effectiveAppearance]; + windowAppearance = [NSApp effectiveAppearance]; } - if ([appearance name] == NSAppearanceNameDarkAqua) { + if ([windowAppearance name] == NSAppearanceNameDarkAqua) { colormap = darkColormap; } else { colormap = lightColormap; @@ -669,17 +669,13 @@ TkpGetColor( if (@available(macOS 11.0, *)) { #if MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 CGFloat *rgbaPtr = rgba; - [appearance performAsCurrentDrawingAppearance:^{ + [windowAppearance performAsCurrentDrawingAppearance:^{ GetRGBA(entry, p.ulong, rgbaPtr); }]; #endif } else { #if MAC_OS_X_VERSION_MIN_REQUIRED < 110000 - if (@available(macOS 11.0, *)) { - NSAppearance *savedAppearance = [NSAppearance currentDrawingAppearance]; - } else { - NSAppearance *savedAppearance = [NSAppearance currentAppearance]; - } + NSAppearance *savedAppearance = [NSAppearance currentAppearance]; [NSAppearance setCurrentAppearance:windowAppearance]; GetRGBA(entry, p.ulong, rgba); [NSAppearance setCurrentAppearance:savedAppearance]; From 63a7ddde3c69911327cd98cd3dbb71ed214c2d8b Mon Sep 17 00:00:00 2001 From: culler Date: Sun, 23 Jun 2024 22:45:11 +0000 Subject: [PATCH 85/96] Don't redraw the container when a content widget is mapped. --- macosx/tkMacOSXSubwindows.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index 150c7564e2..4ac69eb584 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -208,10 +208,7 @@ XMapWindow( * the window. */ - // We could add a TK_CONTAINER_WINDOW flag and have - // TkMacOSXInvalidateWindow invalidate the clip regions. TkMacOSXInvalClipRgns(contWinPtr); - TkMacOSXInvalidateWindow(macWin, TK_PARENT_WINDOW); } TkMacOSXInvalClipRgns((Tk_Window)winPtr); } else { From f8087a27cf185e1321edaf4f8a612a71b99a84e2 Mon Sep 17 00:00:00 2001 From: culler Date: Mon, 24 Jun 2024 20:09:11 +0000 Subject: [PATCH 86/96] Avoid redrawing the entire view each time a widget is mapped by the grid manager. --- macosx/tkMacOSXPrivate.h | 1 + macosx/tkMacOSXSubwindows.c | 51 +++++++++++++++++-------------------- macosx/tkMacOSXWm.c | 7 +++++ 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h index 94b8b3bac2..c4352a0267 100644 --- a/macosx/tkMacOSXPrivate.h +++ b/macosx/tkMacOSXPrivate.h @@ -247,6 +247,7 @@ MODULE_SCOPE void TkMacOSXRestoreDrawingContext( TkMacOSXDrawingContext *dcPtr); MODULE_SCOPE void TkMacOSXSetColorInContext(GC gc, unsigned long pixel, CGContextRef context); +MODULE_SCOPE void TkMacOSXRedrawViewIdleTask(void *clientData); #define TkMacOSXGetTkWindow(window) ((TkWindow *)Tk_MacOSXGetTkWindow(window)) #define TkMacOSXGetNSWindowForDrawable(drawable) ((NSWindow *)Tk_MacOSXGetNSWindowForDrawable(drawable)) #define TkMacOSXGetNSViewForDrawable(macWin) ((NSView *)Tk_MacOSXGetNSViewForDrawable((Drawable)(macWin))) diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index 4ac69eb584..3da0492b37 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -56,7 +56,8 @@ XDestroyWindow( Window window) /* Window. */ { MacDrawable *macWin = (MacDrawable *)window; - // fprintf(stderr, "XDestroyWindow: %s with parent %s\n", + TKContentView *view = (TKContentView *)TkMacOSXGetNSViewForDrawable(macWin); + //fprintf(stderr, "XDestroyWindow: %s with parent %s\n", // Tk_PathName(macWin->winPtr), // Tk_PathName(macWin->winPtr->parentPtr)); @@ -71,7 +72,9 @@ XDestroyWindow( if (!Tk_IsTopLevel(macWin->winPtr)) { if (macWin->winPtr->parentPtr != NULL) { - TkMacOSXInvalidateWindow(macWin, TK_PARENT_WINDOW); + TkMacOSXInvalClipRgns(macWin->winPtr->parentPtr); + Tcl_CancelIdleCall(TkMacOSXRedrawViewIdleTask, (void *) view); + Tcl_DoWhenIdle(TkMacOSXRedrawViewIdleTask, (void *) view); } if (macWin->visRgn) { CFRelease(macWin->visRgn); @@ -149,11 +152,10 @@ XMapWindow( return BadWindow; } MacDrawable *macWin = (MacDrawable *)window; - TkWindow *winPtr = macWin->winPtr; - NSWindow *win = TkMacOSXGetNSWindowForDrawable(window); static Bool initialized = NO; NSPoint mouse = [NSEvent mouseLocation]; int x = mouse.x, y = TkMacOSXZeroScreenHeight() - mouse.y; + //fprintf(stderr, "XMapWindow: %s\n", Tk_PathName(macWin->winPtr)); /* * Under certain situations it's possible for this function to be called @@ -167,10 +169,12 @@ XMapWindow( TkMacOSXMakeRealWindowExist(macWin->toplevel->winPtr); } + TkWindow *winPtr = macWin->winPtr; + NSWindow *win = TkMacOSXGetNSWindowForDrawable(window); + TKContentView *view = [win contentView]; LastKnownRequestProcessed(display)++; 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 @@ -210,7 +214,6 @@ XMapWindow( TkMacOSXInvalClipRgns(contWinPtr); } - TkMacOSXInvalClipRgns((Tk_Window)winPtr); } else { /* @@ -222,20 +225,13 @@ XMapWindow( } /* - * Mark the toplevel as needing to be redrawn, unless the window is being - * mapped while drawing is taking place. + * If a geometry manager is mapping hundreds of windows we + * don't want to redraw the view hundreds of times, so do + * it in an idle task. */ - TKContentView *view = [win contentView]; - - /* - * Do not rely on addTkDirtyRect: to generate Expose events - * (though I’m not sure if this is the place to generate events; - * or if using generateExposeEvents: is the best way; - * what does XMapWindow() do on other platforms?) - */ - // Possibly this only needs to use the widget bounds. - [view generateExposeEvents:[view bounds]]; + Tcl_CancelIdleCall(TkMacOSXRedrawViewIdleTask, (void *) view); + Tcl_DoWhenIdle(TkMacOSXRedrawViewIdleTask, (void *) view); /* * Generate VisibilityNotify events for window and all mapped children. @@ -365,14 +361,6 @@ XUnmapWindow( */ TkMacOSXInvalClipRgns((Tk_Window)winPtr->parentPtr); - //TkMacOSXInvalidateWindow(macWin, TK_PARENT_WINDOW); - // if (parentPtr && parentPtr->privatePtr->visRgn) { - // TkMacOSXInvalidateViewRegion( - // TkMacOSXGetNSViewForDrawable(parentPtr->window), - // parentPtr->privatePtr->visRgn); - // } - //TkMacOSXInvalClipRgns((Tk_Window)parentPtr); - //TkMacOSXUpdateClipRgn(parentPtr); } return Success; } @@ -1073,6 +1061,15 @@ TkMacOSXInvalidateViewRegion( * *---------------------------------------------------------------------- */ +void +TkMacOSXRedrawViewIdleTask( + void *clientData) +{ + TKContentView *view = (TKContentView *) clientData; + // fprintf(stderr, "idle redraw for %p\n", view); + [view generateExposeEvents:[view bounds]]; + [view setTkNeedsDisplay:YES]; +} void TkMacOSXInvalidateWindow( @@ -1091,8 +1088,8 @@ TkMacOSXInvalidateWindow( if ((flag == TK_PARENT_WINDOW) && parent){ TkMacOSXInvalClipRgns(parent); } - // Here we should probably be using the damage region. [view generateExposeEvents:[view bounds]]; + [view setTkNeedsDisplay:YES]; } /* diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index 647c2fddb2..115060dcb1 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -1314,6 +1314,13 @@ TkWmDeadWindow( [NSApp _setKeyWindow:nil]; [NSApp _setMainWindow:nil]; } + + /* + * Avoid redrawing the view after it is released. + */ + + Tcl_CancelIdleCall(TkMacOSXRedrawViewIdleTask, + (void *) [deadNSWindow contentView]); [deadNSWindow close]; #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults]; From 478c22d47c125e789a22ef8cf05c3117288a0cdd Mon Sep 17 00:00:00 2001 From: culler Date: Tue, 25 Jun 2024 14:14:04 +0000 Subject: [PATCH 87/96] Work on unixWm-50.9 and -51.7. --- macosx/tkMacOSXWm.c | 17 ++++++++++++++++- tests/unixWm.test | 3 ++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index 115060dcb1..04a3ce5ff0 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -5665,6 +5665,15 @@ Tk_MoveToplevelWindow( * *---------------------------------------------------------------------- */ +#define PRINT_STACK \ + for (NSWindow *w in [NSApp orderedWindows]) { \ + TkWindow *winPtr2 = TkMacOSXGetTkWindow(w); \ + if (winPtr2) { \ + fprintf(stderr, "%s ", Tk_PathName(winPtr2)); \ + } \ + } \ + fprintf(stderr, "\n"); \ + fflush(stderr) void TkWmRestackToplevel( @@ -5722,8 +5731,14 @@ TkWmRestackToplevel( * Just let the Mac window manager deal with all the subtleties of keeping * track of off-screen windows, etc. */ - +#if 0 + fprintf(stderr, "window order: "); PRINT_STACK; +#endif [macWindow orderWindow:macAboveBelow relativeTo:otherNumber]; +#if 0 + fprintf(stderr, "new window order: "); PRINT_STACK; +#endif +#undef PRINT_STACK } /* diff --git a/tests/unixWm.test b/tests/unixWm.test index 5e6dd76ee3..e3b502919d 100644 --- a/tests/unixWm.test +++ b/tests/unixWm.test @@ -1974,9 +1974,9 @@ test unixWm-50.9 {Tk_CoordsToWindow procedure, unmapped windows} {unix failsOnUb update toplevel .t2 -width 200 -height 200 -bg red tkwait visibility .t2 - update wm geometry .t2 +20+20 update + after 300;# needed for macOS set result [list [winfo containing 120 120]] destroy .t2 update @@ -2069,6 +2069,7 @@ test unixWm-51.7 {TkWmRestackToplevel procedure, other window isn't mapped} {uni update toplevel $w -width 200 -height 200 -bg green tkwait visibility $w + after 300;# needed for macOS wm geometry $w +100+100 update } From 468bf9b8df08c68927e81183d6ece4c3a91c2887 Mon Sep 17 00:00:00 2001 From: culler Date: Tue, 25 Jun 2024 15:50:26 +0000 Subject: [PATCH 88/96] Fix VisibilityNotify in XMapWindow. --- macosx/tkMacOSXSubwindows.c | 25 ++++++++++++++++++------- tests/unixWm.test | 4 +--- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index 3da0492b37..2714de8bc8 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -177,11 +177,11 @@ XMapWindow( if (!Tk_IsEmbedded(winPtr)) { /* - * We want to activate Tk when a toplevel is mapped but we must not - * supply YES here. This is because during Tk initialization the - * root window is mapped before applicationDidFinishLaunching - * returns. Forcing the app to activate too early can make the menu - * bar unresponsive. + * We want to activate Tk when a toplevel is mapped but we can't + * always specify activateIgnoringOtherApps to be YES. This is + * because during Tk initialization the root window is mapped + * before applicationDidFinishLaunching returns. Forcing the app to + * activate too early can make the menu bar unresponsive. */ TkMacOSXApplyWindowAttributes(winPtr, win); @@ -190,8 +190,19 @@ XMapWindow( if (initialized) { if ([win canBecomeKeyWindow]) { [win makeKeyAndOrderFront:NSApp]; - } else { - [win orderFrontRegardless]; + } + + /* + * Delay for up to 20 milliseconds until the toplevel has actually become the + * highest toplevel. This is to ensure that the Visibility event occurs after + * the toplevel is visible. + */ + + for (int count = 0; count < 20; count++) { + if ([[NSApp orderedWindows] firstObject] == win) { + break; + } + [NSThread sleepForTimeInterval:.001]; } } diff --git a/tests/unixWm.test b/tests/unixWm.test index e3b502919d..4a88fd115b 100644 --- a/tests/unixWm.test +++ b/tests/unixWm.test @@ -1976,7 +1976,6 @@ test unixWm-50.9 {Tk_CoordsToWindow procedure, unmapped windows} {unix failsOnUb tkwait visibility .t2 wm geometry .t2 +20+20 update - after 300;# needed for macOS set result [list [winfo containing 120 120]] destroy .t2 update @@ -2069,7 +2068,6 @@ test unixWm-51.7 {TkWmRestackToplevel procedure, other window isn't mapped} {uni update toplevel $w -width 200 -height 200 -bg green tkwait visibility $w - after 300;# needed for macOS wm geometry $w +100+100 update } @@ -2117,8 +2115,8 @@ test unixWm-51.9 {TkWmRestackToplevel procedure, other window overrideredirect} update toplevel $w -width 200 -height 200 -bg green wm overrideredirect $w 1 - wm geometry $w +0+0 tkwait visibility $w + wm geometry $w +0+0 update } lower .t3 .t2 From f7a502702be5e43b4215f77dcf710b166537193a Mon Sep 17 00:00:00 2001 From: culler Date: Wed, 26 Jun 2024 03:46:04 +0000 Subject: [PATCH 89/96] Still fighting with unixWm. --- macosx/tkMacOSXSubwindows.c | 9 +++++---- tests/unixWm.test | 5 ++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index 2714de8bc8..771497c0b6 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -193,12 +193,13 @@ XMapWindow( } /* - * Delay for up to 20 milliseconds until the toplevel has actually become the - * highest toplevel. This is to ensure that the Visibility event occurs after - * the toplevel is visible. + * Delay for up to 20 milliseconds until the toplevel has + * actually become the highest toplevel. This is to ensure + * that the Visibility event occurs after the toplevel is + * visible. */ - for (int count = 0; count < 20; count++) { + for (int try = 0; try < 20; try++) { if ([[NSApp orderedWindows] firstObject] == win) { break; } diff --git a/tests/unixWm.test b/tests/unixWm.test index 4a88fd115b..e70242d6f5 100644 --- a/tests/unixWm.test +++ b/tests/unixWm.test @@ -1971,13 +1971,16 @@ test unixWm-50.9 {Tk_CoordsToWindow procedure, unmapped windows} {unix failsOnUb toplevel .t -width 200 -height 200 -bg green tkwait visibility .t wm geometry .t +20+20 + after 200 update toplevel .t2 -width 200 -height 200 -bg red tkwait visibility .t2 wm geometry .t2 +20+20 + after 200 update set result [list [winfo containing 120 120]] destroy .t2 + after 200 update lappend result [winfo containing 120 120] } {.t2 .t} @@ -2065,10 +2068,10 @@ test unixWm-51.6 {TkWmRestackToplevel procedure, window to be stacked isn't mapp test unixWm-51.7 {TkWmRestackToplevel procedure, other window isn't mapped} {unix failsOnXQuarz} { foreach w {.t .t2 .t3} { destroy $w - update toplevel $w -width 200 -height 200 -bg green tkwait visibility $w wm geometry $w +100+100 + after 200 update } update From 81291da33b1e6f1cab8020cb72dd43a974a0a20e Mon Sep 17 00:00:00 2001 From: culler Date: Wed, 26 Jun 2024 05:07:38 +0000 Subject: [PATCH 90/96] replace accidentally deleted line; move the root window our of the way of raise tests. --- macosx/tkMacOSXSubwindows.c | 2 ++ tests/raise.test | 1 + 2 files changed, 3 insertions(+) diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index 771497c0b6..5a253e78fa 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -190,6 +190,8 @@ XMapWindow( if (initialized) { if ([win canBecomeKeyWindow]) { [win makeKeyAndOrderFront:NSApp]; + } else { + [win orderFrontRegardless]; } /* diff --git a/tests/raise.test b/tests/raise.test index 2e9e2c6c2d..7e6b0bd3ef 100644 --- a/tests/raise.test +++ b/tests/raise.test @@ -16,6 +16,7 @@ namespace import -force tcltest::test # Procedure to create a bunch of overlapping windows, which should # make it easy to detect differences in order. +wm geometry . +400+400 proc raise_setup {} { destroy {*}[winfo children .raise] update idletasks From cf5c1014eabdc1f16d5d92821a8c665f8c622e6f Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Thu, 4 Jul 2024 18:20:32 +0000 Subject: [PATCH 91/96] Re-apply [37ba4f8cdb]: In test-cases same optimization as everywhere else: KeyPress -> Key and ButtonPress -> Button. This somehow got lost in this branch --- tests/event.test | 68 ++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/tests/event.test b/tests/event.test index 80580698c2..30d40b9ed3 100644 --- a/tests/event.test +++ b/tests/event.test @@ -1,9 +1,9 @@ # This file is a Tcl script to test the code in tkEvent.c. It is # organized in the standard fashion for Tcl tests. # -# Copyright (c) 1994 The Regents of the University of California. -# Copyright (c) 1994-1995 Sun Microsystems, Inc. -# Copyright (c) 1998-1999 by Scriptics Corporation. +# Copyright © 1994 The Regents of the University of California. +# Copyright © 1994-1995 Sun Microsystems, Inc. +# Copyright © 1998-1999 Scriptics Corporation. # All rights reserved. package require tcltest 2.2 @@ -88,8 +88,8 @@ proc _init_keypress_lookup {} { # Lookup an event in the keypress table. # For example: # Q -> Q -# . -> period -# / -> slash +# ; -> semicolon +# > -> greater # Delete -> Delete # Escape -> Escape @@ -111,7 +111,7 @@ proc _keypress_lookup {char} { } } -# Lookup and generate a pair of KeyPress and KeyRelease events +# Lookup and generate a pair of Key and KeyRelease events proc _keypress {win key} { set keysym [_keypress_lookup $key] @@ -124,7 +124,7 @@ proc _keypress {win key} { if {[focus] != $win} { focus -force $win } - event generate $win + event generate $win _pause 50 if {[focus] != $win} { focus -force $win @@ -194,10 +194,10 @@ test event-1.1 {Tk_HandleEvent procedure, filter events for dead windows} -setup update bind .b { lappend x destroy - event generate .b <1> + event generate .b event generate .b } - bind .b <1> { + bind .b { lappend x button } @@ -269,7 +269,7 @@ test event-2.3(keypress) {type into entry widget, triple click, hit Delete key, event generate $e for {set i 0} {$i < 3} {incr i} { _pause 100 - event generate $e + event generate $e _pause 100 event generate $e } @@ -324,7 +324,7 @@ test event-2.6(keypress) {type into text widget, triple click, event generate $e for {set i 0} {$i < 3} {incr i} { _pause 100 - event generate $e + event generate $e _pause 100 event generate $e } @@ -356,7 +356,7 @@ test event-3.1(click-drag) {click and drag in a text widget, this tests # Click down to set the insert cursor position event generate $e - event generate $e -x $anchor_x -y $anchor_y + event generate $e -x $anchor_x -y $anchor_y # Save the position of the insert cursor lappend result [$e index insert] @@ -382,7 +382,7 @@ test event-3.1(click-drag) {click and drag in a text widget, this tests # Now click and click and drag to the left, over "Tcl/Tk selection" - event generate $e -x $current_x -y $current_y + event generate $e -x $current_x -y $current_y while {[$e compare $current >= [list $anchor - 4 char]]} { foreach {current_x current_y} [_text_ind_to_x_y $e $current] break @@ -423,7 +423,7 @@ test event-3.1(click-drag) {click and drag in a text widget, this tests # Click down to set the insert cursor position event generate $e - event generate $e -x $anchor_x -y $anchor_y + event generate $e -x $anchor_x -y $anchor_y # Save the position of the insert cursor lappend result [$e index insert] @@ -449,7 +449,7 @@ test event-3.1(click-drag) {click and drag in a text widget, this tests # Now click and click and drag to the left, over "Tcl/Tk selection" - event generate $e -x $current_x -y $current_y + event generate $e -x $current_x -y $current_y while {$current >= ($anchor - 4)} { foreach {current_x current_y} [_text_ind_to_x_y $e $current] break @@ -488,11 +488,11 @@ test event-4.1(double-click-drag) {click down, click up, click down again, # Click down, release, then click down again event generate $e - event generate $e -x $anchor_x -y $anchor_y + event generate $e -x $anchor_x -y $anchor_y _pause 50 event generate $e -x $anchor_x -y $anchor_y _pause 50 - event generate $e -x $anchor_x -y $anchor_y + event generate $e -x $anchor_x -y $anchor_y _pause 50 # Save the highlighted text @@ -559,11 +559,11 @@ test event-4.2(double-click-drag) {click down, click up, click down again, # Click down, release, then click down again event generate $e - event generate $e -x $anchor_x -y $anchor_y + event generate $e -x $anchor_x -y $anchor_y _pause 50 event generate $e -x $anchor_x -y $anchor_y _pause 50 - event generate $e -x $anchor_x -y $anchor_y + event generate $e -x $anchor_x -y $anchor_y _pause 50 set result [list] @@ -631,17 +631,17 @@ test event-5.1(triple-click-drag) {Triple click and drag across lines in a event generate $e - event generate $e -x $anchor_x -y $anchor_y + event generate $e -x $anchor_x -y $anchor_y _pause 50 event generate $e -x $anchor_x -y $anchor_y _pause 50 - event generate $e -x $anchor_x -y $anchor_y + event generate $e -x $anchor_x -y $anchor_y _pause 50 event generate $e -x $anchor_x -y $anchor_y _pause 50 - event generate $e -x $anchor_x -y $anchor_y + event generate $e -x $anchor_x -y $anchor_y _pause 50 set result [list] @@ -681,7 +681,7 @@ test event-6.1(button-state) {button press in a window that is then } -body { set t [toplevel .t] - event generate $t + event generate $t destroy $t set t [toplevel .t] set motion nomotion @@ -720,11 +720,11 @@ test event-7.1(double-click) {A double click on a lone character # Double click near left hand egde of the letter A event generate $e - event generate $e -x $left_x -y $left_y + event generate $e -x $left_x -y $left_y _pause 50 event generate $e -x $left_x -y $left_y _pause 50 - event generate $e -x $left_x -y $left_y + event generate $e -x $left_x -y $left_y _pause 50 event generate $e -x $left_x -y $left_y _pause 50 @@ -735,18 +735,18 @@ test event-7.1(double-click) {A double click on a lone character # Clear selection by clicking at 0,0 - event generate $e -x 0 -y 0 + event generate $e -x 0 -y 0 _pause 50 event generate $e -x 0 -y 0 _pause 50 # Double click near right hand edge of the letter A - event generate $e -x $right_x -y $right_y + event generate $e -x $right_x -y $right_y _pause 50 event generate $e -x $right_x -y $right_y _pause 50 - event generate $e -x $right_x -y $right_y + event generate $e -x $right_x -y $right_y _pause 50 event generate $e -x $right_x -y $right_y _pause 50 @@ -787,11 +787,11 @@ test event-7.2(double-click) {A double click on a lone character # Double click near left hand egde of the letter A event generate $e - event generate $e -x $left_x -y $left_y + event generate $e -x $left_x -y $left_y _pause 50 event generate $e -x $left_x -y $left_y _pause 50 - event generate $e -x $left_x -y $left_y + event generate $e -x $left_x -y $left_y _pause 50 event generate $e -x $left_x -y $left_y _pause 50 @@ -802,18 +802,18 @@ test event-7.2(double-click) {A double click on a lone character # Clear selection by clicking at 0,0 - event generate $e -x 0 -y 0 + event generate $e -x 0 -y 0 _pause 50 event generate $e -x 0 -y 0 _pause 50 # Double click near right hand edge of the letter A - event generate $e -x $right_x -y $right_y + event generate $e -x $right_x -y $right_y _pause 50 event generate $e -x $right_x -y $right_y _pause 50 - event generate $e -x $right_x -y $right_y + event generate $e -x $right_x -y $right_y _pause 50 event generate $e -x $right_x -y $right_y _pause 50 @@ -837,7 +837,7 @@ test event-8 {event generate with keysyms corresponding to set e [entry $t.e] pack $e tkwait visibility $e - bind $e {lappend res keycode: %k keysym: %K} + bind $e {lappend res keycode: %k keysym: %K} focus -force $e update event generate $e From 0d691b8a32629beadc13ba23ce0b64185642bade Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Thu, 4 Jul 2024 20:14:36 +0000 Subject: [PATCH 92/96] Add back testcases event-9.1/9.2 (they were removed in [bf6234fc] because they are superseded by event-9.13 with more complete scope, see discussion in ticket [22349fc78a], but they can be kept with no harm). Restore keypress_lookup array to state in trunk (no need for so many entries any more) --- tests/event.test | 111 ++++++++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 49 deletions(-) diff --git a/tests/event.test b/tests/event.test index 30d40b9ed3..b43f2d3565 100644 --- a/tests/event.test +++ b/tests/event.test @@ -20,57 +20,10 @@ namespace import -force tcltest::test proc _init_keypress_lookup {} { global keypress_lookup - scan A %c start - scan Z %c finish - - for {set i $start} {$i <= $finish} {incr i} { - set l [format %c $i] - set keypress_lookup($l) $l - } - - scan a %c start - scan z %c finish - - for {set i $start} {$i <= $finish} {incr i} { - set l [format %c $i] - set keypress_lookup($l) $l - } - - scan 0 %c start - scan 9 %c finish - - for {set i $start} {$i <= $finish} {incr i} { - set l [format %c $i] - set keypress_lookup($l) $l - } - - # Most punctuation - array set keypress_lookup { - ! exclam - % percent - & ampersand - ( parenleft - ) parenright - * asterisk - + plus - , comma - - minus - . period - / slash - : colon - < less - = equal - > greater - ? question - @ at - ^ asciicircum - _ underscore - | bar - ~ asciitilde - ' apostrophe - } # Characters with meaning to Tcl... array set keypress_lookup [list \ + - minus \ + > greater \ \" quotedbl \ \# numbersign \ \$ dollar \ @@ -81,6 +34,7 @@ proc _init_keypress_lookup {} { \{ braceleft \ \} braceright \ " " space \ + \xA0 nobreakspace \ "\n" Return \ "\t" Tab] } @@ -862,6 +816,65 @@ test event-8 {event generate with keysyms corresponding to deleteWindows } -result {OK} +test event-9.1 {enter . window by destroying a toplevel - bug b1d115fa60} -setup { + set EnterBind [bind . ] +} -body { + wm geometry . 200x200+300+300 + wm deiconify . + _pause 200 + toplevel .top2 -width 200 -height 200 + wm geometry .top2 +[expr {[winfo rootx .]+50}]+[expr {[winfo rooty .]+50}] + update idletasks + wm deiconify .top2 + update idletasks + raise .top2 + _pause 400 + event generate .top2 -warp 1 -x 50 -y 50 + _pause 100 + bind . {lappend res %W} + set res [list ] + destroy .top2 + update idletasks + _pause 200 + set res +} -cleanup { + deleteWindows + bind . $EnterBind +} -result {.} +test event-9.2 {enter toplevel window by destroying a toplevel - bug b1d115fa60} -setup { + set iconified false + if {[winfo ismapped .]} { + wm iconify . + update + set iconified true + } +} -body { + toplevel .top1 + wm geometry .top1 200x200+300+300 + wm deiconify .top1 + _pause 200 + toplevel .top2 -width 200 -height 200 + wm geometry .top2 +[expr {[winfo rootx .top1]+50}]+[expr {[winfo rooty .top1]+50}] + _pause 200 + wm deiconify .top2 + update idletasks + raise .top2 + _pause 400 + event generate .top2 -warp 1 -x 50 -y 50 + _pause 100 + bind .top1 {lappend res %W} + set res [list ] + destroy .top2 + _pause 200 + set res +} -cleanup { + deleteWindows ; # destroy all children of ".", this already includes .top1 + if {$iconified} { + wm deiconify . + update + } +} -result {.top1} + proc waitForWindowEvent {w event {timeout 1000}} { # This proc is intended to overcome latency of windowing system # notifications when toplevel windows are involved. These latencies vary From 9f1eab15da8fe559d9e6448f80063185649108c7 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Thu, 4 Jul 2024 20:36:47 +0000 Subject: [PATCH 93/96] Fix 2 (minor) compiler warnings --- macosx/tkMacOSXColor.c | 6 ------ macosx/tkMacOSXSubwindows.c | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/macosx/tkMacOSXColor.c b/macosx/tkMacOSXColor.c index 436597f492..ffaadd93b3 100644 --- a/macosx/tkMacOSXColor.c +++ b/macosx/tkMacOSXColor.c @@ -655,12 +655,6 @@ TkpGetColor( CGFloat rgba[4]; #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 if (@available(macOS 10.14, *)) { - NSAppearance *windowAppearance; - if (view) { - windowAppearance = [view effectiveAppearance]; - } else { - windowAppearance = [NSApp effectiveAppearance]; - } if ([windowAppearance name] == NSAppearanceNameDarkAqua) { colormap = darkColormap; } else { diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index 0d712365ff..1d4ffec089 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -72,7 +72,7 @@ XDestroyWindow( if (!Tk_IsTopLevel(macWin->winPtr)) { if (macWin->winPtr->parentPtr != NULL) { - TkMacOSXInvalClipRgns(macWin->winPtr->parentPtr); + TkMacOSXInvalClipRgns((Tk_Window)macWin->winPtr->parentPtr); Tcl_CancelIdleCall(TkMacOSXRedrawViewIdleTask, (void *) view); Tcl_DoWhenIdle(TkMacOSXRedrawViewIdleTask, (void *) view); } From c79a0d12b108ff0c86c1313bbac7c08713d85ff1 Mon Sep 17 00:00:00 2001 From: culler Date: Sun, 7 Jul 2024 13:18:29 +0000 Subject: [PATCH 94/96] Update palette.tcl --- library/palette.tcl | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/library/palette.tcl b/library/palette.tcl index 90b499b383..d07f8941a2 100644 --- a/library/palette.tcl +++ b/library/palette.tcl @@ -189,6 +189,9 @@ proc ::tk_setPalette {args} { # which contains color information. Each element # is named after a widget configuration option, and # each value is the value for that option. +# Return Value: +# A list of commands which can be run to update +# the defaults database when exec'ed. proc ::tk::RecolorTree {w colors} { upvar $colors c @@ -200,11 +203,14 @@ proc ::tk::RecolorTree {w colors} { foreach dbOption [array names c] { set option -[string tolower $dbOption] set class [string replace $dbOption 0 0 [string toupper \ - [string index $dbOption 0]]] + [string index $dbOption 0]]] + # Make sure this option is valid for this window. if {![catch {$w configure $option} value]} { - # if the option database has a preference for this - # dbOption, then use it, otherwise use the defaults - # for the widget. + # Update the option for this window. + $w configure $option $c($dbOption) + # Retrieve a default value for this option. First check + # the option database. If it is not in the database use + # the value for the temporary prototype widget. set defaultcolor [option get $w $dbOption $class] if {$defaultcolor eq "" || \ ([info exists prototype] && \ @@ -214,16 +220,15 @@ proc ::tk::RecolorTree {w colors} { if {$defaultcolor ne ""} { set defaultcolor [winfo rgb . $defaultcolor] } - set chosencolor [lindex $value 4] - if {$chosencolor ne ""} { - set chosencolor [winfo rgb . $chosencolor] + # If the color requested for this option differs from + # the default, append a command to update the default. + set requestcolor [lindex $value 4] + if {$requestcolor ne ""} { + set requestcolor [winfo rgb . $requestcolor] } - if {[string match $defaultcolor $chosencolor]} { - # Change the option database so that future windows will get - # the same colors. + if {![string match $defaultcolor $requestcolor]} { append result ";\noption add [list \ *[winfo class $w].$dbOption $c($dbOption) 60]" - $w configure $option $c($dbOption) } } } From 6b412ef050bae794cc765a9d13a415a9912a8142 Mon Sep 17 00:00:00 2001 From: culler Date: Sun, 7 Jul 2024 14:26:18 +0000 Subject: [PATCH 95/96] Fix the black window when setting the docmodal bit in the stylemask, and some other black windows. --- macosx/tkMacOSXWm.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index 6eed3017d6..875b11ce0d 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -588,6 +588,8 @@ static void placeAsTab(TKWindow *macWindow) { - (void) tkLayoutChanged { syncLayout(self); + [[self contentView] setNeedsDisplay:YES]; + Tcl_DoWhenIdle(TkMacOSXDrawAllViews, NULL); } @end @@ -607,6 +609,8 @@ static void placeAsTab(TKWindow *macWindow) { - (void) tkLayoutChanged { syncLayout(self); + [[self contentView] setNeedsDisplay:YES]; + Tcl_DoWhenIdle(TkMacOSXDrawAllViews, NULL); } #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 @@ -1832,8 +1836,8 @@ WmSetAttribute( newFrame.size.height - heightDiff; [(TKWindow *)macWindow tkLayoutChanged]; if (heightDiff) { - //Calling XMoveResizeWindow twice is a hack to force a relayout - //of the window. + // Calling XMoveResizeWindow twice is a hack to force a relayout + // of the window. XMoveResizeWindow(winPtr->display, winPtr->window, winPtr->changes.x, winPtr->changes.y, newFrame.size.width, newHeight - 1); From d516c14a8643ea0b488683bd1b4b0e34c9a91f64 Mon Sep 17 00:00:00 2001 From: culler Date: Mon, 8 Jul 2024 01:26:26 +0000 Subject: [PATCH 96/96] Release the CGImageBitmapContext when the ContentView is destroyed. --- macosx/tkMacOSXWindowEvent.c | 3 ++- macosx/tkMacOSXWm.c | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 8065962d06..2915af155b 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -1352,7 +1352,8 @@ static const char *const accentNames[] = { (long)(self.tkLayerBitmapContext ? CFGetRetainCount(self.tkLayerBitmapContext) : LONG_MIN)); #endif - CGContextRelease(self.tkLayerBitmapContext); // will also need this in a destructor somewhere + // The context is also released in TkWmDeadWindow. + CGContextRelease(self.tkLayerBitmapContext); self.tkLayerBitmapContext = newCtx; } diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index 875b11ce0d..fbea56b188 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -1323,8 +1323,9 @@ TkWmDeadWindow( * Avoid redrawing the view after it is released. */ - Tcl_CancelIdleCall(TkMacOSXRedrawViewIdleTask, - (void *) [deadNSWindow contentView]); + TKContentView *deadView = [deadNSWindow contentView]; + Tcl_CancelIdleCall(TkMacOSXRedrawViewIdleTask,(void *) deadView); + CGContextRelease(deadView.tkLayerBitmapContext); [deadNSWindow close]; #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];