diff options
author | culler <culler> | 2024-07-08 02:07:57 (GMT) |
---|---|---|
committer | culler <culler> | 2024-07-08 02:07:57 (GMT) |
commit | 365059dd56e66d2056e28432b1420bfe676c260b (patch) | |
tree | c9226ff6937df277c893bfd24c4968fd3bfe4693 | |
parent | c2f8aeaa3b5abbf7ce84a9f4476a5592c0932ddf (diff) | |
parent | d516c14a8643ea0b488683bd1b4b0e34c9a91f64 (diff) | |
download | tk-365059dd56e66d2056e28432b1420bfe676c260b.zip tk-365059dd56e66d2056e28432b1420bfe676c260b.tar.gz tk-365059dd56e66d2056e28432b1420bfe676c260b.tar.bz2 |
Merge with cgimage_with_crossing branch.
47 files changed, 1592 insertions, 1043 deletions
diff --git a/generic/tkFont.c b/generic/tkFont.c index 1918d78..6a90add 100644 --- a/generic/tkFont.c +++ b/generic/tkFont.c @@ -887,17 +887,6 @@ TheWorldHasChanged( { TkFontInfo *fiPtr = (TkFontInfo *)clientData; - /* - * On macOS it is catastrophic to recompute all widgets while the - * [NSView drawRect] method is drawing. The best that we can do in - * that situation is to abort the recomputation and hope for the best. - * This is ignored on other platforms. - */ - - if (TkpWillDrawWidget(NULL)) { - return; - } - fiPtr->updatePending = 0; RecomputeWidgets(fiPtr->mainPtr->winPtr); } diff --git a/generic/tkFrame.c b/generic/tkFrame.c index 302542c..d49292b 100644 --- a/generic/tkFrame.c +++ b/generic/tkFrame.c @@ -1478,6 +1478,8 @@ DisplayFrame( 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/generic/tkTextDisp.c b/generic/tkTextDisp.c index 905a4c6..027efc8 100644 --- a/generic/tkTextDisp.c +++ b/generic/tkTextDisp.c @@ -24,7 +24,7 @@ #include "tkMacOSXInt.h" #endif -#define OK_TO_LOG (!TkpWillDrawWidget(textPtr->tkwin)) +#define OK_TO_LOG 1 /* * "Calculations of line pixel heights and the size of the vertical diff --git a/generic/tkWindow.c b/generic/tkWindow.c index 68f3406..9d36e9f 100644 --- a/generic/tkWindow.c +++ b/generic/tkWindow.c @@ -215,6 +215,17 @@ static int Initialize(Tcl_Interp *interp); static int NameWindow(Tcl_Interp *interp, TkWindow *winPtr, TkWindow *parentPtr, const char *name); static void UnlinkWindow(TkWindow *winPtr); + +/* + * 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; + /* *---------------------------------------------------------------------- @@ -239,6 +250,7 @@ static void TkCloseDisplay( TkDisplay *dispPtr) { + displayBeingClosed = 1; TkClipCleanup(dispPtr); if (dispPtr->name != NULL) { @@ -1334,6 +1346,39 @@ Tk_CreateWindowFromPath( *-------------------------------------------------------------- */ +#if defined(MAC_OSX_TK) || defined(_WIN32) +static void SendEnterLeaveForDestroy( + Tk_Window tkwin) +{ + int x, y; + unsigned int state; + Tk_Window pointerWin; + TkWindow *containerPtr; + + if (displayBeingClosed) { + return; + } + 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)) { + containerPtr = TkGetContainer((TkWindow *)pointerWin); + Tk_UpdatePointer((Tk_Window) containerPtr, x, y, state); + } + } + + if (pointerWin && (tkwin == Tk_Parent(pointerWin))) { + Tk_UpdatePointer(Tk_Parent(tkwin), x, y, state); + } +} +#else +static void SendEnterLeaveForDestroy( + TCL_UNUSED(Tk_Window)) +{ +} +#endif + void Tk_DestroyWindow( Tk_Window tkwin) /* Window to destroy. */ @@ -1353,6 +1398,10 @@ Tk_DestroyWindow( return; } + if ((winPtr->flags & TK_DONT_DESTROY_WINDOW) == 0) { + SendEnterLeaveForDestroy(tkwin); + } + winPtr->flags |= TK_ALREADY_DEAD; /* @@ -1523,7 +1572,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); @@ -2612,7 +2661,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/library/demos/mac_styles.tcl b/library/demos/mac_styles.tcl index 3fff03c..804b5c0 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/library/palette.tcl b/library/palette.tcl index 90b499b..d07f894 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) } } } diff --git a/macosx/README b/macosx/README index 64be140..c6159a6 100644 --- a/macosx/README +++ b/macosx/README @@ -477,9 +477,9 @@ https://developer.apple.com/library/mac/documentation/Cocoa/\ Reference/ApplicationKit/Classes/NSApplication_Class) void NSApplicationMain(int argc, char *argv[]) { - [NSApplication sharedApplication]; - [NSBundle loadNibNamed:@"myMain" owner:NSApp]; - [NSApp run]; + [NSApplication sharedApplication]; + [NSBundle loadNibNamed:@"myMain" owner:NSApp]; + [NSApp run]; } Here NSApp is a standard global variable, initialized by the OS, which points to an object in a subclass of NSApplication (called diff --git a/macosx/tkMacOSXClipboard.c b/macosx/tkMacOSXClipboard.c index f16ab0d..8d5f727 100644 --- a/macosx/tkMacOSXClipboard.c +++ b/macosx/tkMacOSXClipboard.c @@ -126,8 +126,8 @@ TkSelGetSelection( ([[NSPasteboard generalPasteboard] changeCount] != changeCount); if (dispPtr && (haveExternalClip || dispPtr->clipboardActive) - && selection == dispPtr->clipboardAtom - && (target == XA_STRING || target == dispPtr->utf8Atom)) { + && selection == dispPtr->clipboardAtom + && (target == XA_STRING || target == dispPtr->utf8Atom)) { NSString *string = nil; NSPasteboard *pb = [NSPasteboard generalPasteboard]; NSString *type = [pb availableTypeFromArray:[NSArray arrayWithObject: diff --git a/macosx/tkMacOSXColor.c b/macosx/tkMacOSXColor.c index 978b9d6..ffaadd9 100644 --- a/macosx/tkMacOSXColor.c +++ b/macosx/tkMacOSXColor.c @@ -52,7 +52,7 @@ static void initColorTable() #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 if (@available(macOS 10.14, *)) { darkAqua = [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua]; - lightAqua = [NSAppearance appearanceNamed:NSAppearanceNameAqua]; + lightAqua = [NSAppearance appearanceNamed:NSAppearanceNameAqua]; } #endif @@ -103,7 +103,7 @@ static void initColorTable() name = (char *)ckalloc(length + 1); strcpy(name, key.UTF8String); name[0] = (char)toupper(UCHAR(name[0])); - if (!strcmp(name, "WindowBackgroundColor")) { + if (!strcmp(name, "WindowBackgroundColor")) { /* * Avoid black windows on old systems. @@ -192,8 +192,8 @@ TkMacOSXRGBPixel( MacPixel p = {0}; p.pixel.colortype = rgbColor; p.pixel.value = (unsigned int)(((red & 0xff) << 16) | - ((green & 0xff) << 8) | - (blue & 0xff)); + ((green & 0xff) << 8) | + (blue & 0xff)); return p.ulong; } @@ -436,7 +436,7 @@ TkMacOSXInDarkMode(Tk_Window tkwin) #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 if (@available(macOS 10.14, *)) { - TkWindow *winPtr = (TkWindow*) tkwin; + TkWindow *winPtr = (TkWindow*) tkwin; NSAppearanceName name; NSView *view = nil; if (winPtr && winPtr->privatePtr) { @@ -611,6 +611,7 @@ TkpGetColor( XColor color; Colormap colormap = tkwin ? Tk_Colormap(tkwin) : noColormap; NSView *view = nil; + Bool haveValidXColor = False; static Bool initialized = NO; if (!initialized) { @@ -638,16 +639,22 @@ TkpGetColor( p.pixel.colortype = entry->type; p.pixel.value = (unsigned int)entry->index; color.pixel = p.ulong; + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 + NSAppearance *windowAppearance; + if (@available(macOS 10.14, *)) { + if (view) { + windowAppearance = [view effectiveAppearance]; + } else { + windowAppearance = [NSApp effectiveAppearance]; + } + } +#endif + if (entry->type == semantic) { 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 { @@ -671,13 +678,13 @@ TkpGetColor( } else { GetRGBA(entry, p.ulong, rgba); } -#else +#else //MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 GetRGBA(entry, p.ulong, rgba); -#endif color.red = (unsigned short)(rgba[0] * 65535.0); color.green = (unsigned short)(rgba[1] * 65535.0); color.blue = (unsigned short)(rgba[2] * 65535.0); - goto validXColor; +#endif //MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 + haveValidXColor = True; } else if (SetCGColorComponents(entry, 0, &c)) { const size_t n = CGColorGetNumberOfComponents(c); const CGFloat *rgba = CGColorGetComponents(c); @@ -695,15 +702,26 @@ 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; + } + } } +#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 ff35da3..8ebdde4 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -264,7 +264,8 @@ Tk_MacOSXGetCGContextForDrawable( colorspace = CGColorSpaceCreateDeviceRGB(); bitmapInfo |= kCGImageAlphaPremultipliedFirst; } - macDraw->context = CGBitmapContextCreate(NULL, (unsigned)macDraw->size.width, + macDraw->context = CGBitmapContextCreate(NULL, + (unsigned)macDraw->size.width, (unsigned)macDraw->size.height, bitsPerComponent, 0, colorspace, bitmapInfo); if (macDraw->context) { @@ -283,7 +284,8 @@ Tk_MacOSXGetCGContextForDrawable( * * 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. @@ -302,25 +304,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) { @@ -369,9 +357,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"); } @@ -434,16 +419,16 @@ XDrawLines( } } - /* - * In the case of closed polylines, the first and last points are the - * same. We want miter or bevel join be rendered also at this point, - * this needs telling CoreGraphics that the path is closed. - */ + /* + * In the case of closed polylines, the first and last points are the + * same. We want miter or bevel join be rendered also at this point, + * this needs telling CoreGraphics that the path is closed. + */ - if ((points[0].x == points[npoints-1].x) && - (points[0].y == points[npoints-1].y)) { - CGContextClosePath(dc.context); - } + if ((points[0].x == points[npoints-1].x) && + (points[0].y == points[npoints-1].y)) { + CGContextClosePath(dc.context); + } CGContextStrokePath(dc.context); } TkMacOSXRestoreDrawingContext(&dc); @@ -1093,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. @@ -1103,36 +1105,21 @@ XFillArcs( int TkScrollWindow( Tk_Window tkwin, /* The window to be scrolled. */ - TCL_UNUSED(GC), /* GC for window to be scrolled. */ + GC gc, /* GC for window to be scrolled. */ int x, int y, /* Position rectangle to be scrolled. */ int width, int height, int dx, int dy, /* Distance rectangle should be moved. */ Region 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 (view) { - - /* - * Get the scroll area in NSView coordinates (origin at bottom left). - */ - - bounds = [view bounds]; - viewSrcRect = NSMakeRect(macDraw->xOff + x, - bounds.size.height - height - (macDraw->yOff + y), - width, height); - - /* - * Scroll the rectangle. - */ - - [view scrollRect:viewSrcRect by:NSMakeSize(dx, -dy)]; + // 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) { /* * Compute the damage region, using Tk coordinates (origin at top left). @@ -1237,14 +1224,19 @@ 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)) { + /* + * 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 /* * 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. @@ -1252,53 +1244,67 @@ TkMacOSXSetupDrawingContext( dc.context = TkMacOSXGetCGContextForDrawable(d); if (!dc.context) { - NSRect drawingBounds, currentBounds; dc.view = view; - dc.context = GET_CGCONTEXT; + dc.context = view.tkLayerBitmapContext; if (dc.clipRgn) { CGRect clipBounds; CGAffineTransform t = { .a = 1, .b = 0, .c = 0, .d = -1, .tx = 0, .ty = [view bounds].size.height}; HIShapeGetBounds(dc.clipRgn, &clipBounds); clipBounds = CGRectApplyAffineTransform(clipBounds, t); - drawingBounds = NSRectFromCGRect(clipBounds); - } else { - drawingBounds = [view bounds]; } /* - * 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. + * Mark the view as needing to be redisplayed, since we are drawing + * to its backing layer. */ - if (view != [NSView focusView]) { - [view addTkDirtyRect:drawingBounds]; - canDraw = false; - goto end; - } + [view setTkNeedsDisplay:YES]; /* - * 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. + * 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. */ - currentBounds = NSRectFromCGRect(CGContextGetClipBoundingBox(dc.context)); - if (!NSContainsRect(currentBounds, drawingBounds)) { - [view addTkDirtyRect:drawingBounds]; + 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 } } @@ -1324,6 +1330,7 @@ TkMacOSXSetupDrawingContext( }; CGContextConcatCTM(dc.context, t); } +//#if 0 // disable clipping if (dc.clipRgn) { #ifdef TK_MAC_DEBUG_DRAWING @@ -1369,6 +1376,8 @@ TkMacOSXSetupDrawingContext( CGContextClipToRect(dc.context, r); } } +//#endif //disable clipping + if (gc) { static const CGLineCap cgCap[] = { [CapNotLast] = kCGLineCapButt, @@ -1438,6 +1447,8 @@ end: dc.clipRgn = NULL; } *dcPtr = dc; + // The goal is to allow immediate drawing; canDraw == 0 should happen far less often. + if (0) fprintf(stderr, "tkmacosxsdc canDraw %d\n", canDraw); return canDraw; } @@ -1524,10 +1535,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; } @@ -1537,7 +1551,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. @@ -1566,7 +1582,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/tkMacOSXEmbed.c b/macosx/tkMacOSXEmbed.c index ef4f44d..f95d5d1 100644 --- a/macosx/tkMacOSXEmbed.c +++ b/macosx/tkMacOSXEmbed.c @@ -850,12 +850,12 @@ EmbedStructureProc( if (eventPtr->type == ConfigureNotify) { /* - * Send a ConfigureNotify to the embedded application. - */ + * Send a ConfigureNotify to the embedded application. + */ - if (containerPtr->embeddedPtr != NULL) { - TkDoConfigureNotify(containerPtr->embeddedPtr); - } + if (containerPtr->embeddedPtr != NULL) { + TkDoConfigureNotify(containerPtr->embeddedPtr); + } if (containerPtr->embedded != None) { /* * Ignore errors, since the embedded application could have diff --git a/macosx/tkMacOSXEvent.c b/macosx/tkMacOSXEvent.c index 4f36915..63493bc 100644 --- a/macosx/tkMacOSXEvent.c +++ b/macosx/tkMacOSXEvent.c @@ -35,7 +35,7 @@ enum { switch ((NSInteger)type) { case NSAppKitDefined: - subtype = [theEvent subtype]; + subtype = [theEvent subtype]; switch (subtype) { /* Ignored at the moment. */ @@ -49,11 +49,11 @@ enum { break; case NSWindowMovedEventType: break; - case NSWindowWillMoveEventType: - break; + case NSWindowWillMoveEventType: + break; - default: - break; + default: + break; } break; /* AppkitEvent. Return theEvent */ case NSKeyUp: @@ -80,7 +80,7 @@ enum { break; /* Mouse event. Return the processed event. */ #if 0 case NSSystemDefined: - subtype = [theEvent subtype]; + subtype = [theEvent subtype]; break; case NSApplicationDefined: { id win; @@ -88,14 +88,14 @@ enum { break; } case NSCursorUpdate: - break; + break; case NSEventTypeGesture: case NSEventTypeMagnify: case NSEventTypeRotate: case NSEventTypeSwipe: case NSEventTypeBeginGesture: case NSEventTypeEndGesture: - break; + break; #endif default: diff --git a/macosx/tkMacOSXFileTypes.c b/macosx/tkMacOSXFileTypes.c index 5df7ddd..acf2da9 100644 --- a/macosx/tkMacOSXFileTypes.c +++ b/macosx/tkMacOSXFileTypes.c @@ -26,9 +26,9 @@ without generating deprecation warnings. #include "tkMacOSXPrivate.h" #define CHARS_TO_OSTYPE(string) (OSType) string[0] << 24 | \ - (OSType) string[1] << 16 | \ - (OSType) string[2] << 8 | \ - (OSType) string[3] + (OSType) string[1] << 16 | \ + (OSType) string[2] << 8 | \ + (OSType) string[3] MODULE_SCOPE NSString *TkMacOSXOSTypeToUTI(OSType ostype) { char string[5]; diff --git a/macosx/tkMacOSXHLEvents.c b/macosx/tkMacOSXHLEvents.c index 8e1fff5..0d07fdf 100644 --- a/macosx/tkMacOSXHLEvents.c +++ b/macosx/tkMacOSXHLEvents.c @@ -307,34 +307,34 @@ static const char getSdefProc[] = "::tk::mac::GetDynamicSdef"; /* * This descriptor can be coerced to a file url. Construct a Tcl * expression which passes the file path as a string argument to - * ::tk::mac::DoScriptFile. + * ::tk::mac::DoScriptFile. */ if (noErr == AEGetParamPtr(theDesc, keyDirectObject, typeFileURL, &type, - (Ptr) URLBuffer, URL_MAX_LENGTH, &actual)) { - if (actual > 0) { - URLBuffer[actual] = '\0'; - NSString *urlString = [NSString stringWithUTF8String:(char*)URLBuffer]; - NSURL *fileURL = [NSURL URLWithString:urlString]; - AppleEventInfo *AEInfo = (AppleEventInfo *)ckalloc(sizeof(AppleEventInfo)); - Tcl_DString *scriptFileCommand = &AEInfo->command; - Tcl_DStringInit(scriptFileCommand); - Tcl_DStringAppend(scriptFileCommand, scriptFileProc, TCL_INDEX_NONE); - Tcl_DStringAppendElement(scriptFileCommand, [[fileURL path] UTF8String]); - AEInfo->interp = _eventInterp; - AEInfo->procedure = scriptFileProc; - AEInfo->replyEvent = nil; + (Ptr) URLBuffer, URL_MAX_LENGTH, &actual)) { + if (actual > 0) { + URLBuffer[actual] = '\0'; + NSString *urlString = [NSString stringWithUTF8String:(char*)URLBuffer]; + NSURL *fileURL = [NSURL URLWithString:urlString]; + AppleEventInfo *AEInfo = (AppleEventInfo *)ckalloc(sizeof(AppleEventInfo)); + Tcl_DString *scriptFileCommand = &AEInfo->command; + Tcl_DStringInit(scriptFileCommand); + Tcl_DStringAppend(scriptFileCommand, scriptFileProc, TCL_INDEX_NONE); + Tcl_DStringAppendElement(scriptFileCommand, [[fileURL path] UTF8String]); + AEInfo->interp = _eventInterp; + AEInfo->procedure = scriptFileProc; + AEInfo->replyEvent = nil; AEInfo->retryCount = 0; - ProcessAppleEvent((void *)AEInfo); - } - } + ProcessAppleEvent((void *)AEInfo); + } + } } else if (noErr == AEGetParamPtr(theDesc, keyDirectObject, typeUTF8Text, &type, NULL, 0, &actual)) { - /* - * The descriptor cannot be coerced to a file URL but can be coerced to - * text. Construct a Tcl expression which passes the text as a string - * argument to ::tk::mac::DoScriptText. - */ + /* + * The descriptor cannot be coerced to a file URL but can be coerced to + * text. Construct a Tcl expression which passes the text as a string + * argument to ::tk::mac::DoScriptText. + */ if (actual > 0) { char *data = (char *)ckalloc(actual + 1); @@ -350,23 +350,23 @@ static const char getSdefProc[] = "::tk::mac::GetDynamicSdef"; AEInfo->interp = _eventInterp; AEInfo->procedure = scriptTextProc; AEInfo->retryCount = 0; - if (Tcl_FindCommand(AEInfo->interp, AEInfo->procedure, NULL, 0)) { - AEInfo->replyEvent = replyEvent; - ProcessAppleEvent(AEInfo); - } else { - AEInfo->replyEvent = nil; - ProcessAppleEvent(AEInfo); - } + if (Tcl_FindCommand(AEInfo->interp, AEInfo->procedure, NULL, 0)) { + AEInfo->replyEvent = replyEvent; + ProcessAppleEvent(AEInfo); + } else { + AEInfo->replyEvent = nil; + ProcessAppleEvent(AEInfo); + } } } } } - (void)handleURLEvent:(NSAppleEventDescriptor*)event - withReplyEvent:(NSAppleEventDescriptor*)replyEvent + withReplyEvent:(NSAppleEventDescriptor*)replyEvent { NSString* url = [[event paramDescriptorForKeyword:keyDirectObject] - stringValue]; + stringValue]; const char *cURL=[url UTF8String]; AppleEventInfo *AEInfo = (AppleEventInfo *)ckalloc(sizeof(AppleEventInfo)); Tcl_DString *launchCommand = &AEInfo->command; @@ -456,18 +456,18 @@ static void ProcessAppleEvent( Tcl_DStringLength(&AEInfo->command), TCL_EVAL_GLOBAL); if (AEInfo->replyEvent && code >= 0) { - Tcl_Size reslen; - const char *result = Tcl_GetStringFromObj(Tcl_GetObjResult(AEInfo->interp), - &reslen); - if (code == TCL_OK) { - AEPutParamPtr((AppleEvent*)[AEInfo->replyEvent aeDesc], - keyDirectObject, typeChar, result, reslen); - } else { - AEPutParamPtr((AppleEvent*)[AEInfo->replyEvent aeDesc], - keyErrorString, typeChar, result, reslen); - AEPutParamPtr((AppleEvent*)[AEInfo->replyEvent aeDesc], - keyErrorNumber, typeSInt32, (Ptr) &code, sizeof(int)); - } + Tcl_Size reslen; + const char *result = Tcl_GetStringFromObj(Tcl_GetObjResult(AEInfo->interp), + &reslen); + if (code == TCL_OK) { + AEPutParamPtr((AppleEvent*)[AEInfo->replyEvent aeDesc], + keyDirectObject, typeChar, result, reslen); + } else { + AEPutParamPtr((AppleEvent*)[AEInfo->replyEvent aeDesc], + keyErrorString, typeChar, result, reslen); + AEPutParamPtr((AppleEvent*)[AEInfo->replyEvent aeDesc], + keyErrorNumber, typeSInt32, (Ptr) &code, sizeof(int)); + } } else if (code != TCL_OK) { Tcl_BackgroundException(AEInfo->interp, code); } @@ -537,8 +537,8 @@ TkMacOSXInitAppleEvents( /* * We do not load our sdef dynamically but this event handler - * is required to silence error messages from inline execution - * of AppleScript at the Objective-C level. + * is required to silence error messages from inline execution + * of AppleScript at the Objective-C level. */ [aeManager setEventHandler:NSApp andSelector:@selector(handleGetSDEFEvent:withReplyEvent:) diff --git a/macosx/tkMacOSXImage.c b/macosx/tkMacOSXImage.c index 30cfc60..488f029 100644 --- a/macosx/tkMacOSXImage.c +++ b/macosx/tkMacOSXImage.c @@ -19,6 +19,10 @@ #include "tkColor.h" #include "xbytes.h" +static CGImageRef CreateCGImageFromPixmap(Drawable pixmap); +static CGImageRef CreateCGImageFromDrawableRect( Drawable drawable, int force_1x_scale, + int x, int y, unsigned int width, unsigned int height); + /* Pixel formats * * Tk uses the XImage structure defined in Xlib.h for storing images. The @@ -510,8 +514,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. @@ -521,12 +533,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"); @@ -620,6 +629,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 * @@ -629,6 +643,7 @@ int TkpPutRGBAImage( static CGImageRef CreateCGImageFromDrawableRect( Drawable drawable, + int force_1x_scale, int x, int y, unsigned int width, @@ -637,6 +652,7 @@ CreateCGImageFromDrawableRect( MacDrawable *mac_drawable = (MacDrawable *)drawable; CGContextRef cg_context = NULL; CGImageRef cg_image = NULL, result = NULL; + CGFloat scaleFactor = 1.0; if (mac_drawable->flags & TK_IS_PIXMAP) { cg_context = TkMacOSXGetCGContextForDrawable(drawable); CGContextRetain(cg_context); @@ -646,18 +662,9 @@ CreateCGImageFromDrawableRect( TkMacOSXDbgMsg("Invalid source drawable"); return NULL; } - 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]; + scaleFactor = view.layer.contentsScale; + cg_context = ((TKContentView *)view).tkLayerBitmapContext; + CGContextRetain(cg_context); } if (cg_context) { cg_image = CGBitmapContextCreateImage(cg_context); @@ -666,8 +673,30 @@ CreateCGImageFromDrawableRect( if (cg_image) { CGRect rect = CGRectMake(x + mac_drawable->xOff, y + mac_drawable->yOff, width, height); + rect = CGRectApplyAffineTransform(rect, CGAffineTransformMakeScale(scaleFactor, scaleFactor)); result = CGImageCreateWithImageInRect(cg_image, rect); CGImageRelease(cg_image); + 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); + } } return result; } @@ -799,7 +828,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]; @@ -890,9 +920,8 @@ XCopyArea( int dest_y) { TkMacOSXDrawingContext dc; - MacDrawable *srcDraw = (MacDrawable *)src; CGImageRef img = NULL; - CGRect bounds, srcRect, dstRect; + CGRect dstRect; LastKnownRequestProcessed(display)++; if (!width || !height) { @@ -909,20 +938,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."); @@ -967,7 +989,7 @@ XCopyPlane( TkMacOSXDrawingContext dc; MacDrawable *srcDraw = (MacDrawable *)src; MacDrawable *dstDraw = (MacDrawable *)dst; - CGRect bounds, srcRect, dstRect; + CGRect srcRect, dstRect; LastKnownRequestProcessed(display)++; if (!width || !height) { /* TkMacOSXDbgMsg("Drawing of empty area requested"); */ @@ -990,7 +1012,7 @@ XCopyPlane( TkpClipMask *clipPtr = (TkpClipMask *) gc->clip_mask; unsigned long imageBackground = gc->background; - if (clipPtr && clipPtr->type == TKP_CLIP_PIXMAP) { + if (clipPtr && clipPtr->type == TKP_CLIP_PIXMAP) { srcRect = CGRectMake(src_x, src_y, width, height); CGImageRef mask = CreateCGImageFromPixmap( clipPtr->value.pixmap); @@ -1033,13 +1055,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 { @@ -1369,7 +1387,7 @@ TkMacOSXNSImageConfigureModel( case NAME_SOURCE: Tcl_SetObjResult(interp, Tcl_NewStringObj("Unknown named NSImage.\n" "Try omitting ImageName, " - "e.g. use NSCaution for NSImageNameCaution.", TCL_INDEX_NONE)); + "e.g. use NSCaution for NSImageNameCaution.", TCL_INDEX_NONE)); Tcl_SetErrorCode(interp, "TK", "IMAGE", "SYSTEM", "BAD_VALUE", NULL); goto errorExit; case FILE_SOURCE: @@ -1473,9 +1491,9 @@ TkMacOSXNSImageObjCmd( objPtr = Tk_GetOptionValue(interp, (char *)modelPtr, optionTable, objv[2], NULL); if (objPtr == NULL) { - goto error; - } - Tcl_SetObjResult(interp, objPtr); + goto error; + } + Tcl_SetObjResult(interp, objPtr); break; case CONFIGURE: if (objc == 2) { diff --git a/macosx/tkMacOSXInit.c b/macosx/tkMacOSXInit.c index 03a05fc..bbd41ee 100644 --- a/macosx/tkMacOSXInit.c +++ b/macosx/tkMacOSXInit.c @@ -41,8 +41,6 @@ static Tcl_ObjCmdProc TkMacOSVersionObjCmd; @implementation TKApplication @synthesize poolLock = _poolLock; @synthesize macOSVersion = _macOSVersion; -@synthesize isDrawing = _isDrawing; -@synthesize isSigned = _isSigned; @synthesize tkLiveResizeEnded = _tkLiveResizeEnded; @synthesize tkPointerWindow = _tkPointerWindow; - (void) setTkPointerWindow: (TkWindow *)winPtr @@ -290,12 +288,6 @@ static Tcl_ObjCmdProc TkMacOSVersionObjCmd; [NSApp setMacOSVersion: 10000*majorVersion + 100*minorVersion]; /* - * We are not drawing right now. - */ - - [NSApp setIsDrawing:NO]; - - /* * Be our own delegate. */ @@ -394,7 +386,7 @@ static void closePanels( [[NSFontPanel sharedFontPanel] orderOut:nil]; } if ([NSColorPanel sharedColorPanelExists]) { - [[NSColorPanel sharedColorPanel] orderOut:nil]; + [[NSColorPanel sharedColorPanel] orderOut:nil]; } } @@ -431,6 +423,18 @@ TCL_NORETURN void TkpExitProc( } /* + * 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. */ @@ -471,7 +475,7 @@ TkpInit( if (!initialized) { struct stat st; Bool shouldOpenConsole = NO; - Bool stdinIsNullish = (!isatty(0) && + Bool stdinIsNullish = (!isatty(0) && (fstat(0, &st) || (S_ISCHR(st.st_mode) && st.st_blocks == 0))); /* @@ -495,7 +499,7 @@ TkpInit( if (Tcl_MacOSXOpenVersionedBundleResources(interp, "com.tcltk.tklibrary", TK_FRAMEWORK_VERSION, 0, PATH_MAX, tkLibPath) != TCL_OK) { - # if 0 /* This is not really an error. Wish still runs fine. */ + # if 0 /* This is not really an error. Wish still runs fine. */ TkMacOSXDbgMsg("Tcl_MacOSXOpenVersionedBundleResources failed"); # endif } @@ -517,40 +521,40 @@ TkpInit( [TKApplication sharedApplication]; [pool drain]; - /* - * WARNING: The finishLaunching method runs asynchronously. This - * creates a race between the initialization of the NSApplication and - * the initialization of Tk. If Tk wins the race bad things happen - * with the root window (see below). If the NSApplication wins then an - * AppleEvent created during launch, e.g. by dropping a file icon on - * the application icon, will be delivered before the procedure meant - * to to handle the AppleEvent has been defined. This is handled in - * tkMacOSXHLEvents.c by scheduling a timer event to handle the - * AppleEvent later, after the required procedure has been defined. - */ + /* + * WARNING: The finishLaunching method runs asynchronously. This + * creates a race between the initialization of the NSApplication and + * the initialization of Tk. If Tk wins the race bad things happen + * with the root window (see below). If the NSApplication wins then an + * AppleEvent created during launch, e.g. by dropping a file icon on + * the application icon, will be delivered before the procedure meant + * to to handle the AppleEvent has been defined. This is handled in + * tkMacOSXHLEvents.c by scheduling a timer event to handle the + * AppleEvent later, after the required procedure has been defined. + */ [NSApp _setup:interp]; [NSApp finishLaunching]; - /* - * Create a Tk event source based on the Appkit event queue. - */ + /* + * Create a Tk event source based on the Appkit event queue. + */ Tk_MacOSXSetupTkNotifier(); /* * If Tk initialization wins the race, the root window is mapped before - * the NSApplication is initialized. This can cause bad things to - * happen. The root window can open off screen with no way to make it - * appear on screen until the app icon is clicked. This will happen if - * a Tk application opens a modal window in its startup script (see - * ticket 56a1823c73). In other cases, an empty root window can open - * on screen and remain visible for a noticeable amount of time while - * the Tk initialization finishes (see ticket d1989fb7cf). The call - * below forces Tk to block until the Appkit event queue has been - * created. This seems to be sufficient to ensure that the - * NSApplication initialization wins the race, avoiding these bad - * window behaviors. + * the NSApplication is initialized. This can cause bad things to + * happen. The root window can open off screen with no way to make it + * appear on screen until the app icon is clicked. This will happen if + * a Tk application opens a modal window in its startup script (see + * ticket 56a1823c73). In other cases, an empty root window can open + * on screen and remain visible for a noticeable amount of time while + * the Tk initialization finishes (see ticket d1989fb7cf). The call + * below forces Tk to block until the Appkit event queue has been + * created. This seems to be sufficient to ensure that the + * NSApplication initialization wins the race, avoiding these bad + * window behaviors. */ Tcl_DoOneEvent(TCL_WINDOW_EVENTS | TCL_DONT_WAIT); @@ -675,7 +679,7 @@ TkpInit( Tcl_CreateObjCommand(interp, "::tk::mac::GetAppPath", TkMacOSXGetAppPathObjCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "::tk::mac::macOSVersion", - TkMacOSVersionObjCmd, NULL, NULL); + TkMacOSVersionObjCmd, NULL, NULL); MacSystrayInit(interp); MacPrint_Init(interp); diff --git a/macosx/tkMacOSXKeyEvent.c b/macosx/tkMacOSXKeyEvent.c index a16eb42..42698ca 100644 --- a/macosx/tkMacOSXKeyEvent.c +++ b/macosx/tkMacOSXKeyEvent.c @@ -64,8 +64,8 @@ static NSUInteger textInputModifiers; static NSMutableArray *nsEvArray = nil; if (nsEvArray == nil) { - nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1]; - processingCompose = NO; + nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1]; + processingCompose = NO; } if (!winPtr) { return theEvent; @@ -80,7 +80,7 @@ static NSUInteger textInputModifiers; if ([theEvent type] == NSKeyDown && [theEvent isARepeat] && [NSEvent keyRepeatDelay] < 0) { - return theEvent; + return theEvent; } /* @@ -311,7 +311,7 @@ static NSUInteger textInputModifiers; Bool sendingIMEText = NO; str = ([aString isKindOfClass: [NSAttributedString class]]) ? - [aString string] : aString; + [aString string] : aString; len = [str length]; if (NS_KEYLOG) { @@ -426,7 +426,7 @@ static NSUInteger textInputModifiers; (void)selRange; str = ([aString isKindOfClass: [NSAttributedString class]]) ? - [aString string] : aString; + [aString string] : aString; if (focusWin) { /* @@ -636,12 +636,12 @@ setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers) display = Tk_Display(tkwin); if (modifiers) { state = (modifiers & NSAlphaShiftKeyMask ? LockMask : 0) | - (modifiers & NSShiftKeyMask ? ShiftMask : 0) | - (modifiers & NSControlKeyMask ? ControlMask : 0) | - (modifiers & NSCommandKeyMask ? Mod1Mask : 0) | - (modifiers & NSAlternateKeyMask ? Mod2Mask : 0) | - (modifiers & NSNumericPadKeyMask ? Mod3Mask : 0) | - (modifiers & NSFunctionKeyMask ? Mod4Mask : 0) ; + (modifiers & NSShiftKeyMask ? ShiftMask : 0) | + (modifiers & NSControlKeyMask ? ControlMask : 0) | + (modifiers & NSCommandKeyMask ? Mod1Mask : 0) | + (modifiers & NSAlternateKeyMask ? Mod2Mask : 0) | + (modifiers & NSNumericPadKeyMask ? Mod3Mask : 0) | + (modifiers & NSFunctionKeyMask ? Mod4Mask : 0) ; } memset(xEvent, 0, sizeof(XEvent)); xEvent->xany.serial = LastKnownRequestProcessed(display); diff --git a/macosx/tkMacOSXKeyboard.c b/macosx/tkMacOSXKeyboard.c index 3874dbd..f898496 100644 --- a/macosx/tkMacOSXKeyboard.c +++ b/macosx/tkMacOSXKeyboard.c @@ -268,7 +268,7 @@ UpdateKeymaps() */ for (index = 3; index >= 0; index--) { - for (virt = 0; virt < 128; virt++) { + for (virt = 0; virt < 128; virt++) { MacKeycode macKC; macKC.v = (keycode_v) {.virt = virt, .o_s = index, .keychar = 0}; int modifiers = INDEX2CARBON(index); @@ -288,7 +288,7 @@ UpdateKeymaps() hPtr = Tcl_CreateHashEntry(&unichar2xvirtual, INT2PTR(macKC.x.keychar), &dummy); Tcl_SetHashValue(hPtr, INT2PTR(macKC.x.xvirtual)); - } + } xvirtual2unichar[macKC.x.xvirtual] = macKC.x.keychar; } } diff --git a/macosx/tkMacOSXMenu.c b/macosx/tkMacOSXMenu.c index 1908bb9..bf41d66 100644 --- a/macosx/tkMacOSXMenu.c +++ b/macosx/tkMacOSXMenu.c @@ -960,8 +960,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]; @@ -971,8 +969,8 @@ TkpPostMenu( inPostMenu = true; result = TkPreprocessMenu(menuPtr); if (result != TCL_OK) { - inPostMenu = false; - return result; + inPostMenu = false; + return result; } if (itemIndex >= numItems) { itemIndex = numItems - 1; @@ -991,8 +989,9 @@ TkpPostMenu( } [menu popUpMenuPositioningItem:item - atLocation:[win tkConvertPointFromScreen:location] - inView:view]; + atLocation:location + inView:nil + appearance:realWinView.effectiveAppearance]; inPostMenu = false; return TCL_OK; } diff --git a/macosx/tkMacOSXMenubutton.c b/macosx/tkMacOSXMenubutton.c index ac7befd..6d71ffc 100644 --- a/macosx/tkMacOSXMenubutton.c +++ b/macosx/tkMacOSXMenubutton.c @@ -173,7 +173,7 @@ TkpDisplayMenuButton( butPtr->flags &= ~REDRAW_PENDING; if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { - return; + return; } pixmap = (Pixmap) Tk_WindowId(tkwin); @@ -191,7 +191,7 @@ TkpDisplayMenuButton( */ if (butPtr->highlightWidth < 3) { - if (butPtr->flags & GOT_FOCUS) { + if (butPtr->flags & GOT_FOCUS) { GC gc = Tk_GCForColor(butPtr->highlightColorPtr, pixmap); TkMacOSXDrawSolidBorder(tkwin, gc, 0, butPtr->highlightWidth); } @@ -259,23 +259,23 @@ TkpComputeMenuButtonGeometry( avgWidth = 0; if (butPtr->image != NULL) { - Tk_SizeOfImage(butPtr->image, &width, &height); - haveImage = 1; + Tk_SizeOfImage(butPtr->image, &width, &height); + haveImage = 1; } else if (butPtr->bitmap != None) { - Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height); - haveImage = 1; + Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height); + haveImage = 1; } if (butPtr->text && strlen(butPtr->text) > 0) { haveText = 1; - Tk_FreeTextLayout(butPtr->textLayout); - butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont, - butPtr->text, TCL_INDEX_NONE, butPtr->wrapLength, - butPtr->justify, 0, &butPtr->textWidth, &butPtr->textHeight); - txtWidth = butPtr->textWidth; - txtHeight = butPtr->textHeight; - avgWidth = Tk_TextWidth(butPtr->tkfont, "0", 1); - Tk_GetFontMetrics(butPtr->tkfont, &fm); + Tk_FreeTextLayout(butPtr->textLayout); + butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont, + butPtr->text, TCL_INDEX_NONE, butPtr->wrapLength, + butPtr->justify, 0, &butPtr->textWidth, &butPtr->textHeight); + txtWidth = butPtr->textWidth; + txtHeight = butPtr->textHeight; + avgWidth = Tk_TextWidth(butPtr->tkfont, "0", 1); + Tk_GetFontMetrics(butPtr->tkfont, &fm); } /* @@ -286,7 +286,7 @@ TkpComputeMenuButtonGeometry( */ if (haveImage && haveText) { - switch ((enum compound) butPtr->compound) { + switch ((enum compound) butPtr->compound) { case COMPOUND_TOP: case COMPOUND_BOTTOM: /* @@ -315,33 +315,33 @@ TkpComputeMenuButtonGeometry( break; case COMPOUND_NONE: break; - } + } - if (butPtr->width > 0) { - width = butPtr->width; - } - if (butPtr->height > 0) { - height = butPtr->height; - } + if (butPtr->width > 0) { + width = butPtr->width; + } + if (butPtr->height > 0) { + height = butPtr->height; + } } else { - if (haveImage) { /* Image only */ - if (butPtr->width > 0) { - width = butPtr->width; - } - if (butPtr->height > 0) { - height = butPtr->height; - } - } else { /* Text only */ - width = txtWidth; - height = txtHeight; - if (butPtr->width > 0) { - width = butPtr->width * avgWidth + 2*butPtr->padX; - } - if (butPtr->height > 0) { - height = butPtr->height * fm.linespace + 2*butPtr->padY; - } - } + if (haveImage) { /* Image only */ + if (butPtr->width > 0) { + width = butPtr->width; + } + if (butPtr->height > 0) { + height = butPtr->height; + } + } else { /* Text only */ + width = txtWidth; + height = txtHeight; + if (butPtr->width > 0) { + width = butPtr->width * avgWidth + 2*butPtr->padX; + } + if (butPtr->height > 0) { + height = butPtr->height * fm.linespace + 2*butPtr->padY; + } + } } butPtr->inset = highlightWidth + butPtr->borderWidth; @@ -381,30 +381,30 @@ DrawMenuButtonImageAndText( int fullWidth = 0, fullHeight = 0; if (tkwin == NULL || !Tk_IsMapped(tkwin)) { - return; + return; } DrawParams *dpPtr = &mbPtr->drawParams; pixmap = (Pixmap) Tk_WindowId(tkwin); if (butPtr->image != NULL) { - Tk_SizeOfImage(butPtr->image, &width, &height); - haveImage = 1; + Tk_SizeOfImage(butPtr->image, &width, &height); + haveImage = 1; } else if (butPtr->bitmap != None) { - Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height); - haveImage = 1; + Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height); + haveImage = 1; } haveText = (butPtr->textWidth != 0 && butPtr->textHeight != 0); if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) { - int x = 0, y = 0; + int x = 0, y = 0; - textXOffset = 0; - textYOffset = 0; - fullWidth = 0; - fullHeight = 0; + textXOffset = 0; + textYOffset = 0; + fullWidth = 0; + fullHeight = 0; - switch ((enum compound) butPtr->compound) { + switch ((enum compound) butPtr->compound) { case COMPOUND_TOP: case COMPOUND_BOTTOM: /* @@ -435,7 +435,7 @@ DrawMenuButtonImageAndText( } fullWidth = butPtr->textWidth + butPtr->padX + width; fullHeight = (height > butPtr->textHeight ? height : - butPtr->textHeight); + butPtr->textHeight); textYOffset = (fullHeight - butPtr->textHeight)/2; imageYOffset = (fullHeight - height)/2; break; @@ -446,7 +446,7 @@ DrawMenuButtonImageAndText( fullWidth = (width > butPtr->textWidth ? width : butPtr->textWidth); fullHeight = (height > butPtr->textHeight ? height : - butPtr->textHeight); + butPtr->textHeight); textXOffset = (fullWidth - butPtr->textWidth) / 2; imageXOffset = (fullWidth - width) / 2; textYOffset = (fullHeight - butPtr->textHeight) / 2; @@ -456,54 +456,54 @@ DrawMenuButtonImageAndText( break; } - TkComputeAnchor(butPtr->anchor, tkwin, - butPtr->padX + butPtr->inset, butPtr->padY + butPtr->inset, - fullWidth, fullHeight, &x, &y); - imageXOffset = LEFT_INSET; - imageYOffset += y; - textYOffset -= 1; + TkComputeAnchor(butPtr->anchor, tkwin, + butPtr->padX + butPtr->inset, butPtr->padY + butPtr->inset, + fullWidth, fullHeight, &x, &y); + imageXOffset = LEFT_INSET; + imageYOffset += y; + textYOffset -= 1; - if (butPtr->image != NULL) { + if (butPtr->image != NULL) { Tk_RedrawImage(butPtr->image, 0, 0, width, - height, pixmap, imageXOffset, imageYOffset); - } else { - XSetClipOrigin(butPtr->display, dpPtr->gc, - imageXOffset, imageYOffset); - XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, dpPtr->gc, - 0, 0, (unsigned int) width, (unsigned int) height, - imageXOffset, imageYOffset, 1); - XSetClipOrigin(butPtr->display, dpPtr->gc, 0, 0); - } - - Tk_DrawTextLayout(butPtr->display, pixmap, - dpPtr->gc, butPtr->textLayout, - x + textXOffset, y + textYOffset, 0, -1); - Tk_UnderlineTextLayout(butPtr->display, pixmap, dpPtr->gc, - butPtr->textLayout, x + textXOffset, y + textYOffset, - butPtr->underline); + height, pixmap, imageXOffset, imageYOffset); + } else { + XSetClipOrigin(butPtr->display, dpPtr->gc, + imageXOffset, imageYOffset); + XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, dpPtr->gc, + 0, 0, (unsigned int) width, (unsigned int) height, + imageXOffset, imageYOffset, 1); + XSetClipOrigin(butPtr->display, dpPtr->gc, 0, 0); + } + + Tk_DrawTextLayout(butPtr->display, pixmap, + dpPtr->gc, butPtr->textLayout, + x + textXOffset, y + textYOffset, 0, -1); + Tk_UnderlineTextLayout(butPtr->display, pixmap, dpPtr->gc, + butPtr->textLayout, x + textXOffset, y + textYOffset, + butPtr->underline); } else { int x, y; - if (haveImage) { - TkComputeAnchor(butPtr->anchor, tkwin, - butPtr->padX + butPtr->borderWidth, - butPtr->padY + butPtr->borderWidth, - width, height, &x, &y); + if (haveImage) { + TkComputeAnchor(butPtr->anchor, tkwin, + butPtr->padX + butPtr->borderWidth, + butPtr->padY + butPtr->borderWidth, + width, height, &x, &y); imageXOffset = LEFT_INSET; imageYOffset += y; if (butPtr->image != NULL) { Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap, imageXOffset, imageYOffset); - } else { - XSetClipOrigin(butPtr->display, dpPtr->gc, x, y); - XCopyPlane(butPtr->display, butPtr->bitmap, + } else { + XSetClipOrigin(butPtr->display, dpPtr->gc, x, y); + XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, dpPtr->gc, 0, 0, (unsigned int) width, (unsigned int) height, imageXOffset, imageYOffset, 1); - XSetClipOrigin(butPtr->display, dpPtr->gc, 0, 0); - } - } else { + XSetClipOrigin(butPtr->display, dpPtr->gc, 0, 0); + } + } else { textXOffset = LEFT_INSET; TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY, butPtr->textWidth, butPtr->textHeight, &x, &y); @@ -537,7 +537,7 @@ TkMacOSXDrawMenuButton( MacMenuButton *mbPtr, /* Mac menubutton. */ TCL_UNUSED(GC), /* The GC we are drawing into - not used */ Pixmap pixmap) /* The pixmap we are drawing into - needed for the - * bevel button */ + * bevel button */ { TkMenuButton *butPtr = (TkMenuButton *) mbPtr; TkWindow *winPtr = (TkWindow *) butPtr->tkwin; @@ -552,23 +552,23 @@ TkMacOSXDrawMenuButton( Tk_Width(butPtr->tkwin), Tk_Height(butPtr->tkwin)); if (useNewerHITools == 1) { - HIRect contHIRec; - static HIThemeButtonDrawInfo hiinfo; + HIRect contHIRec; + static HIThemeButtonDrawInfo hiinfo; - MenuButtonBackgroundDrawCB(mbPtr, 32, true); + MenuButtonBackgroundDrawCB(mbPtr, 32, true); if (!TkMacOSXSetupDrawingContext(pixmap, dpPtr->gc, &dc)) { return; } - hiinfo.version = 0; - hiinfo.state = mbPtr->drawinfo.state; - hiinfo.kind = mbPtr->btnkind; - hiinfo.value = mbPtr->drawinfo.value; - hiinfo.adornment = mbPtr->drawinfo.adornment; - hiinfo.animation.time.current = CFAbsoluteTimeGetCurrent(); - if (hiinfo.animation.time.start == 0) { - hiinfo.animation.time.start = hiinfo.animation.time.current; - } + hiinfo.version = 0; + hiinfo.state = mbPtr->drawinfo.state; + hiinfo.kind = mbPtr->btnkind; + hiinfo.value = mbPtr->drawinfo.value; + hiinfo.adornment = mbPtr->drawinfo.adornment; + hiinfo.animation.time.current = CFAbsoluteTimeGetCurrent(); + if (hiinfo.animation.time.start == 0) { + hiinfo.animation.time.start = hiinfo.animation.time.current; + } /* * To avoid menubuttons with white text on a white background, we @@ -581,10 +581,10 @@ TkMacOSXDrawMenuButton( hiinfo.state = kThemeStateInactive; } - HIThemeDrawButton(&cntrRect, &hiinfo, dc.context, + HIThemeDrawButton(&cntrRect, &hiinfo, dc.context, kHIThemeOrientationNormal, &contHIRec); TkMacOSXRestoreDrawingContext(&dc); - MenuButtonContentDrawCB(mbPtr->btnkind, &mbPtr->drawinfo, + MenuButtonContentDrawCB(mbPtr->btnkind, &mbPtr->drawinfo, mbPtr, 32, true); } else { if (!TkMacOSXSetupDrawingContext(pixmap, dpPtr->gc, &dc)) { @@ -623,11 +623,11 @@ MenuButtonBackgroundDrawCB ( Pixmap pixmap; if (tkwin == NULL || !Tk_IsMapped(tkwin)) { - return; + return; } pixmap = (Pixmap) Tk_WindowId(tkwin); Tk_Fill3DRectangle(tkwin, pixmap, butPtr->normalBorder, 0, 0, - Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT); + Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT); } /* @@ -658,7 +658,7 @@ MenuButtonContentDrawCB ( Tk_Window tkwin = butPtr->tkwin; if (tkwin == NULL || !Tk_IsMapped(tkwin)) { - return; + return; } DrawMenuButtonImageAndText(butPtr); } @@ -749,22 +749,22 @@ TkMacOSXComputeMenuButtonParams( drawinfo->state = kThemeStateInactive; if ((mbPtr->flags & ACTIVE) == 0) { - if (butPtr->state == STATE_DISABLED) { - drawinfo->state = kThemeStateUnavailableInactive; - } else { - drawinfo->state = kThemeStateInactive; - } + if (butPtr->state == STATE_DISABLED) { + drawinfo->state = kThemeStateUnavailableInactive; + } else { + drawinfo->state = kThemeStateInactive; + } } else if (butPtr->state == STATE_DISABLED) { - drawinfo->state = kThemeStateUnavailable; + drawinfo->state = kThemeStateUnavailable; } else { - drawinfo->state = kThemeStateActive; + drawinfo->state = kThemeStateActive; } drawinfo->adornment = kThemeAdornmentNone; if (butPtr->highlightWidth >= 3) { - if ((butPtr->flags & GOT_FOCUS)) { - drawinfo->adornment |= kThemeAdornmentFocus; - } + if ((butPtr->flags & GOT_FOCUS)) { + drawinfo->adornment |= kThemeAdornmentFocus; + } } drawinfo->adornment |= kThemeAdornmentArrowDoubleArrow; } @@ -795,12 +795,12 @@ TkMacOSXComputeMenuButtonDrawParams( ((butPtr->image != NULL) || (butPtr->bitmap != None)); dpPtr->border = butPtr->normalBorder; if ((butPtr->state == STATE_DISABLED) && (butPtr->disabledFg != NULL)) { - dpPtr->gc = butPtr->disabledGC; + dpPtr->gc = butPtr->disabledGC; } else if (butPtr->state == STATE_ACTIVE) { - dpPtr->gc = butPtr->activeTextGC; - dpPtr->border = butPtr->activeBorder; + dpPtr->gc = butPtr->activeTextGC; + dpPtr->border = butPtr->activeBorder; } else { - dpPtr->gc = butPtr->normalTextGC; + dpPtr->gc = butPtr->normalTextGC; } } diff --git a/macosx/tkMacOSXMenus.c b/macosx/tkMacOSXMenus.c index b1b8bad..b717352 100644 --- a/macosx/tkMacOSXMenus.c +++ b/macosx/tkMacOSXMenus.c @@ -116,18 +116,18 @@ static Tcl_Obj * GetWidgetDemoPath(Tcl_Interp *interp); _defaultWindowsMenuItems = [_defaultWindowsMenuItems arrayByAddingObjectsFromArray: [NSArray arrayWithObjects: - [NSMenuItem separatorItem], + [NSMenuItem separatorItem], [NSMenuItem itemWithTitle:@"Show Previous Tab" - action:@selector(selectPreviousTab:) - target:nil + action:@selector(selectPreviousTab:) + target:nil keyEquivalent:@"\t" - keyEquivalentModifierMask: + keyEquivalentModifierMask: NSControlKeyMask|NSShiftKeyMask], - [NSMenuItem itemWithTitle:@"Show Next Tab" - action:@selector(selectNextTab:) - target:nil + [NSMenuItem itemWithTitle:@"Show Next Tab" + action:@selector(selectNextTab:) + target:nil keyEquivalent:@"\t" - keyEquivalentModifierMask:NSControlKeyMask], + keyEquivalentModifierMask:NSControlKeyMask], [NSMenuItem itemWithTitle:@"Move Tab To New Window" action:@selector(moveTabToNewWindow:) target:nil], @@ -135,7 +135,7 @@ static Tcl_Obj * GetWidgetDemoPath(Tcl_Interp *interp); action:@selector(mergeAllWindows:) target:nil], [NSMenuItem separatorItem], - nil]]; + nil]]; } _defaultWindowsMenuItems = [_defaultWindowsMenuItems arrayByAddingObject: [NSMenuItem itemWithTitle:@"Bring All to Front" @@ -193,7 +193,7 @@ static Tcl_Obj * GetWidgetDemoPath(Tcl_Interp *interp); } return haveDemo; } else { - return [super validateUserInterfaceItem:anItem]; + return [super validateUserInterfaceItem:anItem]; } } diff --git a/macosx/tkMacOSXMouseEvent.c b/macosx/tkMacOSXMouseEvent.c index 83d2cd2..062a125 100644 --- a/macosx/tkMacOSXMouseEvent.c +++ b/macosx/tkMacOSXMouseEvent.c @@ -504,8 +504,9 @@ enum { state |= Tk_GetButtonMask(Button1); } if (eventType == NSMouseEntered) { - Tk_UpdatePointer((Tk_Window) [NSApp tkPointerWindow], - global.x, global.y, state); + Tk_Window new_win = Tk_CoordsToWindow(global.x, global.y, + (Tk_Window) [NSApp tkPointerWindow]); + Tk_UpdatePointer(new_win, global.x, global.y, state); } else if (eventType == NSMouseExited) { if ([NSApp tkDragTarget]) { Tk_UpdatePointer((Tk_Window) [NSApp tkDragTarget], @@ -883,9 +884,9 @@ TkpWarpPointer( CGWarpMouseCursorPosition(pt); if (dispPtr->warpWindow) { - TkGenerateButtonEventForXPointer(Tk_WindowId(dispPtr->warpWindow)); + TkGenerateButtonEventForXPointer(Tk_WindowId(dispPtr->warpWindow)); } else { - TkGenerateButtonEventForXPointer(None); + TkGenerateButtonEventForXPointer(None); } } diff --git a/macosx/tkMacOSXNotify.c b/macosx/tkMacOSXNotify.c index a20eec2..f353380 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]]) { @@ -352,7 +352,6 @@ TkMacOSXDrawAllViews( if (dirtyCount) { continue; } - [[view layer] setNeedsDisplayInRect:[view tkDirtyRect]]; [view setNeedsDisplay:YES]; } } else { @@ -362,26 +361,15 @@ TkMacOSXDrawAllViews( if (dirtyCount) { *dirtyCount = count; } + + /* + * Trigger calls to updateLayer methods for the views flagged above. + */ + [NSApp nextEventMatchingMask:NSAnyEventMask 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]) { - [view setNeedsDisplay: NO]; - } - } - } - [NSApp setNeedsToDraw:NO]; } /* @@ -448,11 +436,11 @@ TkMacOSXEventsSetupProc( */ NSEvent *currentEvent = - [NSApp nextEventMatchingMask:NSAnyEventMask + [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:GetRunLoopMode(TkMacOSXGetModalSession()) dequeue:NO]; - if ((currentEvent) || [NSApp needsToDraw] ) { + if ((currentEvent)) { Tcl_SetMaxBlockTime(&zeroBlockTime); Tcl_DeleteTimerHandler(ticker); ticker = NULL; diff --git a/macosx/tkMacOSXPrint.c b/macosx/tkMacOSXPrint.c index ee30e1f..ac29714 100644 --- a/macosx/tkMacOSXPrint.c +++ b/macosx/tkMacOSXPrint.c @@ -92,8 +92,8 @@ StartPrint( /* Check for proper number of arguments. */ if (objc < 2) { - Tcl_WrongNumArgs(interp, 1, objv, "file"); - return TCL_ERROR; + Tcl_WrongNumArgs(interp, 1, objv, "file"); + return TCL_ERROR; } fileName = [NSString stringWithUTF8String: Tcl_GetString(objv[1])]; @@ -105,20 +105,20 @@ StartPrint( status = PMCreateSession( & printSession); if (status != noErr) { - NSLog(@ "Error creating print session."); - return TCL_ERROR; + NSLog(@ "Error creating print session."); + return TCL_ERROR; } status = PMCreatePrintSettings( & printSettings); if (status != noErr) { - NSLog(@ "Error creating print settings."); - return TCL_ERROR; + NSLog(@ "Error creating print settings."); + return TCL_ERROR; } status = PMSessionDefaultPrintSettings(printSession, printSettings); if (status != noErr) { - NSLog(@ "Error creating default print settings."); - return TCL_ERROR; + NSLog(@ "Error creating default print settings."); + return TCL_ERROR; } printSession = (PMPrintSession)[printInfo PMPrintSession]; @@ -163,25 +163,25 @@ FinishPrint( * otherwise printing will occur regardless of value. */ if (buttonValue == NSModalResponseCancel) { - return noErr; + return noErr; } status = PMCreateSession( & printSession); if (status != noErr) { - NSLog(@ "Error creating print session."); - return status; + NSLog(@ "Error creating print session."); + return status; } status = PMCreatePrintSettings( & printSettings); if (status != noErr) { - NSLog(@ "Error creating print settings."); - return status; + NSLog(@ "Error creating print settings."); + return status; } status = PMSessionDefaultPrintSettings(printSession, printSettings); if (status != noErr) { - NSLog(@ "Error creating default print settings."); - return status; + NSLog(@ "Error creating default print settings."); + return status; } printSession = (PMPrintSession)[printInfo PMPrintSession]; @@ -191,81 +191,81 @@ FinishPrint( /*Handle print operation.*/ if (buttonValue == NSModalResponseOK) { - if (urlFile == NULL) { - NSLog(@ "Could not get file to print."); - return noErr; - } - - fileName = file; - - CFURLRef printURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, urlFile, kCFURLPOSIXPathStyle, false); - - PMPrinter currentPrinter; - PMDestinationType printDestination; - - /*Get the intended destination.*/ - status = PMSessionGetDestinationType(printSession, printSettings, & printDestination); - - /*Destination is printer. Send file to printer.*/ - if (status == noErr && printDestination == kPMDestinationPrinter) { - - status = PMSessionGetCurrentPrinter(printSession, & currentPrinter); - if (status == noErr) { - CFArrayRef mimeTypes; - status = PMPrinterGetMimeTypes(currentPrinter, printSettings, & mimeTypes); - if (status == noErr && mimeTypes != NULL) { - mimeType = CFSTR("application/pdf"); - if (CFArrayContainsValue(mimeTypes, CFRangeMake(0, CFArrayGetCount(mimeTypes)), mimeType)) { - status = PMPrinterPrintWithFile(currentPrinter, printSettings, pageFormat, mimeType, printURL); - CFRelease(urlFile); - return status; - } - } - } - } - - /* Destination is file. Determine how to handle. */ - if (status == noErr && printDestination == kPMDestinationFile) { - CFURLRef outputLocation = NULL; - - status = PMSessionCopyDestinationLocation(printSession, printSettings, & outputLocation); - if (status == noErr) { - /*Get the source file and target destination, convert to strings.*/ - CFStringRef sourceFile = CFURLCopyFileSystemPath(printURL, kCFURLPOSIXPathStyle); - CFStringRef savePath = CFURLCopyFileSystemPath(outputLocation, kCFURLPOSIXPathStyle); - NSString * sourcePath = (NSString * ) sourceFile; - NSString * finalPath = (NSString * ) savePath; - NSString * pathExtension = [finalPath pathExtension]; - NSFileManager * fileManager = [NSFileManager defaultManager]; + if (urlFile == NULL) { + NSLog(@ "Could not get file to print."); + return noErr; + } + + fileName = file; + + CFURLRef printURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, urlFile, kCFURLPOSIXPathStyle, false); + + PMPrinter currentPrinter; + PMDestinationType printDestination; + + /*Get the intended destination.*/ + status = PMSessionGetDestinationType(printSession, printSettings, & printDestination); + + /*Destination is printer. Send file to printer.*/ + if (status == noErr && printDestination == kPMDestinationPrinter) { + + status = PMSessionGetCurrentPrinter(printSession, & currentPrinter); + if (status == noErr) { + CFArrayRef mimeTypes; + status = PMPrinterGetMimeTypes(currentPrinter, printSettings, & mimeTypes); + if (status == noErr && mimeTypes != NULL) { + mimeType = CFSTR("application/pdf"); + if (CFArrayContainsValue(mimeTypes, CFRangeMake(0, CFArrayGetCount(mimeTypes)), mimeType)) { + status = PMPrinterPrintWithFile(currentPrinter, printSettings, pageFormat, mimeType, printURL); + CFRelease(urlFile); + return status; + } + } + } + } + + /* Destination is file. Determine how to handle. */ + if (status == noErr && printDestination == kPMDestinationFile) { + CFURLRef outputLocation = NULL; + + status = PMSessionCopyDestinationLocation(printSession, printSettings, & outputLocation); + if (status == noErr) { + /*Get the source file and target destination, convert to strings.*/ + CFStringRef sourceFile = CFURLCopyFileSystemPath(printURL, kCFURLPOSIXPathStyle); + CFStringRef savePath = CFURLCopyFileSystemPath(outputLocation, kCFURLPOSIXPathStyle); + NSString * sourcePath = (NSString * ) sourceFile; + NSString * finalPath = (NSString * ) savePath; + NSString * pathExtension = [finalPath pathExtension]; + NSFileManager * fileManager = [NSFileManager defaultManager]; NSError * error = nil; - /* + /* * Is the target file a PDF? If so, copy print file * to output location. */ - if ([pathExtension isEqualToString: @ "pdf"]) { + if ([pathExtension isEqualToString: @ "pdf"]) { /*Make sure no file conflict exists.*/ if ([fileManager fileExistsAtPath: finalPath]) { [fileManager removeItemAtPath: finalPath error: &error]; } - if ([fileManager fileExistsAtPath: sourcePath]) { - error = nil; - [fileManager copyItemAtPath: sourcePath toPath: finalPath error: & error]; - } + if ([fileManager fileExistsAtPath: sourcePath]) { + error = nil; + [fileManager copyItemAtPath: sourcePath toPath: finalPath error: & error]; + } return status; - } - - /* - * Is the target file PostScript? If so, run print file - * through CUPS filter to convert back to PostScript. - */ - - if ([pathExtension isEqualToString: @ "ps"]) { - char source[5012]; - char target[5012]; - [sourcePath getCString: source maxLength: (sizeof source) encoding: NSUTF8StringEncoding]; - [finalPath getCString: target maxLength: (sizeof target) encoding: NSUTF8StringEncoding]; + } + + /* + * Is the target file PostScript? If so, run print file + * through CUPS filter to convert back to PostScript. + */ + + if ([pathExtension isEqualToString: @ "ps"]) { + char source[5012]; + char target[5012]; + [sourcePath getCString: source maxLength: (sizeof source) encoding: NSUTF8StringEncoding]; + [finalPath getCString: target maxLength: (sizeof target) encoding: NSUTF8StringEncoding]; /*Make sure no file conflict exists.*/ if ([fileManager fileExistsAtPath: finalPath]) { [fileManager removeItemAtPath: finalPath error: &error]; @@ -290,39 +290,39 @@ FinishPrint( } } - /* Destination is preview. Open file in default application for PDF. */ - if ((status == noErr) && (printDestination == kPMDestinationPreview)) { - CFStringRef urlpath = CFURLCopyFileSystemPath(printURL, kCFURLPOSIXPathStyle); - NSString * path = (NSString * ) urlpath; - NSURL * url = [NSURL fileURLWithPath: path]; - NSWorkspace * ws = [NSWorkspace sharedWorkspace]; - [ws openURL: url]; - status = noErr; - return status; - } - - /* - * If destination is not printer, file or preview, - * we do not support it. Display alert. - */ + /* Destination is preview. Open file in default application for PDF. */ + if ((status == noErr) && (printDestination == kPMDestinationPreview)) { + CFStringRef urlpath = CFURLCopyFileSystemPath(printURL, kCFURLPOSIXPathStyle); + NSString * path = (NSString * ) urlpath; + NSURL * url = [NSURL fileURLWithPath: path]; + NSWorkspace * ws = [NSWorkspace sharedWorkspace]; + [ws openURL: url]; + status = noErr; + return status; + } + + /* + * If destination is not printer, file or preview, + * we do not support it. Display alert. + */ if (((status == noErr) && (printDestination != kPMDestinationPreview)) || ((status == noErr) && (printDestination != kPMDestinationFile)) || ((status == noErr) && (printDestination != kPMDestinationPrinter))) { - NSAlert * alert = [[[NSAlert alloc] init] autorelease]; - [alert addButtonWithTitle: @ "OK"]; + NSAlert * alert = [[[NSAlert alloc] init] autorelease]; + [alert addButtonWithTitle: @ "OK"]; - [alert setMessageText: @ "Unsupported Printing Operation"]; - [alert setInformativeText: @ "This printing operation is not supported."]; - [alert setAlertStyle: NSAlertStyleInformational]; - [alert runModal]; - return status; - } + [alert setMessageText: @ "Unsupported Printing Operation"]; + [alert setInformativeText: @ "This printing operation is not supported."]; + [alert setAlertStyle: NSAlertStyleInformational]; + [alert runModal]; + return status; + } } /* Return because cancel button was clicked. */ if (buttonValue == NSModalResponseCancel) { - PMRelease(printSession); - return status; + PMRelease(printSession); + return status; } return status; diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h index 151f2f8..5143515 100644 --- a/macosx/tkMacOSXPrivate.h +++ b/macosx/tkMacOSXPrivate.h @@ -166,7 +166,7 @@ typedef union MacKeycode_t { #define ON_KEYPAD(virt) ((virt >= 0x41) && (virt <= 0x5C)) #define IS_PRINTABLE(keychar) ((keychar >= 0x20) && (keychar != 0x7f) && \ - ((keychar < 0xF700) || keychar >= 0xF8FF)) + ((keychar < 0xF700) || keychar >= 0xF8FF)) /* * An "index" is 2-bit bitfield showing the state of the Option and Shift @@ -240,14 +240,14 @@ 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( 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))) @@ -320,9 +320,6 @@ VISIBILITY_HIDDEN } @property int poolLock; @property int macOSVersion; -@property Bool isDrawing; -@property Bool needsToDraw; -@property Bool isSigned; @property Bool tkLiveResizeEnded; /* @@ -392,7 +389,7 @@ VISIBILITY_HIDDEN - (void) handleDoScriptEvent: (NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent; - (void)handleURLEvent: (NSAppleEventDescriptor*)event - withReplyEvent: (NSAppleEventDescriptor*)replyEvent; + withReplyEvent: (NSAppleEventDescriptor*)replyEvent; @end VISIBILITY_HIDDEN @@ -411,6 +408,7 @@ VISIBILITY_HIDDEN } @property Bool tkNeedsDisplay; @property NSRect tkDirtyRect; +@property CGContextRef tkLayerBitmapContext; @end @interface TKContentView(TKKeyEvent) @@ -423,6 +421,7 @@ VISIBILITY_HIDDEN - (void) clearTkDirtyRect; - (void) generateExposeEvents: (NSRect) rect; - (void) tkToolbarButton: (id) sender; +- (void) resetTkLayerBitmapContext; @end @interface NSWindow(TKWm) @@ -467,6 +466,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/tkMacOSXScale.c b/macosx/tkMacOSXScale.c index 96a61b8..38ff385 100644 --- a/macosx/tkMacOSXScale.c +++ b/macosx/tkMacOSXScale.c @@ -169,10 +169,10 @@ TkpDisplayScale( Tcl_Preserve(scalePtr); if ((scalePtr->flags & INVOKE_COMMAND) && (scalePtr->command != NULL)) { Tcl_Preserve(interp); - if (snprintf(string, TCL_DOUBLE_SPACE, scalePtr->format, - scalePtr->value) < 0) { - string[TCL_DOUBLE_SPACE - 1] = '\0'; - } + if (snprintf(string, TCL_DOUBLE_SPACE, scalePtr->format, + scalePtr->value) < 0) { + string[TCL_DOUBLE_SPACE - 1] = '\0'; + } Tcl_DStringInit(&buf); Tcl_DStringAppend(&buf, scalePtr->command, TCL_INDEX_NONE); Tcl_DStringAppend(&buf, " ", TCL_INDEX_NONE); diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index a420898..1d4ffec 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -56,32 +56,37 @@ XDestroyWindow( Window window) /* Window. */ { MacDrawable *macWin = (MacDrawable *)window; + TKContentView *view = (TKContentView *)TkMacOSXGetNSViewForDrawable(macWin); + //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 * deleting is being tracked by the grab code. */ - TkPointerDeadWindow(macWin->winPtr); TkMacOSXSelDeadWindow(macWin->winPtr); + TkPointerDeadWindow(macWin->winPtr); macWin->toplevel->referenceCount--; if (!Tk_IsTopLevel(macWin->winPtr)) { - TkMacOSXInvalidateWindow(macWin, TK_PARENT_WINDOW); if (macWin->winPtr->parentPtr != NULL) { TkMacOSXInvalClipRgns((Tk_Window)macWin->winPtr->parentPtr); + Tcl_CancelIdleCall(TkMacOSXRedrawViewIdleTask, (void *) view); + Tcl_DoWhenIdle(TkMacOSXRedrawViewIdleTask, (void *) view); } if (macWin->visRgn) { CFRelease(macWin->visRgn); - macWin->visRgn = NULL; + macWin->visRgn = NULL; } if (macWin->aboveVisRgn) { CFRelease(macWin->aboveVisRgn); - macWin->aboveVisRgn = NULL; + macWin->aboveVisRgn = NULL; } if (macWin->drawRgn) { CFRelease(macWin->drawRgn); - macWin->drawRgn = NULL; + macWin->drawRgn = NULL; } if (macWin->toplevel->referenceCount == 0) { @@ -93,15 +98,15 @@ XDestroyWindow( } if (macWin->visRgn) { CFRelease(macWin->visRgn); - macWin->visRgn = NULL; + macWin->visRgn = NULL; } if (macWin->aboveVisRgn) { CFRelease(macWin->aboveVisRgn); - macWin->aboveVisRgn = NULL; + macWin->aboveVisRgn = NULL; } if (macWin->drawRgn) { CFRelease(macWin->drawRgn); - macWin->drawRgn = NULL; + macWin->drawRgn = NULL; } macWin->view = nil; macWin->winPtr->privatePtr = NULL; @@ -147,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 @@ -165,29 +169,44 @@ 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 - * 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); [win setExcludedFromWindowsMenu:NO]; [NSApp activateIgnoringOtherApps:initialized]; - [view addTkDirtyRect: [view bounds]]; 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 try = 0; try < 20; try++) { + if ([[NSApp orderedWindows] firstObject] == win) { + break; + } + [NSThread sleepForTimeInterval:.001]; + } } /* @@ -208,9 +227,7 @@ XMapWindow( */ TkMacOSXInvalClipRgns(contWinPtr); - TkMacOSXInvalidateWindow(macWin, TK_PARENT_WINDOW); } - TkMacOSXInvalClipRgns((Tk_Window)winPtr); } else { /* @@ -222,14 +239,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]; - if (view != [NSView focusView]) { - [view addTkDirtyRect:[view bounds]]; - } + Tcl_CancelIdleCall(TkMacOSXRedrawViewIdleTask, (void *) view); + Tcl_DoWhenIdle(TkMacOSXRedrawViewIdleTask, (void *) view); /* * Generate VisibilityNotify events for window and all mapped children. @@ -253,7 +269,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. @@ -308,7 +325,6 @@ XUnmapWindow( { MacDrawable *macWin = (MacDrawable *)window; TkWindow *winPtr = macWin->winPtr; - TkWindow *parentPtr = winPtr->parentPtr; NSWindow *win = TkMacOSXGetNSWindowForDrawable(window); if (!window) { @@ -353,22 +369,12 @@ 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); - } - TKContentView *view = [win contentView]; - if (view != [NSView focusView]) { - [view addTkDirtyRect:[view bounds]]; + TkMacOSXInvalClipRgns((Tk_Window)winPtr->parentPtr); } return Success; } @@ -408,7 +414,6 @@ XResizeWindow( [(TKWindow *)w tkLayoutChanged]; } else { NSRect r = [w contentRectForFrameRect:[w frame]]; - r.origin.y += r.size.height - height; r.size.width = width; r.size.height = height; @@ -739,15 +744,10 @@ XConfigureWindow( NSView *view = TkMacOSXGetNSViewForDrawable(macWin); if (view) { - TkMacOSXInvalClipRgns((Tk_Window)winPtr->parentPtr); - TkpRedrawWidget((Tk_Window)winPtr); + TkMacOSXInvalidateWindow(macWin, TK_PARENT_WINDOW); } } -#if 0 - TkGenWMMoveRequestEvent(macWin->winPtr, - macWin->winPtr->changes.x, macWin->winPtr->changes.y); -#endif return Success; } @@ -964,6 +964,7 @@ TkMacOSXUpdateClipRgn( } } +// Unused and misspelled stub function /* *---------------------------------------------------------------------- * @@ -992,6 +993,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. + /* *---------------------------------------------------------------------- * @@ -1029,9 +1035,13 @@ InvalViewRect( break; case kHIShapeEnumerateRect: dirtyRect = NSRectFromCGRect(CGRectApplyAffineTransform(*rect, t)); - [view addTkDirtyRect:dirtyRect]; + // Cannot rely on addTkDirtyRect: to force redrawing. + //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; } @@ -1046,22 +1056,34 @@ 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. * *---------------------------------------------------------------------- */ +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( @@ -1072,11 +1094,16 @@ 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); + 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); } - TkMacOSXInvalidateViewRegion(TkMacOSXGetNSViewForDrawable(macWin), - (flag == TK_WINDOW_ONLY) ? macWin->visRgn : macWin->aboveVisRgn); + [view generateExposeEvents:[view bounds]]; + [view setTkNeedsDisplay:YES]; } /* @@ -1105,17 +1132,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/tkMacOSXSysTray.c b/macosx/tkMacOSXSysTray.c index 76186cc..99ffc9b 100644 --- a/macosx/tkMacOSXSysTray.c +++ b/macosx/tkMacOSXSysTray.c @@ -205,7 +205,7 @@ MacSystrayObjCmd( static const char *modifyOptions[] = {"image", "text", "b1_callback", "b3_callback", NULL}; typedef enum {TRAY_IMAGE, TRAY_TEXT, TRAY_B1_CALLBACK, TRAY_B3_CALLBACK - } modifyOptionsEnum; + } modifyOptionsEnum; if ([NSApp macOSVersion] < 101000) { Tcl_AppendResult(interp, @@ -329,9 +329,9 @@ MacSystrayObjCmd( break; } - /* - * Modify the text for the tooltip. - */ + /* + * Modify the text for the tooltip. + */ case TRAY_TEXT: { NSString *tooltip = [NSString stringWithUTF8String:Tcl_GetString(objv[3])]; @@ -345,9 +345,9 @@ MacSystrayObjCmd( break; } - /* - * Modify the proc for the callback. - */ + /* + * Modify the proc for the callback. + */ case TRAY_B1_CALLBACK: { [statusItem setB1Callback : objv[3]]; @@ -365,14 +365,14 @@ MacSystrayObjCmd( /* * Set all properties to nil, and release statusItem. */ - [statusItem setImagewithImage: nil]; - [statusItem setTextwithString: nil]; - [statusItem setB1Callback : NULL]; - [statusItem setB3Callback : NULL]; - [statusItem release]; - *info = NULL; - statusItem = NULL; - break; + [statusItem setImagewithImage: nil]; + [statusItem setTextwithString: nil]; + [statusItem setB1Callback : NULL]; + [statusItem setB3Callback : NULL]; + [statusItem release]; + *info = NULL; + statusItem = NULL; + break; } } diff --git a/macosx/tkMacOSXTest.c b/macosx/tkMacOSXTest.c index 0e0b06b..b36c283 100644 --- a/macosx/tkMacOSXTest.c +++ b/macosx/tkMacOSXTest.c @@ -140,6 +140,8 @@ MenuBarHeightObjCmd( * Returns true if and only if the NSView of the drawable is the * current focusView, which on 10.14 and newer systems can only be the * case when within [NSView drawRect]. + * NOTE: This is no longer needed when we use updateLayer instead + * of drawRect. Now it always returns True. * * Side effects: * None @@ -151,21 +153,8 @@ MODULE_SCOPE Bool TkTestLogDisplay( Drawable drawable) { - MacDrawable *macWin = (MacDrawable *)drawable; - 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; } /* @@ -209,8 +198,8 @@ PressButtonObjCmd( } if (objc != 3) { - Tcl_WrongNumArgs(interp, 1, objv, "x y"); - return TCL_ERROR; + Tcl_WrongNumArgs(interp, 1, objv, "x y"); + return TCL_ERROR; } for (i = 1; i < objc; i++) { if (Tcl_GetIntFromObj(interp,objv[i],&value) != TCL_OK) { @@ -308,8 +297,8 @@ MoveMouseObjCmd( } if (objc != 3) { - Tcl_WrongNumArgs(interp, 1, objv, "x y"); - return TCL_ERROR; + Tcl_WrongNumArgs(interp, 1, objv, "x y"); + return TCL_ERROR; } for (i = 1; i < objc; i++) { if (Tcl_GetIntFromObj(interp,objv[i],&value) != TCL_OK) { @@ -370,12 +359,12 @@ InjectKeyEventObjCmd( if (objc < 3) { wrongArgs: - Tcl_WrongNumArgs(interp, 1, objv, "option keysym ?arg?"); - return TCL_ERROR; + Tcl_WrongNumArgs(interp, 1, objv, "option keysym ?arg?"); + return TCL_ERROR; } if (Tcl_GetIndexFromObjStruct(interp, objv[1], optionStrings, - sizeof(char *), "option", 0, &index) != TCL_OK) { - return TCL_ERROR; + sizeof(char *), "option", 0, &index) != TCL_OK) { + return TCL_ERROR; } type = types[index]; if (Tcl_GetIntFromObj(interp, objv[2], &keysym) != TCL_OK) { @@ -387,37 +376,37 @@ InjectKeyEventObjCmd( macKC.uint = XKeysymToKeycode(NULL, keysym); for (i = 3; i < objc; i++) { if (Tcl_GetIndexFromObjStruct(interp, objv[i], argStrings, - sizeof(char *), "option", TCL_EXACT, &index) != TCL_OK) { - return TCL_ERROR; - } - switch ((enum args) index) { + sizeof(char *), "option", TCL_EXACT, &index) != TCL_OK) { + return TCL_ERROR; + } + switch ((enum args) index) { case KEYEVENT_SHIFT: mods |= NSShiftKeyMask; - break; + break; case KEYEVENT_CONTROL: mods |= NSControlKeyMask; - break; + break; case KEYEVENT_OPTION: mods |= NSAlternateKeyMask; - break; + break; case KEYEVENT_COMMAND: mods |= NSCommandKeyMask; - break; + break; case KEYEVENT_FUNCTION: mods |= NSFunctionKeyMask; - break; + break; case KEYEVENT_X: if (++i >= objc) { - goto wrongArgs; - } + goto wrongArgs; + } if (Tcl_GetIntFromObj(interp,objv[i], &x) != TCL_OK) { return TCL_ERROR; } break; case KEYEVENT_Y: if (++i >= objc) { - goto wrongArgs; - } + goto wrongArgs; + } if (Tcl_GetIntFromObj(interp,objv[i], &y) != TCL_OK) { return TCL_ERROR; } @@ -445,7 +434,7 @@ InjectKeyEventObjCmd( } keyEvent = [NSEvent keyEventWithType:type location:NSMakePoint(x, y) - modifierFlags:mods + modifierFlags:mods timestamp:GetCurrentEventTime() windowNumber:0 context:nil diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index e261bee..2915af1 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -150,7 +150,7 @@ extern NSString *NSWindowDidOrderOffScreenNotification; } - (NSRect)windowWillUseStandardFrame:(NSWindow *)window - defaultFrame:(NSRect)newFrame + defaultFrame:(NSRect)newFrame { (void)window; @@ -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); } @@ -256,7 +256,7 @@ extern NSString *NSWindowDidOrderOffScreenNotification; TkWindow *winPtr = TkMacOSXGetTkWindow(w); if (winPtr) { - while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} + while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} } } @@ -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 */ @@ -396,7 +398,7 @@ static void RefocusGrabWindow(void *data) { } - (BOOL)applicationShouldHandleReopen:(NSApplication *)sender - hasVisibleWindows:(BOOL)flag + hasVisibleWindows:(BOOL)flag { (void)sender; (void)flag; @@ -473,31 +475,11 @@ static void RefocusGrabWindow(void *data) { * *---------------------------------------------------------------------- */ - +//XXXXX This stub is not used with CGImage drawing. int TkpWillDrawWidget(Tk_Window tkwin) { - int result; - if (tkwin) { - TkWindow *winPtr = (TkWindow *)tkwin; - TKContentView *view = (TKContentView *)TkMacOSXGetNSViewForDrawable( - (Drawable)winPtr->privatePtr); - result = ([NSApp isDrawing] && view == [NSView focusView]); -#if 0 - printf("TkpWillDrawWidget: %s %d %d \n", Tk_PathName(tkwin), - [NSApp isDrawing], (view == [NSView focusView])); - if (!result) { - NSRect dirtyRect; - TkMacOSXWinNSBounds(winPtr, view, &dirtyRect); - printf("TkpAppCanDraw: dirtyRect for %s is %s\n", - Tk_PathName(tkwin), - NSStringFromRect(dirtyRect).UTF8String); - [view addTkDirtyRect:dirtyRect]; - } -#endif - } else { - result = [NSApp isDrawing]; - } - return result; + (void) tkwin; + return false; } /* @@ -527,11 +509,14 @@ GenerateUpdates( TkWindow *childPtr; XEvent event; CGRect bounds, damageBounds; + NSView *view = TkMacOSXGetNSViewForDrawable((Drawable)winPtr->privatePtr); TkMacOSXWinCGBounds(winPtr, &bounds); +#if 0 if (!CGRectIntersectsRect(bounds, *updateBounds)) { return 0; } +#endif /* * Compute the bounding box of the area that the damage occurred in. @@ -548,7 +533,11 @@ 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, @@ -970,22 +959,10 @@ ConfigureRestrictProc( { self = [super initWithFrame:frame]; if (self) { - /* - * The layer must exist before we set wantsLayer to YES. - */ - - self.layer = [CALayer layer]; self.wantsLayer = YES; self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay; self.layer.contentsGravity = self.layer.contentsAreFlipped ? kCAGravityTopLeft : kCAGravityBottomLeft; - - /* - * 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; trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:(NSTrackingMouseEnteredAndExited | @@ -995,18 +972,38 @@ ConfigureRestrictProc( NSTrackingActiveAlways) owner:self userInfo:nil]; - [self addTrackingArea:trackingArea]; + [self addTrackingArea:trackingArea]; } return self; } -/* - * We will just use drawRect. - */ - - (BOOL) wantsUpdateLayer { - return NO; + return YES; +} +- (void) updateLayer { + CGContextRef context = self.tkLayerBitmapContext; + if (context) { + /* + * Create a CGImage by copying (probably using copy-on-write) the + * bitmap data of the CGBitmapContext that we have been using for + * drawing. Then render that CGImage into the CALayer of this view by + * assigning a reference to the CGImage to the contents property of the + * layer. This will cause all drawing done since the last call to this + * function to become visible. + */ + + CGImageRef newImg = CGBitmapContextCreateImage(context); + self.layer.contents = (__bridge id) newImg; + CGImageRelease(newImg); // will quickly leak memory if this is missing + + /* + * Run any pending widget display procs as part of the update. + */ + + while(Tcl_DoOneEvent(TCL_IDLE_EVENTS)){} + [self setTkNeedsDisplay:NO]; + } } #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 @@ -1021,6 +1018,9 @@ ConfigureRestrictProc( */ self.layer.contentsScale = self.window.screen.backingScaleFactor; + [self resetTkLayerBitmapContext]; + // need to redraw + [self generateExposeEvents: self.bounds]; } #endif @@ -1028,84 +1028,39 @@ ConfigureRestrictProc( { _tkNeedsDisplay = YES; _tkDirtyRect = NSUnionRect(_tkDirtyRect, rect); - [NSApp setNeedsToDraw:YES]; - [self setNeedsDisplay:YES]; - [[self layer] setNeedsDisplay]; } - (void) clearTkDirtyRect { _tkNeedsDisplay = NO; _tkDirtyRect = NSZeroRect; - [NSApp setNeedsToDraw:NO]; -} - -- (void) drawRect: (NSRect) rect -{ - (void)rect; - -#ifdef TK_MAC_DEBUG_DRAWING - TkWindow *winPtr = TkMacOSXGetTkWindow([self window]); - if (winPtr) { - fprintf(stderr, "drawRect: drawing %s in %s\n", - Tk_PathName(winPtr), NSStringFromRect(rect).UTF8String); - } -#endif - - /* - * We do not allow recursive calls to drawRect, but we only log them on OSX - * > 10.13, where they should never happen. - */ - - if ([NSApp isDrawing]) { - if ([NSApp macOSVersion] > 101300) { - TKLog(@"WARNING: a recursive call to drawRect was aborted."); - } - return; - } - - [NSApp setIsDrawing: YES]; - [self clearTkDirtyRect]; - [self generateExposeEvents:rect]; - [NSApp setIsDrawing:NO]; - -#ifdef TK_MAC_DEBUG_DRAWING - fprintf(stderr, "drawRect: done.\n"); -#endif } -(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 (![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; + unsigned int width = (unsigned int) newsize.width; + unsigned int height= (unsigned int) newsize.height; void *oldArg; 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. */ @@ -1113,24 +1068,31 @@ 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); + /* - * Now that Tk has configured all subwindows, create the clip regions. + * Update Tk's window data for the new size. */ - TkMacOSXSetDrawingEnabled(winPtr, 1); - TkMacOSXInvalClipRgns(tkwin); - TkMacOSXUpdateClipRgn(winPtr); + if ([w respondsToSelector: @selector (tkLayoutChanged)]) { + [(TKWindow *)w tkLayoutChanged]; + } - /* - * Generate and process expose events to redraw the window. To avoid - * crashes, only do this if we are being called from drawRect. See - * ticket [1fa8c3ed8d]. - */ + /* + * 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([NSApp isDrawing] || [self inLiveResize]) { - [self generateExposeEvents: [self bounds]]; + if ([self inLiveResize]) { + [self generateExposeEvents:self.bounds]; } /* @@ -1138,7 +1100,14 @@ ConfigureRestrictProc( */ [NSApp _unlockAutoreleasePool]; + } + + /* + * Request a call to updateLayer. + */ + + [self setNeedsDisplay:YES]; } /* @@ -1150,53 +1119,62 @@ ConfigureRestrictProc( - (void) generateExposeEvents: (NSRect) rect { - unsigned long serial; - int updatesNeeded; CGRect updateBounds; TkWindow *winPtr = TkMacOSXGetTkWindow([self window]); void *oldArg; Tk_RestrictProc *oldProc; - if (!winPtr) { + static int reentered = 0; + + if (!winPtr || + (winPtr->flags & (TK_ALREADY_DEAD)) || + !Tk_IsMapped(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. */ - - 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 (updatesNeeded) { - - serial = LastKnownRequestProcessed(Tk_Display(winPtr)); - + if ( GenerateUpdates(&updateBounds, winPtr)) { /* - * Use the ExposeRestrictProc to process only the expose events. This - * will create idle drawing tasks, which we handle before we return. + * 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. */ - - oldProc = Tk_RestrictEvents(ExposeRestrictProc, UINT2PTR(serial), &oldArg); - while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS|TCL_DONT_WAIT)) {}; - Tk_RestrictEvents(oldProc, oldArg, &oldArg); + 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); /* - * 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)) {} + if ([self inLiveResize]) { + while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} + } } + reentered = 0; } /* @@ -1242,7 +1220,7 @@ static const char *const accentNames[] = { } NSString *accent = [preferences stringForKey:@"AppleAccentColor"]; NSArray *words = [[preferences stringForKey:@"AppleHighlightColor"] - componentsSeparatedByString: @" "]; + componentsSeparatedByString: @" "]; NSString *highlight = [words count] > 3 ? [words objectAtIndex:3] : nil; const char *accentName = accent ? accentNames[1 + accent.intValue] : defaultColor; const char *highlightName = highlight ? highlight.UTF8String: defaultColor; @@ -1251,6 +1229,7 @@ static const char *const accentNames[] = { effectiveAppearanceName.UTF8String, accentName, highlightName); Tk_SendVirtualEvent(tkwin, "AppearanceChanged", Tcl_NewStringObj(data, TCL_INDEX_NONE)); + [self generateExposeEvents:self.bounds]; } - (void)observeValueForKeyPath:(NSString *)keyPath @@ -1352,6 +1331,32 @@ static const char *const accentNames[] = { return [super validRequestorForSendType:sendType returnType:returnType]; } +-(void) resetTkLayerBitmapContext { + 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, + 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); + fprintf(stderr, "rTkLBC %p %ld\n", self.tkLayerBitmapContext, + (long)(self.tkLayerBitmapContext ? + CFGetRetainCount(self.tkLayerBitmapContext) : LONG_MIN)); +#endif + // The context is also released in TkWmDeadWindow. + CGContextRelease(self.tkLayerBitmapContext); + self.tkLayerBitmapContext = newCtx; +} + @end /* diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index 403025d..fbea56b 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -112,7 +112,7 @@ static const struct { .forceOnAttrs = kWindowNoTitleBarAttribute | kWindowDoesNotCycleAttribute, .flags = WM_TOPMOST, - .styleMask = 0}, + .styleMask = 0}, [kSheetWindowClass] = { .validAttrs = kWindowResizableAttribute, .forceOnAttrs = kWindowNoTitleBarAttribute | @@ -291,6 +291,7 @@ static void syncLayout(NSWindow *macWindow) contentRect.size.width; wmPtr->parentHeight = winPtr->changes.height + frameRect.size.height - contentRect.size.height; + TkMacOSXInvalClipRgns((Tk_Window)winPtr); } } #endif @@ -587,6 +588,8 @@ static void placeAsTab(TKWindow *macWindow) { - (void) tkLayoutChanged { syncLayout(self); + [[self contentView] setNeedsDisplay:YES]; + Tcl_DoWhenIdle(TkMacOSXDrawAllViews, NULL); } @end @@ -606,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 @@ -621,7 +626,7 @@ static void placeAsTab(TKWindow *macWindow) { #endif - (NSSize)windowWillResize:(NSWindow *)sender - toSize:(NSSize)frameSize + toSize:(NSSize)frameSize { NSRect currentFrame = [sender frame]; TkWindow *winPtr = TkMacOSXGetTkWindow(sender); @@ -818,30 +823,29 @@ 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) { - WmInfo *wmPtr = winPtr->wmInfoPtr; 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. */ - 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; + } else if (NSMouseInRect(p, windowFrame, NO)) { + /* + * The pointer is in the title bar of the highest NSWindow + * containing it, and therefore it should not be considered + * to be contained in any Tk window. + */ + return NULL; } } } @@ -1100,12 +1104,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. * *---------------------------------------------------------------------- */ @@ -1114,13 +1121,15 @@ void TkWmDeadWindow( TkWindow *winPtr) /* Top-level window that's being deleted. */ { + TkWindow *winPtr2; + NSWindow *w; WmInfo *wmPtr = winPtr->wmInfoPtr, *wmPtr2; - TKWindow *deadNSWindow; - - if (wmPtr == 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. */ @@ -1172,11 +1181,10 @@ TkWmDeadWindow( for (Transient *transientPtr = wmPtr->transientPtr; transientPtr != NULL; transientPtr = transientPtr->nextPtr) { - TkWindow *winPtr2 = transientPtr->winPtr; - TkWindow *containerPtr = (TkWindow *)TkMacOSXGetContainer(winPtr2); - + TkWindow *containerPtr = (TkWindow *)TkMacOSXGetContainer( + transientPtr->winPtr); if (containerPtr == winPtr) { - wmPtr2 = winPtr2->wmInfoPtr; + wmPtr2 = transientPtr->winPtr->wmInfoPtr; wmPtr2->container = NULL; } } @@ -1188,29 +1196,48 @@ 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]; + [NSApp setTkPointerWindow:nil]; + 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); + /* + * 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]); } } @@ -1223,9 +1250,9 @@ TkWmDeadWindow( if (deadNSWindow && !Tk_IsEmbedded(winPtr)) { NSWindow *parent = [deadNSWindow parentWindow]; [deadNSWindow setTkWindow:None]; - if (winPtr->window) { - ((MacDrawable *)winPtr->window)->view = nil; - } + if (winPtr->window) { + ((MacDrawable *)winPtr->window)->view = nil; + } wmPtr->window = NULL; if (parent) { @@ -1262,9 +1289,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); + for (w in [NSApp orderedWindows]) { + winPtr2 = TkMacOSXGetTkWindow(w); BOOL isOnScreen; if (!winPtr2 || !winPtr2->wmInfoPtr) { @@ -1290,6 +1318,14 @@ TkWmDeadWindow( [NSApp _setKeyWindow:nil]; [NSApp _setMainWindow:nil]; } + + /* + * Avoid redrawing the view after it is released. + */ + + 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]; @@ -1749,7 +1785,7 @@ WmSetAttribute( } else if (![macWindow isKindOfClass: [NSPanel class]] && styleMaskBits[index].allowed == NSWindowClass_panel) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "styleMask bit \"%s\" can only be used with an NSPanel", + "styleMask bit \"%s\" can only be used with an NSPanel", styleMaskBits[index].bitname)); Tcl_SetErrorCode(interp, "TK", "INVALID_STYLEMASK_BIT", NULL); return TCL_ERROR; @@ -1794,15 +1830,15 @@ WmSetAttribute( fprintf(stderr, "Current styleMask: %lx\n", [macWindow styleMask]); fprintf(stderr, "Setting styleMask to %lx\n", styleMaskValue); #endif - macWindow.styleMask = (unsigned long) styleMaskValue; + macWindow.styleMask = (unsigned long) styleMaskValue; NSRect newFrame = [macWindow frame]; int heightDiff = newFrame.size.height - oldFrame.size.height; int newHeight = heightDiff < 0 ? newFrame.size.height : 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); @@ -2390,8 +2426,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; @@ -2410,11 +2445,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] setTkNeedsDisplay:YES]; + } if (wmPtr->icon) { Tk_UnmapWindow((Tk_Window)wmPtr->icon); } @@ -2439,7 +2480,6 @@ WmDeiconifyCmd( } } - [[win contentView] setNeedsDisplay:YES]; Tcl_DoWhenIdle(TkMacOSXDrawAllViews, NULL); return TCL_OK; } @@ -2541,19 +2581,18 @@ WmForgetCmd( macWin->toplevel->referenceCount++; macWin->flags &= ~TK_HOST_EXISTS; - TkWmDeadWindow(winPtr); RemapWindows(winPtr, (MacDrawable *)winPtr->parentPtr->window); - /* - * Make sure wm no longer manages this window - */ - Tk_ManageGeometry(frameWin, NULL, NULL); + /* + * Make sure wm no longer manages this window + */ + Tk_ManageGeometry(frameWin, NULL, NULL); winPtr->flags &= ~(TK_TOP_HIERARCHY|TK_TOP_LEVEL|TK_HAS_WRAPPER|TK_WIN_MANAGED); /* - * Flags (above) must be cleared before calling TkMapTopFrame (below). - */ + * Flags (above) must be cleared before calling TkMapTopFrame (below). + */ TkMapTopFrame(frameWin); } else { @@ -2632,11 +2671,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; @@ -3382,18 +3423,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); @@ -3488,6 +3534,7 @@ WmManageCmd( } else if (Tk_IsTopLevel(frameWin)) { /* Already managed by wm - ignore it */ } + Tk_ManageGeometry((Tk_Window)winPtr, &wmMgrType, NULL); return TCL_OK; } @@ -3626,7 +3673,11 @@ 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?"); @@ -4311,12 +4362,12 @@ WmTransientCmd( RemoveTransient(winPtr); containerPtr = (TkWindow*) container; while (!Tk_TopWinHierarchy(containerPtr)) { - /* - * Ensure that the container window is actually a Tk toplevel. - */ + /* + * Ensure that the container window is actually a Tk toplevel. + */ - containerPtr = containerPtr->parentPtr; - } + containerPtr = containerPtr->parentPtr; + } Tk_MakeWindowExist((Tk_Window)containerPtr); if (wmPtr->iconFor != NULL) { @@ -5216,7 +5267,7 @@ Tk_GetRootCoords( */ winPtr = otherPtr; - continue; + continue; } winPtr = winPtr->parentPtr; } @@ -5619,6 +5670,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( @@ -5676,8 +5736,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 } /* @@ -6027,7 +6093,7 @@ Tk_Window TkMacOSXGetContainer( TkWindow *winPtr) { - if (winPtr->wmInfoPtr != NULL) { + if (Tk_PathName(winPtr)) { return (Tk_Window)winPtr->wmInfoPtr->container; } return NULL; @@ -6072,7 +6138,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: @@ -6088,13 +6154,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; } /* @@ -6119,7 +6184,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]; } @@ -6240,7 +6308,7 @@ TkUnsupported1ObjCmd( case TKMWS_APPEARANCE: if ([NSApp macOSVersion] < 100900) { Tcl_SetObjResult(interp, Tcl_NewStringObj( - "Window appearances did not exist until OSX 10.9.", TCL_INDEX_NONE)); + "Window appearances did not exist until OSX 10.9.", TCL_INDEX_NONE)); Tcl_SetErrorCode(interp, "TK", "WINDOWSTYLE", "APPEARANCE", NULL); return TCL_ERROR; } @@ -6794,7 +6862,7 @@ TkMacOSXMakeRealWindowExist( } if ((styleMask & (NSTexturedBackgroundWindowMask|NSHUDWindowMask)) && !(styleMask & NSDocModalWindowMask)) { - /* + /* * Workaround for [Bug 2824538]: Textured windows are draggable from * opaque content. */ @@ -6832,9 +6900,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. @@ -6847,15 +6917,16 @@ TkMacOSXMakeRealWindowExist( void TkpRedrawWidget(Tk_Window tkwin) { + (void) tkwin; +#if 0 TkWindow *winPtr = (TkWindow *)tkwin; - NSWindow *w; + NSWindow *w = nil; Rect tkBounds; NSRect bounds; - if ([NSApp isDrawing]) { - return; + if (winPtr && winPtr->window) { + w = TkMacOSXGetNSWindowForDrawable(winPtr->window); } - w = TkMacOSXGetNSWindowForDrawable(winPtr->window); if (w) { TKContentView *view = [w contentView]; TkMacOSXWinBounds(winPtr, &tkBounds); @@ -6864,7 +6935,9 @@ TkpRedrawWidget(Tk_Window tkwin) { tkBounds.right - tkBounds.left, tkBounds.bottom - tkBounds.top); [view addTkDirtyRect:bounds]; + [view setNeedsDisplay:YES]; } +#endif } @@ -6987,14 +7060,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, @@ -7181,7 +7255,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; } @@ -7570,9 +7647,9 @@ ApplyContainerOverrideChanges( wmPtr->attributes &= ~kWindowNoActivatesAttribute; if ([NSApp macOSVersion] == 100600) { styleMask = NSTitledWindowMask | - NSClosableWindowMask | - NSMiniaturizableWindowMask | - NSResizableWindowMask; + NSClosableWindowMask | + NSMiniaturizableWindowMask | + NSResizableWindowMask; } else { styleMask |= NSTitledWindowMask; } diff --git a/macosx/ttkMacOSXTheme.c b/macosx/ttkMacOSXTheme.c index ff850f0..2d8d3da 100644 --- a/macosx/ttkMacOSXTheme.c +++ b/macosx/ttkMacOSXTheme.c @@ -214,9 +214,9 @@ static GrayPalette LookupGrayPalette( { const PaletteStateTable *entry = design->palettes; while ((state & entry->onBits) != entry->onBits || - (~state & entry->offBits) != entry->offBits) + (~state & entry->offBits) != entry->offBits) { - ++entry; + ++entry; } return isDark ? entry->dark : entry->light; } @@ -356,12 +356,12 @@ static void GetBackgroundColorRGBA( rgba[i] -= Ttk_ContrastDelta*contrast / 255.0; } } - if (save && winPtr->privatePtr) { - winPtr->privatePtr->flags |= TTK_HAS_CONTRASTING_BG; - for (int i = 0; i < 4; i++) { - winPtr->privatePtr->fillRGBA[i] = rgba[i]; - } - } + if (save && winPtr->privatePtr) { + winPtr->privatePtr->flags |= TTK_HAS_CONTRASTING_BG; + for (int i = 0; i < 4; i++) { + winPtr->privatePtr->fillRGBA[i] = rgba[i]; + } + } } } @@ -931,8 +931,8 @@ DrawHelpSymbol( NSColor *foreground = state & TTK_STATE_DISABLED ? [NSColor disabledControlTextColor] : [NSColor controlTextColor]; NSDictionary *attrs = @{ - NSForegroundColorAttributeName : foreground, - NSFontAttributeName : font + NSForegroundColorAttributeName : foreground, + NSFontAttributeName : font }; NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:@"?" @@ -1505,10 +1505,10 @@ DrawTab( if (!(state & TTK_STATE_SELECTED)) { DrawGrayButton(context, bounds, &tabDesign, state, tkwin); - /* - * Draw a separator line on the left side of the tab if it - * not first. - */ + /* + * Draw a separator line on the left side of the tab if it + * not first. + */ if (!(state & TTK_STATE_FIRST)) { CGContextSaveGState(context); @@ -1653,20 +1653,20 @@ static void ButtonElementMinSize( if (params->heightMetric != NoThemeMetric) { ChkErr(GetThemeMetric, params->heightMetric, minHeight); - /* - * The theme height does not include the 1-pixel border around - * the button, although it does include the 1-pixel shadow at - * the bottom. - */ + /* + * The theme height does not include the 1-pixel border around + * the button, although it does include the 1-pixel shadow at + * the bottom. + */ *minHeight += 2; - /* - * For buttons with labels the minwidth must be 0 to force the - * correct text layout. For example, a non-zero value will cause the - * text to be left justified, no matter what -anchor setting is used in - * the style. - */ + /* + * For buttons with labels the minwidth must be 0 to force the + * correct text layout. For example, a non-zero value will cause the + * text to be left justified, no matter what -anchor setting is used in + * the style. + */ if (params->widthMetric != NoThemeMetric) { ChkErr(GetThemeMetric, params->widthMetric, minWidth); @@ -1700,10 +1700,10 @@ static void ButtonElementSize( return; case TkGradientButton: *paddingPtr = Ttk_MakePadding(1, 1, 1, 1); - /* Fall through. */ + /* Fall through. */ case kThemeArrowButton: case kThemeRoundButtonHelp: - return; + return; /* Buttons which are sized like PushButtons but unknown to HITheme. */ case TkRoundedRectButton: case TkRecessedButton: @@ -1711,7 +1711,7 @@ static void ButtonElementSize( info.kind = kThemePushButton; break; default: - break; + break; } /* @@ -2152,14 +2152,14 @@ static void EntryElementDraw( .isFocused = state & TTK_STATE_FOCUS, }; - /* - * Earlier versions of the Aqua theme ignored the -fieldbackground - * option and used the -background as if it were -fieldbackground. - * Here we are enabling -fieldbackground. For backwards - * compatibility, if -fieldbackground is set to the default color and - * -background is set to a different color then we use -background as - * -fieldbackground. - */ + /* + * Earlier versions of the Aqua theme ignored the -fieldbackground + * option and used the -background as if it were -fieldbackground. + * Here we are enabling -fieldbackground. For backwards + * compatibility, if -fieldbackground is set to the default color and + * -background is set to a different color then we use -background as + * -fieldbackground. + */ if (0 != strcmp(Tcl_GetString(e->fieldbackgroundObj), defaultBG)) { backgroundPtr = @@ -2828,14 +2828,14 @@ static void ThumbElementDraw( CGRect troughBounds = {{macWin->xOff, macWin->yOff}, {Tk_Width(tkwin), Tk_Height(tkwin)}}; - /* - * The info struct has integer fields, which will be converted to - * floats in the drawing routine. All of values provided in the info - * struct, namely min, max, value, and viewSize are only defined up to - * an arbitrary scale factor. To avoid roundoff error we scale so - * that the viewSize is a large float which is smaller than the - * largest int. - */ + /* + * The info struct has integer fields, which will be converted to + * floats in the drawing routine. All of values provided in the info + * struct, namely min, max, value, and viewSize are only defined up to + * an arbitrary scale factor. To avoid roundoff error we scale so + * that the viewSize is a large float which is smaller than the + * largest int. + */ HIThemeTrackDrawInfo info = { .version = 0, @@ -2941,7 +2941,7 @@ static void SeparatorElementDraw( CGRect bounds = BoxToRect(d, b); const HIThemeSeparatorDrawInfo info = { .version = 0, - /* Separator only supports kThemeStateActive, kThemeStateInactive */ + /* Separator only supports kThemeStateActive, kThemeStateInactive */ .state = Ttk_StateTableLookup(ThemeStateTable, state & TTK_STATE_BACKGROUND), }; @@ -3004,7 +3004,7 @@ static void SizegripElementDraw( CGRect bounds = BoxToRect(d, b); HIThemeGrowBoxDrawInfo info = { .version = 0, - /* Grow box only supports kThemeStateActive, kThemeStateInactive */ + /* Grow box only supports kThemeStateActive, kThemeStateInactive */ .state = Ttk_StateTableLookup(ThemeStateTable, state & TTK_STATE_BACKGROUND), .kind = kHIThemeGrowBoxKindNormal, @@ -3295,10 +3295,10 @@ static void TreeHeaderElementDraw( BEGIN_DRAWING(d) if ([NSApp macOSVersion] > 100800) { - /* - * Compensate for the padding added in TreeHeaderElementSize, so - * the larger heading will be drawn at the top of the widget. - */ + /* + * Compensate for the padding added in TreeHeaderElementSize, so + * the larger heading will be drawn at the top of the widget. + */ bounds.origin.y -= 4; DrawListHeader(bounds, dc.context, tkwin, state); @@ -3484,13 +3484,13 @@ TTK_LAYOUT("TSpinbox", TTK_LAYOUT("TEntry", TTK_GROUP("Entry.field", TTK_FILL_BOTH|TTK_BORDER, - TTK_GROUP("Entry.padding", TTK_FILL_BOTH, + TTK_GROUP("Entry.padding", TTK_FILL_BOTH, TTK_NODE("Entry.textarea", TTK_FILL_BOTH)))) /* Searchbox */ TTK_LAYOUT("Searchbox", TTK_GROUP("Searchbox.field", TTK_FILL_BOTH|TTK_BORDER, - TTK_GROUP("Entry.padding", TTK_FILL_BOTH, + TTK_GROUP("Entry.padding", TTK_FILL_BOTH, TTK_NODE("Entry.textarea", TTK_FILL_BOTH)))) /* Progress bars -- track only */ diff --git a/macosx/ttkMacOSXTheme.h b/macosx/ttkMacOSXTheme.h index 1e2b7ae..2092b02 100644 --- a/macosx/ttkMacOSXTheme.h +++ b/macosx/ttkMacOSXTheme.h @@ -561,7 +561,7 @@ static ThemeFrameParams #define CHECK_RADIUS(radius, bounds) \ if ((radius) > (bounds).size.width / 2 || (radius) > (bounds).size.height / 2) { \ - return; \ + return; \ } /* diff --git a/tests/event.test b/tests/event.test index c56d4d8..b43f2d3 100644 --- a/tests/event.test +++ b/tests/event.test @@ -265,6 +265,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] @@ -843,9 +844,9 @@ test event-9.1 {enter . window by destroying a toplevel - bug b1d115fa60} -setup 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 + wm iconify . + update + set iconified true } } -body { toplevel .top1 @@ -869,12 +870,318 @@ test event-9.2 {enter toplevel window by destroying a toplevel - bug b1d115fa60} } -cleanup { deleteWindows ; # destroy all children of ".", this already includes .top1 if {$iconified} { - wm deiconify . - update + 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 +# 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 stderr "wait 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 + update idletasks +} + +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+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 + tkwait visibility $w + update; # service remaining screen drawing events (e.g. <Expose>) + set pointerWin [winfo containing [winfo pointerx $w] [winfo pointery $w]] + event generate $w <Motion> -warp 1 -x 250 -y 250 + if {($pointerWin ne $w) && ([tk windowingsystem] ne "aqua")} { + waitForWindowEvent $w <Enter> + } 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; # needed for Windows + update idletasks; # finish display of window + set result "|" +} -body { + bind all <Leave> {append result "<Leave> %d %W|"} + bind all <Enter> {append result "<Enter> %d %W|"} + destroy .one.f1.f2 + update + set result +} -cleanup { + bind all <Leave> {} + bind all <Enter> {} + destroy .one + unset result +} -result {|<Enter> 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 <Motion> -warp 1 -x 250 -y 250 + _pause 200; # needed for Windows + set result "|" +} -body { + bind all <Leave> {append result "<Leave> %d %W|"} + bind all <Enter> {append result "<Enter> %d %W|"} + destroy .one.g + update + set result +} -cleanup { + bind all <Leave> {} + bind all <Enter> {} + destroy .one + unset result +} -result {|<Enter> NotifyNonlinearVirtual .one.f1|<Enter> NotifyNonlinear .one.f1.f2|} + +test event-9.13 {pointer window is a toplevel, toplevel destination} -setup { + setup_win_mousepointer .one + toplevel .two + wm geometry .two 300x300+150+150 + wm withdraw .two + wm deiconify .two + waitForWindowEvent .two <Enter> + update idletasks; # finish displaying windows + set result | +} -body { + bind all <Leave> {append result "<Leave> %d %W|"} + bind all <Enter> {append result "<Enter> %d %W|"} + destroy .two + waitForWindowEvent .one <Enter> + update + set result +} -cleanup { + bind all <Leave> {} + bind all <Enter> {} + destroy .one + unset result +} -result {|<Enter> 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 <Enter> + set result "|" +} -body { + bind all <Leave> {append result "<Leave> %d %W|"} + bind all <Enter> {append result "<Enter> %d %W|"} + destroy .two + waitForWindowEvent .one.f1.f2 <Enter> + set result +} -cleanup { + bind all <Leave> {} + bind all <Enter> {} + destroy .one + unset result +} -result {|<Enter> NotifyNonlinearVirtual .one|<Enter> NotifyNonlinearVirtual .one.f1|<Enter> 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) +# destroy .one + toplevel .two + wm geometry .two 300x300+150+150 + wm deiconify .two + waitForWindowEvent .two <Enter> + update idletasks; # finish displaying .two + event generate .two <Motion> -warp 1 -x 275 -y 275 + controlPointerWarpTiming + set result "|" +} -body { + bind all <Leave> {append result "<Leave> %d %W|"} + bind all <Enter> {append result "<Enter> %d %W|"} + destroy .two + set result +} -cleanup { + bind all <Leave> {} + bind all <Enter> {} + 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 + update idletasks; # finish displaying window + _pause 200; # needed for Windows + set result "|" +} -body { + bind all <Leave> {append result "<Leave> %d %W|"} + bind all <Enter> {append result "<Enter> %d %W|"} + destroy .one.f1 + update + set result +} -cleanup { + bind all <Leave> {} + bind all <Enter> {} + destroy .one + unset result +} -result {|<Enter> 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 + update idletasks; # finish displaying window + _pause 200; # needed for Windows + set result "|" +} -body { + bind all <Leave> {append result "<Leave> %d %W|"} + bind all <Enter> {append result "<Enter> %d %W|"} + destroy .one.f1.f2 + update; # make sure window is gone + destroy .one.f1 + update; # make sure window is gone + set result +} -cleanup { + bind all <Leave> {} + bind all <Enter> {} + destroy .one + unset result +} -result {|<Enter> NotifyInferior .one.f1|<Enter> 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 <Enter> + set result "|" +} -body { + bind all <Leave> {append result "<Leave> %d %W|"} + bind all <Enter> {append result "<Enter> %d %W|"} + destroy .two + waitForWindowEvent .one <Enter> + set result +} -cleanup { + bind all <Leave> {} + bind all <Enter> {} + destroy .one + unset result +} -result {|<Enter> 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 + create_and_pack_frames .two + wm deiconify .two + toplevel .three + pack propagate .three 0 + wm geometry .three 300x300+110+110 + create_and_pack_frames .three + wm deiconify .three + waitForWindowEvent .three.f1.f2 <Enter> + update idletasks; # finish displaying windows + set result "|" +} -body { + bind all <Leave> {append result "<Leave> %d %W|"} + bind all <Enter> {append result "<Enter> %d %W|"} + destroy .three + waitForWindowEvent .two.f1.f2 <Enter> + update idletasks; #finish destroying .two + set result +} -cleanup { + bind all <Leave> {} + bind all <Enter> {} + destroy .one + destroy .two + unset result +} -result {|<Enter> NotifyNonlinearVirtual .two|<Enter> NotifyNonlinearVirtual .two.f1|<Enter> 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) + destroy .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 <Enter> + set result "|" +} -body { + bind all <Leave> {append result "<Leave> %d %W|"} + bind all <Enter> {append result "<Enter> %d %W|"} + destroy .two + update idletasks; #finish destroying .two + set result +} -cleanup { + bind all <Leave> {} + bind all <Enter> {} + unset result +} -result {|} + # cleanup +# macOS sometimes has trouble deleting the test window, +# causing a failure in focus.test. +_pause 200; +deleteWindows update unset -nocomplain keypress_lookup rename _init_keypress_lookup {} @@ -883,6 +1190,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 diff --git a/tests/focus.test b/tests/focus.test index 03563bc..17cdd58 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/font.test b/tests/font.test index ca38269..4c1f0de 100644 --- a/tests/font.test +++ b/tests/font.test @@ -2434,15 +2434,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" @@ -2468,6 +2468,7 @@ test font-47.2 {Bug 3049518 - Canvas} -body { apply $circle_text $c FontChanged $textid $circid update bind $c <<TkWorldChanged>> [list apply $circle_text %W %d $textid $circid] + update idletasks # Begin test: set results {} diff --git a/tests/image.test b/tests/image.test index c7b6511..cdac20d 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/pack.test b/tests/pack.test index 0731125..201bf9f 100644 --- a/tests/pack.test +++ b/tests/pack.test @@ -1553,6 +1553,11 @@ test pack-17.2 {PackLostContentProc procedure} -setup { # into account while the window is unmapped. # pack-18.1.2 checks that, on Windows, width/height changes are taken into # account on window remapping. +# +# While these tests pass on macOS, one can see by watching the tests +# that the window .pack is sometimes black, even though the frame is +# colored. So, evidently, even though the size changes are honored, +# the window is sometimes not completely configured. test pack-18.1.1 {unmap content when container unmapped} -constraints { macOrUnix failsOnUbuntu failsOnXQuarz } -setup { @@ -1562,7 +1567,8 @@ test pack-18.1.1 {unmap content when container unmapped} -constraints { # as the screen (screen switch causes scale and other tests to fail). wm geometry .pack +100+100 } -body { - frame .pack.a -width 100 -height 50 -relief raised -bd 2 + frame .pack.a -width 100 -height 50 -relief raised -bd 2 -bg green + after 100 pack .pack.a update set result [winfo ismapped .pack.a] @@ -1585,7 +1591,7 @@ test pack-18.1.2 {unmap content when container unmapped} -constraints { # as the screen (screen switch causes scale and other tests to fail). wm geometry .pack +100+100 } -body { - frame .pack.a -width 100 -height 50 -relief raised -bd 2 + frame .pack.a -width 100 -height 50 -relief raised -bd 2 -bg green pack .pack.a update set result [winfo ismapped .pack.a] @@ -1606,8 +1612,8 @@ test pack-18.2 {unmap content when container unmapped} -constraints {failsOnUbun # as the screen (screen switch causes scale and other tests to fail). wm geometry .pack +100+100 } -body { - frame .pack.a -relief raised -bd 2 - frame .pack.b -width 70 -height 30 -relief sunken -bd 2 + frame .pack.a -relief raised -bd 2 -bg green + frame .pack.b -width 70 -height 30 -relief sunken -bd 2 -bg red pack .pack.a pack .pack.b -in .pack.a update diff --git a/tests/raise.test b/tests/raise.test index 2e9e2c6..7e6b0bd 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 diff --git a/tests/textDisp.test b/tests/textDisp.test index 7583bc4..67df965 100644 --- a/tests/textDisp.test +++ b/tests/textDisp.test @@ -1107,22 +1107,28 @@ 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 update list $tk_textEmbWinDisplay diff --git a/tests/ttk/ttk.test b/tests/ttk/ttk.test index 6963a2a..d099c40 100644 --- a/tests/ttk/ttk.test +++ b/tests/ttk/ttk.test @@ -136,6 +136,8 @@ test ttk-selfdestruct-ok-1 "Intentional self-destruction" -body { # Basic tests. # test ttk-1.1 "Create multiline button showing justified text" -body { + wm geometry . +100+100 + event generate . <Motion> -warp 1 -x 600 -y 600 pack [ttk::button .t -text "Hello\nWorld!!" -justify center] -expand true -fill both update } @@ -152,6 +154,8 @@ test ttk-1.4 "Original style preserved" -body { .t cget -style } -result "" +# Tests using this will fail if the top-level window contains the cursor + proc checkstate {w} { foreach statespec { {!active !disabled} @@ -166,7 +170,6 @@ proc checkstate {w} { set result } -# NB: this will fail if the top-level window pops up underneath the cursor test ttk-2.0 "Check state" -body { checkstate .t } -result [list 1 0 0 0 0 0] @@ -342,7 +345,7 @@ test ttk-8.1 "Test -compound options" -body { # Exhaustively test each combination. # Main goal is to make sure no code paths crash. foreach image {icon ""} { - foreach text {"Hi!" ""} { + foreach text {"Hi!" ""} { foreach compound $::compoundStrings { .ctb configure -image $image -text $text -compound $compound update; tick @@ -357,7 +360,7 @@ test ttk-8.2 "Test -compound options with regular button" -body { pack .rtb foreach image {"" icon} { - foreach text {"Hi!" ""} { + foreach text {"Hi!" ""} { foreach compound [lrange $::compoundStrings 2 end] { .rtb configure -image $image -text $text -compound $compound update; tick @@ -369,7 +372,7 @@ tock test ttk-8.3 "Rerun test 8.1" -body { foreach image {icon ""} { - foreach text {"Hi!" ""} { + foreach text {"Hi!" ""} { foreach compound $::compoundStrings { .ctb configure -image $image -text $text -compound $compound update; tick diff --git a/tests/unixWm.test b/tests/unixWm.test index 2ad40e2..9194796 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} @@ -105,6 +93,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 @@ -1373,7 +1362,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 @@ -1798,6 +1789,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. @@ -1834,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] @@ -1855,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 @@ -1888,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 @@ -1897,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 } {{} .} @@ -1979,21 +1967,22 @@ 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 + after 200 update toplevel .t2 -width 200 -height 200 -bg red tkwait visibility .t2 + wm geometry .t2 +20+20 + after 200 update - wm geometry .t2 +0+0 - update - restackDelay - set result [list [winfo containing 100 100]] - wm iconify .t2 + set result [list [winfo containing 120 120]] + destroy .t2 + after 200 update - lappend result [winfo containing 100 100] + lappend result [winfo containing 120 120] } {.t2 .t} test unixWm-50.10 {Tk_CoordsToWindow procedure, unmapped windows} unix { destroy .t @@ -2071,7 +2060,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. @@ -2083,15 +2071,15 @@ test unixWm-51.7 {TkWmRestackToplevel procedure, other window isn't mapped} {uni toplevel $w -width 200 -height 200 -bg green tkwait visibility $w wm geometry $w +100+100 + after 200 update } + 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 { @@ -2105,7 +2093,6 @@ 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 @@ -2116,15 +2103,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} { @@ -2132,12 +2118,11 @@ 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 - restackDelay update # Need to use vrootx and vrooty to make tests work correctly with @@ -2149,11 +2134,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 diff --git a/tests/window.test b/tests/window.test index 8a56d5a..892ceea 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 {} <Motion> -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 diff --git a/tests/winfo.test b/tests/winfo.test index d4cc1ff..908647b 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 diff --git a/tests/wm.test b/tests/wm.test index d913006..650292d 100644 --- a/tests/wm.test +++ b/tests/wm.test @@ -1084,6 +1084,8 @@ 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 set result {} } -body { diff --git a/tests/xmfbox.test b/tests/xmfbox.test index 89eda3c..0d3b4d3 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 file normalize [file join {*}[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 file normalize [file join {*}[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 diff --git a/win/tkWinWm.c b/win/tkWinWm.c index 7938539..ba52b32 100644 --- a/win/tkWinWm.c +++ b/win/tkWinWm.c @@ -2461,6 +2461,33 @@ TkpWmGetState( *-------------------------------------------------------------- */ +static void CheckForPointer(TkWindow *winPtr) +{ + POINT mouse; + int x, y; + unsigned int state = TkWinGetModifierState(); + TkWindow **windows = TkWmStackorderToplevel(winPtr->mainPtr->winPtr); + TkWindow **w; + TkGetPointerCoords(NULL, &x, &y); + mouse.x = x; + mouse.y = y; + if (windows != NULL) { + for (w = windows; *w ; w++) { + RECT windowRect; + HWND hwnd = Tk_GetHWND(Tk_WindowId((Tk_Window) *w)); + if (GetWindowRect(hwnd, &windowRect) == 0) { + continue; + } + if (winPtr != *w && PtInRect(&windowRect, mouse)) { + Tk_Window target = Tk_CoordsToWindow(x, y, (Tk_Window) *w); + Tk_UpdatePointer((Tk_Window) target, x, y, state); + break; + } + } + ckfree(windows); + } +} + void TkWmDeadWindow( TkWindow *winPtr) /* Top-level window that's being deleted. */ @@ -2599,6 +2626,13 @@ 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); ckfree(wmPtr); winPtr->wmInfoPtr = NULL; } @@ -6590,8 +6624,6 @@ TkWmStackorderToplevelEnumProc( TkWmStackorderToplevelPair *pair = (TkWmStackorderToplevelPair *) lParam; - /*fprintf(stderr, "Looking up HWND %d\n", hwnd);*/ - hPtr = Tcl_FindHashEntry(pair->table, hwnd); if (hPtr != NULL) { childWinPtr = (TkWindow *)Tcl_GetHashValue(hPtr); |