From 31f82fb5b5ae52d1893a87e7a5a83c0e9eae2f58 Mon Sep 17 00:00:00 2001 From: culler Date: Mon, 5 Nov 2018 16:03:56 +0000 Subject: Make XSync, and hence update, be synchronous so test results are consistent. Fix duplicate reports of calls to the test image displayProc. --- generic/tkTest.c | 32 +++++++++++++++++++++++--------- macosx/tkMacOSXConstants.h | 2 ++ macosx/tkMacOSXEvent.c | 25 +++++++++++++++++++------ macosx/tkMacOSXInt.h | 2 +- macosx/tkMacOSXNotify.c | 2 +- macosx/tkMacOSXTest.c | 26 ++++++++++++++++++++++++++ macosx/tkMacOSXWindowEvent.c | 19 +++++++++++++++++-- 7 files changed, 89 insertions(+), 19 deletions(-) diff --git a/generic/tkTest.c b/generic/tkTest.c index 2dbd877..e95d274 100644 --- a/generic/tkTest.c +++ b/generic/tkTest.c @@ -31,6 +31,9 @@ #if defined(MAC_OSX_TK) #include "tkMacOSXInt.h" #include "tkScrollbar.h" +#define APP_IS_DRAWING (TkTestAppIsDrawing()) +#else +#define APP_IS_DRAWING 1 #endif #ifdef __UNIX__ @@ -1550,15 +1553,26 @@ ImageDisplay( TImageInstance *instPtr = (TImageInstance *) clientData; char buffer[200 + TCL_INTEGER_SPACE * 6]; - 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; + /* + * 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. + */ + 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; + } } XDrawRectangle(display, drawable, instPtr->gc, drawableX, drawableY, (unsigned) (width-1), (unsigned) (height-1)); diff --git a/macosx/tkMacOSXConstants.h b/macosx/tkMacOSXConstants.h index b160083..dbe533c 100644 --- a/macosx/tkMacOSXConstants.h +++ b/macosx/tkMacOSXConstants.h @@ -40,6 +40,7 @@ #if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200 #define NSAppKitDefined NSEventTypeAppKitDefined +#define NSApplicationDefined NSEventTypeApplicationDefined #define NSApplicationActivatedEventType NSEventSubtypeApplicationActivated #define NSApplicationDeactivatedEventType NSEventSubtypeApplicationDeactivated #define NSWindowExposedEventType NSEventSubtypeWindowExposed @@ -92,6 +93,7 @@ #define NSAlphaShiftKeyMask NSEventModifierFlagCapsLock #define NSShiftKeyMask NSEventModifierFlagShift #define NSAnyEventMask NSEventMaskAny +#define NSApplicationDefinedEventMask NSEventMaskApplicationDefined #define NSTexturedBackgroundWindowMask NSWindowStyleMaskTexturedBackground #define NSUtilityWindowMask NSWindowStyleMaskUtilityWindow #define NSNonactivatingPanelMask NSWindowStyleMaskNonactivatingPanel diff --git a/macosx/tkMacOSXEvent.c b/macosx/tkMacOSXEvent.c index 25c0bea..36167cb 100644 --- a/macosx/tkMacOSXEvent.c +++ b/macosx/tkMacOSXEvent.c @@ -112,11 +112,16 @@ enum { * * TkMacOSXFlushWindows -- * - * This routine is a stub called by XSync, which is called during - * the Tk update command. It calls displayIfNeeded on all visible - * windows. This is necessary in order to insure that update will - * run all of the display procedures which have been registered as - * idle tasks. The test suite assumes that this is the case. + * This routine is a stub called by XSync, which is called during the Tk + * update command. The language specification does not require that the + * update command be synchronous but many of the tests assume that is the + * case. It is not naturally the case on macOS since many idle tasks are + * run inside of the drawRect method of a window's contentView, and that + * method will not be called until after this function returns. To make + * the tests work, we attempt to force this to be synchronous by waiting + * until drawRect has been called for each window. The mechanism we use + * for this is to have drawRect post an ApplicationDefined NSEvent on the + * AppKit event queue when it finishes drawing, and wait for it here. * * Results: * None. @@ -131,11 +136,19 @@ enum { MODULE_SCOPE void TkMacOSXFlushWindows(void) { + NSModalSession modalSession = TkMacOSXGetModalSession(); + NSEvent *syncEvent; NSArray *macWindows = [NSApp orderedWindows]; - for (NSWindow *w in macWindows) { if (TkMacOSXGetXWindow(w)) { [w displayIfNeeded]; + syncEvent = [NSApp + nextEventMatchingMask:NSApplicationDefinedEventMask + untilDate:[NSDate distantPast] + inMode:GetRunLoopMode(modalSession) + dequeue:YES]; + [NSApp discardEventsMatchingMask:NSApplicationDefinedEventMask + beforeEvent:syncEvent]; } } diff --git a/macosx/tkMacOSXInt.h b/macosx/tkMacOSXInt.h index 09fbed3..6108383 100644 --- a/macosx/tkMacOSXInt.h +++ b/macosx/tkMacOSXInt.h @@ -200,7 +200,7 @@ 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); /* * Include the stubbed internal platform-specific API. */ diff --git a/macosx/tkMacOSXNotify.c b/macosx/tkMacOSXNotify.c index e276926..7cbd248 100644 --- a/macosx/tkMacOSXNotify.c +++ b/macosx/tkMacOSXNotify.c @@ -169,7 +169,7 @@ void DebugPrintQueue(void) *---------------------------------------------------------------------- */ -static NSString * +NSString * GetRunLoopMode(NSModalSession modalSession) { NSString *runLoopMode = nil; diff --git a/macosx/tkMacOSXTest.c b/macosx/tkMacOSXTest.c index 1882ce6..92c925c 100644 --- a/macosx/tkMacOSXTest.c +++ b/macosx/tkMacOSXTest.c @@ -80,6 +80,32 @@ DebuggerObjCmd( } /* + *---------------------------------------------------------------------- + * + * TkTestAppIsDrawing -- + * + * 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; + * + * Side effects: + * None + * + *---------------------------------------------------------------------- + */ +MODULE_SCOPE Bool +TkTestAppIsDrawing(void) { + return [NSApp isDrawing]; +} + + + +/* * Local Variables: * mode: objc * c-basic-offset: 4 diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 19e4ba2..f69ee0c 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -861,6 +861,22 @@ 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 @@ -981,8 +997,7 @@ ConfigureRestrictProc( * * Fortunately, Tk schedules all drawing to be done while Tcl is idle. * So we can do the drawing by processing all of the idle events that - * were created when the expose events were processed. Unfortunately - * this does not work in macOS 10.13. + * were created when the expose events were processed. */ while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} } -- cgit v0.12