From 1cd3199254c47a7fd9e308663776b9e540d527c2 Mon Sep 17 00:00:00 2001 From: culler Date: Tue, 6 Nov 2018 21:21:50 +0000 Subject: Came up with a scheme for making test images behave the way that the tests assume they do. --- generic/tkTest.c | 48 ++++++++++++++++++++++++++------------------ macosx/tkMacOSXDraw.c | 21 ++++++++++++++++--- macosx/tkMacOSXEvent.c | 20 +++++++----------- macosx/tkMacOSXInit.c | 7 +++++-- macosx/tkMacOSXInt.h | 3 ++- macosx/tkMacOSXPrivate.h | 2 ++ macosx/tkMacOSXTest.c | 23 ++++++++++++--------- macosx/tkMacOSXWindowEvent.c | 27 ++++++++----------------- 8 files changed, 83 insertions(+), 68 deletions(-) diff --git a/generic/tkTest.c b/generic/tkTest.c index e95d274..5609391 100644 --- a/generic/tkTest.c +++ b/generic/tkTest.c @@ -31,9 +31,9 @@ #if defined(MAC_OSX_TK) #include "tkMacOSXInt.h" #include "tkScrollbar.h" -#define APP_IS_DRAWING (TkTestAppIsDrawing()) +#define SIMULATE_DRAWING TkTestSimulateDrawing(true); #else -#define APP_IS_DRAWING 1 +#define SIMULATE_DRAWING #endif #ifdef __UNIX__ @@ -1554,26 +1554,34 @@ ImageDisplay( char buffer[200 + TCL_INTEGER_SPACE * 6]; /* - * On macOS the fake drawing below will not take place when this - * displayProc is run as an idle task. That is because the idle task will - * not have a valid graphics context available. Instead, the window will - * be marked as needing redisplay and will get redrawn in the next call to - * its contentView's drawRect method. We only record the display - * information when the actual drawing takes place to avoid duplicate - * records which would cause some image tests to fail. + * The purpose of the test image type is to track the calls to an image + * display proc and record the parameters passed in each call. On macOS + * these tests will fail because of the asynchronous drawing. The low + * level graphics calls below which are supposed to draw a rectangle will + * not draw anything to the screen because the idle task will not be + * processed inside of the drawRect method and hence will not be able to + * obtain a valid graphics context. Instead, the window will be marked as + * needing display, and will be redrawn during a future asynchronous call + * to drawRect. This will generate an other call to this display proc, + * and the recorded data will show extra calls, causing the test to fail. + * To avoid this, we can set the [NSApp simulateDrawing] flag, which will + * cause all low level drawing routines to return immediately and not + * schedule the window for drawing later. This flag is cleared by the + * next call to XSync, which is called by the update command. */ - if (APP_IS_DRAWING) { - sprintf(buffer, "%s display %d %d %d %d", - instPtr->masterPtr->imageName, imageX, imageY, width, height); - Tcl_SetVar2(instPtr->masterPtr->interp, instPtr->masterPtr->varName, NULL, - buffer, TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT); - if (width > (instPtr->masterPtr->width - imageX)) { - width = instPtr->masterPtr->width - imageX; - } - if (height > (instPtr->masterPtr->height - imageY)) { - height = instPtr->masterPtr->height - imageY; - } + + sprintf(buffer, "%s display %d %d %d %d", + instPtr->masterPtr->imageName, imageX, imageY, width, height); + Tcl_SetVar2(instPtr->masterPtr->interp, instPtr->masterPtr->varName, NULL, + buffer, TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT); + if (width > (instPtr->masterPtr->width - imageX)) { + width = instPtr->masterPtr->width - imageX; + } + if (height > (instPtr->masterPtr->height - imageY)) { + height = instPtr->masterPtr->height - imageY; } + + SIMULATE_DRAWING XDrawRectangle(display, drawable, instPtr->gc, drawableX, drawableY, (unsigned) (width-1), (unsigned) (height-1)); XDrawLine(display, drawable, instPtr->gc, drawableX, drawableY, diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index 94e8778..42c9059 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -1448,7 +1448,7 @@ TkMacOSXSetUpGraphicsPort( *---------------------------------------------------------------------- */ -int +Bool TkMacOSXSetupDrawingContext( Drawable d, GC gc, @@ -1462,16 +1462,26 @@ TkMacOSXSetupDrawingContext( CGRect clipBounds; /* + * If we are simulating drawing for tests, just return false. + */ + + if ([NSApp simulateDrawing]) { + return false; + } + + /* * If the drawable is not a pixmap and it has an associated - * NSWindow then we are drawing to a window. + * NSWindow then we know we are drawing to a window. */ + if (!(macDraw->flags & TK_IS_PIXMAP)) { win = TkMacOSXDrawableWindow(d); } - + /* * Check that we have a non-empty clipping region. */ + dc.clipRgn = TkMacOSXGetClipRgn(d); ClipToGC(d, gc, &dc.clipRgn); if (dc.clipRgn && HIShapeIsEmpty(dc.clipRgn)) { @@ -1484,12 +1494,14 @@ TkMacOSXSetupDrawingContext( * are drawing to a window then we can get one from the * window. */ + dc.context = TkMacOSXGetCGContextForDrawable(d); if (dc.context) { dc.portBounds = clipBounds = CGContextGetClipBoundingBox(dc.context); } else if (win) { NSView *view = TkMacOSXDrawableView(macDraw); if (view) { + /* * We can only draw into the view when the current CGContext is * valid and belongs to the view. Validity can only be guaranteed @@ -1501,6 +1513,7 @@ TkMacOSXSetupDrawingContext( * then we mark our view as needing display and return failure. * It should get drawn in a later call to drawRect. */ + if (view != [NSView focusView]) { [view setNeedsDisplay:YES]; canDraw = false; @@ -1520,9 +1533,11 @@ TkMacOSXSetupDrawingContext( Tcl_Panic("TkMacOSXSetupDrawingContext(): " "no context to draw into !"); } + /* * Configure the drawing context. */ + if (dc.context) { CGAffineTransform t = { .a = 1, .b = 0, .c = 0, .d = -1, .tx = 0, .ty = dc.portBounds.size.height}; diff --git a/macosx/tkMacOSXEvent.c b/macosx/tkMacOSXEvent.c index 676344c..798c73c 100644 --- a/macosx/tkMacOSXEvent.c +++ b/macosx/tkMacOSXEvent.c @@ -136,22 +136,16 @@ enum { MODULE_SCOPE void TkMacOSXFlushWindows(void) { - NSModalSession modalSession = TkMacOSXGetModalSession(); - NSEvent *syncEvent; NSArray *macWindows = [NSApp orderedWindows]; + if ([NSApp simulateDrawing]) { + [NSApp setSimulateDrawing:NO]; + return; + } for (NSWindow *w in macWindows) { - if (TkMacOSXGetXWindow(w)) { - [w displayIfNeeded]; - syncEvent = [NSApp - nextEventMatchingMask:NSApplicationDefinedMask - untilDate:[NSDate distantPast] - inMode:GetRunLoopMode(modalSession) - dequeue:YES]; - [NSApp discardEventsMatchingMask:NSApplicationDefinedMask - beforeEvent:syncEvent]; - } + if (TkMacOSXGetXWindow(w)) { + [w displayIfNeeded]; + } } - } diff --git a/macosx/tkMacOSXInit.c b/macosx/tkMacOSXInit.c index 10ef87e..8e9c600 100644 --- a/macosx/tkMacOSXInit.c +++ b/macosx/tkMacOSXInit.c @@ -47,6 +47,7 @@ static char scriptPath[PATH_MAX + 1] = ""; @synthesize poolLock = _poolLock; @synthesize macMinorVersion = _macMinorVersion; @synthesize isDrawing = _isDrawing; +@synthesize simulateDrawing = _simulateDrawing; @end /* @@ -162,13 +163,14 @@ static char scriptPath[PATH_MAX + 1] = ""; systemVersion = [[NSProcessInfo processInfo] operatingSystemVersion]; minorVersion = systemVersion.minorVersion; #endif - [NSApp setMacMinorVersion: minorVersion]; + [NSApp setMacMinorVersion: minorVersion]; /* * We are not drawing yet. */ - [NSApp setIsDrawing: NO]; + [NSApp setIsDrawing:NO]; + [NSApp setSimulateDrawing:NO]; /* * Be our own delegate. @@ -178,6 +180,7 @@ static char scriptPath[PATH_MAX + 1] = ""; /* * Make sure we are allowed to open windows. */ + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; /* diff --git a/macosx/tkMacOSXInt.h b/macosx/tkMacOSXInt.h index 6108383..f04d9dc 100644 --- a/macosx/tkMacOSXInt.h +++ b/macosx/tkMacOSXInt.h @@ -200,7 +200,8 @@ MODULE_SCOPE void TkpReleaseRegion(TkRegion r); MODULE_SCOPE void TkpShiftButton(NSButton *button, NSPoint delta); MODULE_SCOPE Bool TkpAppIsDrawing(void); MODULE_SCOPE void TkpDisplayWindow(Tk_Window tkwin); -MODULE_SCOPE NSString* GetRunLoopMode(NSModalSession modalSession); +MODULE_SCOPE void TkTestSimulateDrawing(Bool); + /* * Include the stubbed internal platform-specific API. */ diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h index f5924e5..5eef949 100644 --- a/macosx/tkMacOSXPrivate.h +++ b/macosx/tkMacOSXPrivate.h @@ -266,11 +266,13 @@ VISIBILITY_HIDDEN int _poolLock; int _macMinorVersion; Bool _isDrawing; + Bool _simulateDrawing; #endif } @property int poolLock; @property int macMinorVersion; @property Bool isDrawing; +@property Bool simulateDrawing; @end @interface TKApplication(TKInit) diff --git a/macosx/tkMacOSXTest.c b/macosx/tkMacOSXTest.c index 92c925c..5576c44 100644 --- a/macosx/tkMacOSXTest.c +++ b/macosx/tkMacOSXTest.c @@ -82,25 +82,28 @@ DebuggerObjCmd( /* *---------------------------------------------------------------------- * - * TkTestAppIsDrawing -- + * TkTestSimulateDrawing -- + * + * A test widget display procedure which records calls can use this to + * avoid duplicate calls which would occur due to fact that no valid + * graphics context is available to the idle task which is running the + * display proc. Note that no actual drawing to the screen will take + * place when this flag is set. This is just a wrapper for the NSApp + * property. * - * A widget display procedure can call this to determine whether it - * is being run inside of the drawRect method. This is needed for - * some tests, especially of the Text widget, which record data in - * a global Tcl variable and assume that display procedures will be - * run in a predictable sequence as Tcl idle tasks. * * Results: - * True only while running the drawRect method of a TKContentView; + * Calls to low level drawing routines will return without actually + * drawing anything to the screen. * * Side effects: * None * *---------------------------------------------------------------------- */ -MODULE_SCOPE Bool -TkTestAppIsDrawing(void) { - return [NSApp isDrawing]; +MODULE_SCOPE void +TkTestSimulateDrawing(Bool yesno) { + [NSApp setSimulateDrawing:yesno]; } diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index f69ee0c..724a9af 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -832,14 +832,19 @@ ConfigureRestrictProc( Tk_PathName(winPtr)); #endif + if ([NSApp simulateDrawing]) { + return; + } + /* - * We do not allow recursive calls to drawRect. Only log this - * in 10.14 and higher, where it should not happen. + * We do not allow recursive calls to drawRect. */ - if ([NSApp isDrawing] && [NSApp macMinorVersion] > 13) { + + if ([NSApp isDrawing]) { TKLog(@"WARNING: a recursive call to drawRect was aborted."); return; } + [NSApp setIsDrawing: YES]; [self getRectsBeingDrawn:&rectsBeingDrawn count:&rectsBeingDrawnCount]; @@ -861,22 +866,6 @@ ConfigureRestrictProc( CFRelease(drawShape); [NSApp setIsDrawing: NO]; - /* - * To make it possible to wait for this method, we post an application - * defined event when all drawing has been done. - */ - NSEvent* drawEvent = [NSEvent otherEventWithType:NSApplicationDefined - location:NSMakePoint(0,0) - modifierFlags:0 - timestamp:0 - windowNumber:[[self window] windowNumber] - context:nil - subtype:1 - data1:0 - data2:0 - ]; - [NSApp postEvent:drawEvent atStart:YES]; - #ifdef TK_MAC_DEBUG_DRAWING fprintf(stderr, "drawRect: done.\n"); #endif -- cgit v0.12