summaryrefslogtreecommitdiffstats
path: root/macosx
diff options
context:
space:
mode:
authormarc_culler <marc.culler@gmail.com>2020-07-18 16:53:31 (GMT)
committermarc_culler <marc.culler@gmail.com>2020-07-18 16:53:31 (GMT)
commitb63b41199525b0e3e0e95f65e6991f75f8e31dd5 (patch)
tree40c8cd9613fe3160eb087628d192d6ee40a35487 /macosx
parent451fa4203bff3f5e6a0ab5028f900823174b4c83 (diff)
parent132c939d5ab36c6a55d7bc9e1692fd981c9a88ca (diff)
downloadtk-b63b41199525b0e3e0e95f65e6991f75f8e31dd5.zip
tk-b63b41199525b0e3e0e95f65e6991f75f8e31dd5.tar.gz
tk-b63b41199525b0e3e0e95f65e6991f75f8e31dd5.tar.bz2
Provide better control over how and when [NSView drawRect:] is called.
Diffstat (limited to 'macosx')
-rw-r--r--macosx/tkMacOSXDraw.c82
-rw-r--r--macosx/tkMacOSXEmbed.c4
-rw-r--r--macosx/tkMacOSXEvent.c35
-rw-r--r--macosx/tkMacOSXEvent.h4
-rw-r--r--macosx/tkMacOSXInt.h2
-rw-r--r--macosx/tkMacOSXKeyEvent.c12
-rw-r--r--macosx/tkMacOSXNotify.c166
-rw-r--r--macosx/tkMacOSXPrivate.h15
-rw-r--r--macosx/tkMacOSXSubwindows.c69
-rw-r--r--macosx/tkMacOSXTest.c30
-rw-r--r--macosx/tkMacOSXWindowEvent.c271
-rw-r--r--macosx/tkMacOSXWm.c20
-rw-r--r--macosx/tkMacOSXXStubs.c15
13 files changed, 416 insertions, 309 deletions
diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c
index e56e666..7d12a4b 100644
--- a/macosx/tkMacOSXDraw.c
+++ b/macosx/tkMacOSXDraw.c
@@ -174,7 +174,9 @@ TkMacOSXBitmapRepFromDrawableRect(
if (cg_image) {
CGImageRelease(cg_image);
}
- } else if ((view = TkMacOSXDrawableView(mac_drawable)) != NULL) {
+ } else if (TkMacOSXDrawableView(mac_drawable) != NULL) {
+ TKContentView *tkview = (TKContentView *)view;
+
/*
* Convert Tk top-left to NSView bottom-left coordinates.
*/
@@ -197,7 +199,7 @@ TkMacOSXBitmapRepFromDrawableRect(
[view cacheDisplayInRect:view_rect toBitmapImageRep:bitmap_rep];
} else {
TkMacOSXDbgMsg("No CGContext - cannot copy from screen to bitmap.");
- [view setNeedsDisplay:YES];
+ [tkview addTkDirtyRect:[tkview bounds]];
return NULL;
}
} else {
@@ -1622,47 +1624,53 @@ TkMacOSXSetupDrawingContext(
if (dc.context) {
dc.portBounds = clipBounds = CGContextGetClipBoundingBox(dc.context);
} else if (win) {
- NSView *view = TkMacOSXDrawableView(macDraw);
+ TKContentView *view = (TKContentView *)TkMacOSXDrawableView(macDraw);
if (!view) {
Tcl_Panic("TkMacOSXSetupDrawingContext(): "
"no NSView to draw into !");
}
+ if (dc.clipRgn) {
+ 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);
+ }
+ if (view != [NSView focusView]) {
- /*
- * We can only draw into the view when the current CGContext is valid
- * and belongs to the view. Validity can only be guaranteed inside of
- * a view's drawRect or setFrame methods. The isDrawing attribute
- * tells us whether we are being called from one of those methods.
- *
- * If the CGContext is not valid then we mark our view as needing
- * display in the bounding rectangle of the clipping region and
- * return failure. That rectangle should get drawn in a later call
- * to drawRect.
- *
- * As an exception to the above, if mouse buttons are pressed at the
- * moment when we fail to obtain a valid context we schedule the entire
- * view for a redraw rather than just the clipping region. The purpose
- * of this is to make sure that scrollbars get updated correctly.
- */
+ /*
+ * We can only draw into the view when the current CGContext is
+ * valid and belongs to the view. Validity can only be guaranteed
+ * inside of a view's drawRect or setFrame methods. The isDrawing
+ * attribute tells us whether we are being called from drawRect.
+ * If the CGContext is not valid then we mark our view as needing
+ * display.
+ */
- if (![NSApp isDrawing] || view != [NSView focusView]) {
- NSRect bounds = [view bounds];
- NSRect dirtyNS = bounds;
- if ([NSEvent pressedMouseButtons]) {
- [view setNeedsDisplay:YES];
+ if (dc.clipRgn) {
+ [view addTkDirtyRect:NSRectFromCGRect(clipBounds)];
} else {
- CGAffineTransform t = { .a = 1, .b = 0, .c = 0, .d = -1, .tx = 0,
- .ty = dirtyNS.size.height};
- if (dc.clipRgn) {
- CGRect dirtyCG = NSRectToCGRect(dirtyNS);
- HIShapeGetBounds(dc.clipRgn, &dirtyCG);
- dirtyNS = NSRectToCGRect(CGRectApplyAffineTransform(dirtyCG, t));
- }
- [view setNeedsDisplayInRect:dirtyNS];
+ [view addTkDirtyRect:[view bounds]];
}
canDraw = false;
goto end;
+ } else if (dc.clipRgn) {
+
+ /*
+ * Drawing will also fail if we are being called from drawRect but
+ * the clipping rectangle set by drawRect does not contain the
+ * clipping region of our drawing context. See bug [2a61eca3a8].
+ * If we can't draw all of the clipping region of the drawing
+ * context then we draw whatever we can, but we also add a dirty
+ * rectangle so the entire widget will get redrawn in the next
+ * cycle.
+ */
+
+ CGRect currentClip = CGContextGetClipBoundingBox(
+ [NSGraphicsContext currentContext].CGContext);
+ if (!NSContainsRect(currentClip, clipBounds)) {
+ [view addTkDirtyRect:clipBounds];
+ }
}
dc.view = view;
@@ -1694,6 +1702,7 @@ TkMacOSXSetupDrawingContext(
CGContextSetTextDrawingMode(dc.context, kCGTextFill);
CGContextConcatCTM(dc.context, t);
if (dc.clipRgn) {
+
#ifdef TK_MAC_DEBUG_DRAWING
CGContextSaveGState(dc.context);
ChkErr(HIShapeReplacePathInCGContext, dc.clipRgn, dc.context);
@@ -1701,13 +1710,14 @@ TkMacOSXSetupDrawingContext(
CGContextEOFillPath(dc.context);
CGContextRestoreGState(dc.context);
#endif /* TK_MAC_DEBUG_DRAWING */
+
CGRect r;
if (!HIShapeIsRectangular(dc.clipRgn) || !CGRectContainsRect(
- *HIShapeGetBounds(dc.clipRgn, &r),
- CGRectApplyAffineTransform(clipBounds, t))) {
- ChkErr(HIShapeReplacePathInCGContext, dc.clipRgn, dc.context);
- CGContextEOClip(dc.context);
+ *HIShapeGetBounds(dc.clipRgn, &r),
+ CGRectApplyAffineTransform(clipBounds, t))) {
+ ChkErr(HIShapeReplacePathInCGContext, dc.clipRgn, dc.context);
+ CGContextEOClip(dc.context);
}
}
if (gc) {
diff --git a/macosx/tkMacOSXEmbed.c b/macosx/tkMacOSXEmbed.c
index 6c4ac1f..e58cdd0 100644
--- a/macosx/tkMacOSXEmbed.c
+++ b/macosx/tkMacOSXEmbed.c
@@ -1049,9 +1049,7 @@ EmbedGeometryRequest(
*/
Tk_GeometryRequest((Tk_Window) winPtr, width, height);
- while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {
- /* Empty loop body. */
- }
+ while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_TIMER_EVENTS|TCL_DONT_WAIT)) {}
if ((winPtr->changes.width != width)
|| (winPtr->changes.height != height)) {
EmbedSendConfigure(containerPtr);
diff --git a/macosx/tkMacOSXEvent.c b/macosx/tkMacOSXEvent.c
index b9c9b6a..31a7663 100644
--- a/macosx/tkMacOSXEvent.c
+++ b/macosx/tkMacOSXEvent.c
@@ -104,43 +104,8 @@ enum {
return processedEvent;
}
@end
-
#pragma mark -
-
-/*
- *----------------------------------------------------------------------
- *
- * TkMacOSXFlushWindows --
- *
- * 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 implicitly assume
- * that it is. It is definitely asynchronous on macOS since many idle
- * tasks are run inside of the drawRect method of a window's contentView,
- * which will not be called until after this function returns.
- *
- * Results:
- * None.
- *
- * Side effects: Processes all pending idle events then calls the display
- * method of each visible window.
- *
- *----------------------------------------------------------------------
- */
-
-MODULE_SCOPE void
-TkMacOSXFlushWindows(void)
-{
- if (Tk_GetNumMainWindows() == 0) {
- return;
- }
- while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)){}
- for (NSWindow *w in [NSApp orderedWindows]) {
- [w display];
- }
-}
-
/*
* Local Variables:
* mode: objc
diff --git a/macosx/tkMacOSXEvent.h b/macosx/tkMacOSXEvent.h
index 46d1585..850e9f6 100644
--- a/macosx/tkMacOSXEvent.h
+++ b/macosx/tkMacOSXEvent.h
@@ -18,6 +18,8 @@
#include "tkMacOSXInt.h"
#endif
-MODULE_SCOPE void TkMacOSXFlushWindows(void);
+/*
+ * Currently nothing needs to be declared here.
+ */
#endif
diff --git a/macosx/tkMacOSXInt.h b/macosx/tkMacOSXInt.h
index ee2ab75..89d76ce 100644
--- a/macosx/tkMacOSXInt.h
+++ b/macosx/tkMacOSXInt.h
@@ -200,7 +200,7 @@ MODULE_SCOPE void TkpClipDrawableToRect(Display *display, Drawable d, int x,
MODULE_SCOPE void TkpRetainRegion(TkRegion r);
MODULE_SCOPE void TkpReleaseRegion(TkRegion r);
MODULE_SCOPE void TkpShiftButton(NSButton *button, NSPoint delta);
-MODULE_SCOPE Bool TkTestLogDisplay(void);
+MODULE_SCOPE Bool TkTestLogDisplay(Drawable drawable);
MODULE_SCOPE Bool TkMacOSXInDarkMode(Tk_Window tkwin);
/*
diff --git a/macosx/tkMacOSXKeyEvent.c b/macosx/tkMacOSXKeyEvent.c
index b0d91ad..572e318 100644
--- a/macosx/tkMacOSXKeyEvent.c
+++ b/macosx/tkMacOSXKeyEvent.c
@@ -255,12 +255,11 @@ static NSUInteger textInputModifiers;
*/
if (type == NSKeyDown && [theEvent isARepeat]) {
+
xEvent.xany.type = KeyRelease;
Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
xEvent.xany.type = KeyPress;
}
- if (xEvent.xany.type == KeyPress) {
- }
Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
return theEvent;
}
@@ -269,14 +268,6 @@ static NSUInteger textInputModifiers;
@implementation TKContentView
--(id)init {
- self = [super init];
- if (self) {
- _needsRedisplay = NO;
- }
- return self;
-}
-
/*
* Implementation of the NSTextInputClient protocol.
*/
@@ -552,7 +543,6 @@ static NSUInteger textInputModifiers;
}
/* End of NSTextInputClient implementation. */
-@synthesize needsRedisplay = _needsRedisplay;
@end
diff --git a/macosx/tkMacOSXNotify.c b/macosx/tkMacOSXNotify.c
index 6120ae5..367f3b6 100644
--- a/macosx/tkMacOSXNotify.c
+++ b/macosx/tkMacOSXNotify.c
@@ -261,7 +261,6 @@ Tk_MacOSXSetupTkNotifier(void)
Tcl_CreateEventSource(TkMacOSXEventsSetupProc,
TkMacOSXEventsCheckProc, NULL);
TkCreateExitHandler(TkMacOSXNotifyExitHandler, NULL);
- Tcl_SetServiceMode(TCL_SERVICE_ALL);
TclMacOSXNotifierAddRunLoopMode(NSEventTrackingRunLoopMode);
TclMacOSXNotifierAddRunLoopMode(NSModalPanelRunLoopMode);
}
@@ -299,6 +298,86 @@ TkMacOSXNotifyExitHandler(
/*
*----------------------------------------------------------------------
*
+ * TkMacOSXDrawAllViews --
+ *
+ * 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
+ * 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 time when any NSViews have the needsDisplay property set to YES
+ * is during execution of this function.
+ *
+ * The reason for running this function as an idle task is to try to
+ * arrange that all widgets will be fully configured before they are
+ * drawn. Any idle tasks that might reconfigure them should be higher on
+ * the idle queue, so they should be run before the display procs are run
+ * by drawRect.
+ *
+ * If this function is called directly with non-NULL clientData parameter
+ * then the int which it references will be set to the number of windows
+ * that need display, but the needsDisplay property of those windows will
+ * not be changed.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Parts of windows my get redrawn.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkMacOSXDrawAllViews(
+ ClientData clientData)
+{
+ int count = 0, *dirtyCount = (int *)clientData;
+
+ for (NSWindow *window in [NSApp windows]) {
+ if ([[window contentView] isMemberOfClass:[TKContentView class]]) {
+ TKContentView *view = [window contentView];
+ if ([view tkNeedsDisplay]) {
+ count++;
+ if (dirtyCount) {
+ continue;
+ }
+ [view setNeedsDisplayInRect:[view tkDirtyRect]];
+ }
+ } else {
+ [window displayIfNeeded];
+ }
+ }
+ if (dirtyCount) {
+ *dirtyCount = count;
+ }
+ [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];
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TkMacOSXEventsSetupProc --
*
* This procedure implements the setup part of the MacOSX event source. It
@@ -312,18 +391,33 @@ TkMacOSXNotifyExitHandler(
*
* Side effects:
*
- * If NSEvents are queued, then the maximum block time will be set to 0 to
- * ensure that control returns immediately to Tcl.
+ * If NSEvents are queued, or if there is any drawing that needs to be
+ * done, then the maximum block time will be set to 0 to ensure that
+ * Tcl_WaitForEvent returns immediately.
*
*----------------------------------------------------------------------
*/
+#define TICK 200
+static Tcl_TimerToken ticker = NULL;
+
+static void
+Heartbeat(
+ ClientData clientData)
+{
+
+ if (ticker) {
+ ticker = Tcl_CreateTimerHandler(TICK, Heartbeat, NULL);
+ }
+}
+
+static const Tcl_Time zeroBlockTime = { 0, 0 };
+
static void
TkMacOSXEventsSetupProc(
ClientData clientData,
int flags)
{
- static Bool havePeriodicEvents = NO;
NSString *runloopMode = [[NSRunLoop currentRunLoop] currentMode];
/*
@@ -331,35 +425,39 @@ TkMacOSXEventsSetupProc(
*/
if (flags & TCL_WINDOW_EVENTS && !runloopMode) {
- static const Tcl_Time zeroBlockTime = { 0, 0 };
+
[NSApp _resetAutoreleasePool];
- /*
- * Call this with dequeue=NO -- just checking if the queue is empty.
- */
+ /*
+ * After calling this setup proc, Tcl_DoOneEvent will call
+ * Tcl_WaitForEvent. Then it will call check proc to collect the
+ * events and translate them into XEvents.
+ *
+ * If we have any events waiting or if there is any drawing to be done
+ * we want Tcl_WaitForEvent to return immediately. So we set the block
+ * time to 0 and stop the heatbeat.
+ */
NSEvent *currentEvent =
[NSApp nextEventMatchingMask:NSAnyEventMask
untilDate:[NSDate distantPast]
inMode:GetRunLoopMode(TkMacOSXGetModalSession())
dequeue:NO];
- if (currentEvent) {
- if (currentEvent.type > 0) {
- Tcl_SetMaxBlockTime(&zeroBlockTime);
- [NSEvent stopPeriodicEvents];
- havePeriodicEvents = NO;
- }
- } else if (!havePeriodicEvents){
+ if ((currentEvent) || [NSApp needsToDraw] ) {
+ Tcl_SetMaxBlockTime(&zeroBlockTime);
+ Tcl_DeleteTimerHandler(ticker);
+ ticker = NULL;
+ } else if (ticker == NULL) {
/*
- * When the user is not generating events we schedule a "hearbeat"
- * event to fire every 0.1 seconds. This helps to make the vwait
- * command more responsive when there is no user input, e.g. when
- * running the test suite.
+ * When the user is not generating events we schedule a "heartbeat"
+ * TimerHandler to fire every 200 milliseconds. The handler does
+ * nothing, but when its timer fires it causes Tcl_WaitForEvent to
+ * return. This helps avoid hangs when calling vwait during the
+ * non-regression tests.
*/
- havePeriodicEvents = YES;
- [NSEvent startPeriodicEventsAfterDelay:0.0 withPeriod:0.1];
+ ticker = Tcl_CreateTimerHandler(TICK, Heartbeat, NULL);
}
}
}
@@ -387,6 +485,7 @@ TkMacOSXEventsCheckProc(
int flags)
{
NSString *runloopMode = [[NSRunLoop currentRunLoop] currentMode];
+ int eventsFound = 0;
/*
* runloopMode will be nil if we are in a Tcl event loop.
@@ -425,17 +524,19 @@ TkMacOSXEventsCheckProc(
inMode:GetRunLoopMode(modalSession)
dequeue:YES];
if (currentEvent) {
+
/*
* Generate Xevents.
*/
- int oldServiceMode = Tcl_SetServiceMode(TCL_SERVICE_ALL);
NSEvent *processedEvent = [NSApp tkProcessEvent:currentEvent];
- Tcl_SetServiceMode(oldServiceMode);
if (processedEvent) {
+ eventsFound++;
+
#ifdef TK_MAC_DEBUG_EVENTS
TKLog(@" event: %@", currentEvent);
#endif
+
if (modalSession) {
[NSApp _modalSession:modalSession sendEvent:currentEvent];
} else {
@@ -452,6 +553,25 @@ TkMacOSXEventsCheckProc(
*/
[NSApp _unlockAutoreleasePool];
+
+ /*
+ * Add an idle task to the end of the idle queue which will redisplay
+ * all of our dirty windows. We want this to happen after all other
+ * idle tasks have run so that all widgets will be configured before
+ * they are displayed. The drawRect method "borrows" the idle queue
+ * while drawing views. That is, it sends expose events which cause
+ * display procs to be posted as idle tasks and then runs an inner
+ * event loop to processes those idle tasks. We are trying to arrange
+ * for the idle queue to be empty when it starts that process and empty
+ * when it finishes.
+ */
+
+ int dirtyCount = 0;
+ TkMacOSXDrawAllViews(&dirtyCount);
+ if (dirtyCount > 0) {
+ Tcl_CancelIdleCall(TkMacOSXDrawAllViews, NULL);
+ Tcl_DoWhenIdle(TkMacOSXDrawAllViews, NULL);
+ }
}
}
diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h
index 1266c8b..a77282c 100644
--- a/macosx/tkMacOSXPrivate.h
+++ b/macosx/tkMacOSXPrivate.h
@@ -307,6 +307,9 @@ MODULE_SCOPE int TkMacOSXRegisterServiceWidgetObjCmd(ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]);
MODULE_SCOPE unsigned TkMacOSXAddVirtual(unsigned int keycode);
+MODULE_SCOPE void TkMacOSXWinNSBounds(TkWindow *winPtr, NSView *view,
+ NSRect *bounds);
+MODULE_SCOPE void TkMacOSXDrawAllViews(ClientData clientData);
#pragma mark Private Objective-C Classes
@@ -342,11 +345,13 @@ VISIBILITY_HIDDEN
int _poolLock;
int _macOSVersion; /* 10000 * major + 100*minor */
Bool _isDrawing;
+ Bool _needsToDraw;
#endif
}
@property int poolLock;
@property int macOSVersion;
@property Bool isDrawing;
+@property Bool needsToDraw;
@end
@interface TKApplication(TKInit)
@@ -417,9 +422,11 @@ VISIBILITY_HIDDEN
{
@private
NSString *privateWorkingText;
- Bool _needsRedisplay;
+ Bool _tkNeedsDisplay;
+ NSRect _tkDirtyRect;
}
-@property Bool needsRedisplay;
+@property Bool tkNeedsDisplay;
+@property NSRect tkDirtyRect;
@end
@interface TKContentView(TKKeyEvent)
@@ -428,7 +435,9 @@ VISIBILITY_HIDDEN
@end
@interface TKContentView(TKWindowEvent)
-- (void) generateExposeEvents: (HIShapeRef) shape;
+- (void) addTkDirtyRect: (NSRect) rect;
+- (void) clearTkDirtyRect;
+- (void) generateExposeEvents: (NSRect) rect;
- (void) tkToolbarButton: (id) sender;
@end
diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c
index b085ba4..8bae8fd 100644
--- a/macosx/tkMacOSXSubwindows.c
+++ b/macosx/tkMacOSXSubwindows.c
@@ -136,6 +136,9 @@ XMapWindow(
Display *display, /* Display. */
Window window) /* Window. */
{
+ if (!window) {
+ return BadWindow;
+ }
MacDrawable *macWin = (MacDrawable *) window;
TkWindow *winPtr = macWin->winPtr;
NSWindow *win = TkMacOSXDrawableWindow(window);
@@ -157,6 +160,7 @@ XMapWindow(
winPtr->flags |= TK_MAPPED;
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
@@ -169,7 +173,7 @@ XMapWindow(
TkMacOSXApplyWindowAttributes(winPtr, win);
[win setExcludedFromWindowsMenu:NO];
[NSApp activateIgnoringOtherApps:NO];
- [[win contentView] setNeedsDisplay:YES];
+ [view addTkDirtyRect: [view bounds]];
if ([win canBecomeKeyWindow]) {
[win makeKeyAndOrderFront:NSApp];
} else {
@@ -212,10 +216,9 @@ XMapWindow(
TkMacOSXInvalClipRgns((Tk_Window) winPtr->parentPtr);
}
- if ([NSApp isDrawing]) {
- [[win contentView] setNeedsRedisplay:YES];
- } else {
- [[win contentView] setNeedsDisplay:YES];
+ TKContentView *view = [win contentView];
+ if (view != [NSView focusView]) {
+ [view addTkDirtyRect:[view bounds]];
}
/*
@@ -329,10 +332,9 @@ XUnmapWindow(
TkMacOSXUpdateClipRgn(parentPtr);
}
winPtr->flags &= ~TK_MAPPED;
- if ([NSApp isDrawing]) {
- [[win contentView] setNeedsRedisplay:YES];
- } else {
- [[win contentView] setNeedsDisplay:YES];
+ TKContentView *view = [win contentView];
+ if (view != [NSView focusView]) {
+ [view addTkDirtyRect:[view bounds]];
}
return Success;
}
@@ -986,7 +988,8 @@ InvalViewRect(
void *ref)
{
static CGAffineTransform t;
- NSView *view = ref;
+ TKContentView *view = ref;
+ NSRect dirtyRect;
if (!view) {
return paramErr;
@@ -997,8 +1000,8 @@ InvalViewRect(
NSHeight([view bounds]));
break;
case kHIShapeEnumerateRect:
- [view setNeedsDisplayInRect:NSRectFromCGRect(
- CGRectApplyAffineTransform(*rect, t))];
+ dirtyRect = NSRectFromCGRect(CGRectApplyAffineTransform(*rect, t));
+ [view addTkDirtyRect:dirtyRect];
break;
}
return noErr;
@@ -1284,7 +1287,7 @@ TkMacOSXInvalClipRgns(
* None.
*
* Side effects:
- * None.
+ * Fills in a Rect.
*
*----------------------------------------------------------------------
*/
@@ -1308,15 +1311,14 @@ TkMacOSXWinBounds(
* TkMacOSXWinCGBounds --
*
* Given a Tk window this function determines the window's bounds in
- * relation to the Macintosh window's coordinate system. This is also the
- * same coordinate system as the Tk toplevel window in which this window
- * is contained.
+ * the coordinate system of the Tk toplevel window in which this window
+ * is contained. This fills in a CGRect struct.
*
* Results:
* None.
*
* Side effects:
- * None.
+ * Fill in a CGRect.
*
*----------------------------------------------------------------------
*/
@@ -1331,6 +1333,39 @@ TkMacOSXWinCGBounds(
bounds->size.width = winPtr->changes.width;
bounds->size.height = winPtr->changes.height;
}
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkMacOSXWinNSBounds --
+ *
+ * Given a Tk window this function determines the window's bounds in
+ * the coordinate system of the TKContentView in which this Tk window
+ * is contained, which has the origin at the lower left corner. This
+ * fills in an NSRect struct and requires the TKContentView as a
+ * parameter
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Fills in an NSRect.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkMacOSXWinNSBounds(
+ TkWindow *winPtr,
+ NSView *view,
+ NSRect *bounds)
+{
+ bounds->size.width = winPtr->changes.width;
+ bounds->size.height = winPtr->changes.height;
+ bounds->origin.x = winPtr->privatePtr->xOff;
+ bounds->origin.y = ([view bounds].size.height -
+ bounds->size.height -
+ winPtr->privatePtr->yOff);
+}
/*
*----------------------------------------------------------------------
diff --git a/macosx/tkMacOSXTest.c b/macosx/tkMacOSXTest.c
index a5d1d83..39f50e6 100644
--- a/macosx/tkMacOSXTest.c
+++ b/macosx/tkMacOSXTest.c
@@ -14,6 +14,8 @@
#include "tkMacOSXPrivate.h"
#include "tkMacOSXConstants.h"
+#include "tkMacOSXWm.h"
+
/*
* Forward declarations of procedures defined later in this file:
@@ -108,7 +110,8 @@ DebuggerObjCmd(
* first call will usually not occur inside of drawRect.
*
* Results:
- * On OSX 10.14 and later, returns true if and only if called from
+ * On OSX 10.14 and later, returns true if and only if the NSView of the
+ * drawable is the current focusView, which can only be the case when
* within [NSView drawRect]. On earlier systems returns false if
* and only if called from with [NSView drawRect].
*
@@ -118,9 +121,28 @@ DebuggerObjCmd(
*----------------------------------------------------------------------
*/
MODULE_SCOPE Bool
-TkTestLogDisplay(void) {
- if ([NSApp macOSVersion] >= 101400) {
- return [NSApp isDrawing];
+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;
+ }/*
+ else if (macWin->toplevel && (macWin->toplevel->flags & TK_EMBEDDED)) {
+ TkWindow *contWinPtr = TkpGetOtherWindow(macWin->toplevel->winPtr);
+ if (contWinPtr) {
+ win = TkMacOSXDrawableWindow((Drawable) contWinPtr->privatePtr);
+ }
+ }*/
+ if (win && [NSApp macOSVersion] >= 101400) {
+ TKContentView *view = [win contentView];
+ return (view == [NSView focusView]);
} else {
return ![NSApp isDrawing];
}
diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c
index 17f8585..5f52ea7 100644
--- a/macosx/tkMacOSXWindowEvent.c
+++ b/macosx/tkMacOSXWindowEvent.c
@@ -30,17 +30,17 @@
* Declaration of functions used only in this file
*/
-static int GenerateUpdates(HIShapeRef updateRgn,
- CGRect *updateBounds, TkWindow *winPtr);
+static int GenerateUpdates(
+ CGRect *updateBounds, TkWindow *winPtr);
static int GenerateActivateEvents(TkWindow *winPtr,
int activeFlag);
-static void DoWindowActivate(ClientData clientData);
#pragma mark TKApplication(TKWindowEvent)
-#ifdef TK_MAC_DEBUG_NOTIFICATIONS
-extern NSString *NSWindowWillOrderOnScreenNotification;
extern NSString *NSWindowDidOrderOnScreenNotification;
+extern NSString *NSWindowWillOrderOnScreenNotification;
+
+#ifdef TK_MAC_DEBUG_NOTIFICATIONS
extern NSString *NSWindowDidOrderOffScreenNotification;
#endif
@@ -90,14 +90,11 @@ extern NSString *NSWindowDidOrderOffScreenNotification;
height = bounds.size.height - wmPtr->yInParent;
flags |= TK_SIZE_CHANGED;
}
- if (Tcl_GetServiceMode() != TCL_SERVICE_NONE) {
- /*
- * Propagate geometry changes immediately.
- */
-
- flags |= TK_MACOSX_HANDLE_EVENT_IMMEDIATELY;
- }
+ /*
+ * Propagate geometry changes immediately.
+ */
+ flags |= TK_MACOSX_HANDLE_EVENT_IMMEDIATELY;
TkGenWMConfigureEvent((Tk_Window) winPtr, x, y, width, height, flags);
}
@@ -115,24 +112,20 @@ extern NSString *NSWindowDidOrderOffScreenNotification;
winPtr->wmInfoPtr->hints.initial_state =
TkMacOSXIsWindowZoomed(winPtr) ? ZoomState : NormalState;
Tk_MapWindow((Tk_Window) winPtr);
- if (Tcl_GetServiceMode() != TCL_SERVICE_NONE) {
- /*
- * Process all Tk events generated by Tk_MapWindow().
- */
+ /*
+ * Process all Tk events generated by Tk_MapWindow().
+ */
- while (Tcl_ServiceEvent(0)) {}
- while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {}
+ while (Tcl_ServiceEvent(0)) {}
+ while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {}
- /*
- * NSWindowDidDeminiaturizeNotification is received after
- * NSWindowDidBecomeKeyNotification, so activate manually
- */
+ /*
+ * NSWindowDidDeminiaturizeNotification is received after
+ * NSWindowDidBecomeKeyNotification, so activate manually
+ */
- GenerateActivateEvents(winPtr, 1);
- } else {
- Tcl_DoWhenIdle(DoWindowActivate, winPtr);
- }
+ GenerateActivateEvents(winPtr, 1);
}
}
@@ -208,35 +201,41 @@ extern NSString *NSWindowDidOrderOffScreenNotification;
return (winPtr ? NO : YES);
}
-#ifdef TK_MAC_DEBUG_NOTIFICATIONS
-
-- (void) windowDragStart: (NSNotification *) notification
-{
- TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification);
-}
-
-- (void) windowLiveResize: (NSNotification *) notification
+- (void) windowBecameVisible: (NSNotification *) notification
{
- TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification);
- //BOOL start = [[notification name] isEqualToString:NSWindowWillStartLiveResizeNotification];
+ NSWindow *window = [notification object];
+ TkWindow *winPtr = TkMacOSXGetTkWindow(window);
+ if (winPtr) {
+ TKContentView *view = [window contentView];
+ [view addTkDirtyRect:[view bounds]];
+ Tcl_CancelIdleCall(TkMacOSXDrawAllViews, NULL);
+ Tcl_DoWhenIdle(TkMacOSXDrawAllViews, NULL);
+ }
}
- (void) windowMapped: (NSNotification *) notification
{
- TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification);
NSWindow *w = [notification object];
TkWindow *winPtr = TkMacOSXGetTkWindow(w);
if (winPtr) {
- //Tk_MapWindow((Tk_Window) winPtr);
+ while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {}
}
}
-- (void) windowBecameVisible: (NSNotification *) notification
+#ifdef TK_MAC_DEBUG_NOTIFICATIONS
+
+- (void) windowDragStart: (NSNotification *) notification
{
TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification);
}
+- (void) windowLiveResize: (NSNotification *) notification
+{
+ TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification);
+ //BOOL start = [[notification name] isEqualToString:NSWindowWillStartLiveResizeNotification];
+}
+
- (void) windowUnmapped: (NSNotification *) notification
{
TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification);
@@ -263,6 +262,8 @@ extern NSString *NSWindowDidOrderOffScreenNotification;
observe(NSWindowDidResizeNotification, windowBoundsChanged:);
observe(NSWindowDidDeminiaturizeNotification, windowExpanded:);
observe(NSWindowDidMiniaturizeNotification, windowCollapsed:);
+ observe(NSWindowWillOrderOnScreenNotification, windowMapped:);
+ observe(NSWindowDidOrderOnScreenNotification, windowBecameVisible:);
#if !(MAC_OS_X_VERSION_MAX_ALLOWED < 1070)
observe(NSWindowDidEnterFullScreenNotification, windowEnteredFullScreen:);
@@ -273,8 +274,6 @@ extern NSString *NSWindowDidOrderOffScreenNotification;
observe(NSWindowWillMoveNotification, windowDragStart:);
observe(NSWindowWillStartLiveResizeNotification, windowLiveResize:);
observe(NSWindowDidEndLiveResizeNotification, windowLiveResize:);
- observe(NSWindowWillOrderOnScreenNotification, windowMapped:);
- observe(NSWindowDidOrderOnScreenNotification, windowBecameVisible:);
observe(NSWindowDidOrderOffScreenNotification, windowUnmapped:);
#endif
#undef observe
@@ -399,24 +398,36 @@ extern NSString *NSWindowDidOrderOffScreenNotification;
* tkwin NULL or pointing to a widget in the current focusView.
*
* Side effects:
- * The tkwin parameter may be recorded to handle redrawing the widget
- * later.
+ * Currently none. One day the tkwin parameter may be recorded to
+ * handle redrawing the widget later.
*
*----------------------------------------------------------------------
*/
int
TkpWillDrawWidget(Tk_Window tkwin) {
- if (![NSApp isDrawing]) {
- return 0;
- }
+ int result;
if (tkwin) {
TkWindow *winPtr = (TkWindow *)tkwin;
- NSView *view = TkMacOSXDrawableView(winPtr->privatePtr);
- return (view == [NSView focusView]);
+ TKContentView *view = (TKContentView *) TkMacOSXDrawableView(
+ 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 {
- return 1;
+ result = [NSApp isDrawing];
}
+ return result;
}
/*
@@ -424,7 +435,7 @@ TkpWillDrawWidget(Tk_Window tkwin) {
*
* GenerateUpdates --
*
- * Given a Macintosh update region and a Tk window this function generates
+ * Given an update rectangle and a Tk window, this function generates
* an X Expose event for the window if it meets the update region. The
* function will then recursively have each damaged window generate Expose
* events for its child windows.
@@ -440,39 +451,23 @@ TkpWillDrawWidget(Tk_Window tkwin) {
static int
GenerateUpdates(
- HIShapeRef updateRgn,
CGRect *updateBounds,
TkWindow *winPtr)
{
TkWindow *childPtr;
XEvent event;
CGRect bounds, damageBounds;
- HIShapeRef boundsRgn, damageRgn;
TkMacOSXWinCGBounds(winPtr, &bounds);
if (!CGRectIntersectsRect(bounds, *updateBounds)) {
return 0;
}
- if (!HIShapeIntersectsRect(updateRgn, &bounds)) {
- return 0;
- }
/*
* Compute the bounding box of the area that the damage occurred in.
*/
- boundsRgn = HIShapeCreateWithRect(&bounds);
- damageRgn = HIShapeCreateIntersection(updateRgn, boundsRgn);
- if (HIShapeIsEmpty(damageRgn)) {
- CFRelease(damageRgn);
- CFRelease(boundsRgn);
- return 0;
- }
- HIShapeGetBounds(damageRgn, &damageBounds);
-
- CFRelease(damageRgn);
- CFRelease(boundsRgn);
-
+ damageBounds = CGRectIntersection(bounds, *updateBounds);
event.xany.serial = LastKnownRequestProcessed(Tk_Display(winPtr));
event.xany.send_event = false;
event.xany.window = Tk_WindowId(winPtr);
@@ -486,7 +481,7 @@ GenerateUpdates(
Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
#ifdef TK_MAC_DEBUG_DRAWING
- TKLog(@"Expose %p {{%d, %d}, {%d, %d}}", event.xany.window, event.xexpose.x,
+ TKLog(@"Exposed %p {{%d, %d}, {%d, %d}}", event.xany.window, event.xexpose.x,
event.xexpose.y, event.xexpose.width, event.xexpose.height);
#endif
@@ -499,7 +494,7 @@ GenerateUpdates(
if (!Tk_IsMapped(childPtr) || Tk_IsTopLevel(childPtr)) {
continue;
}
- GenerateUpdates(updateRgn, updateBounds, childPtr);
+ GenerateUpdates(updateBounds, childPtr);
}
/*
@@ -509,7 +504,7 @@ GenerateUpdates(
if (Tk_IsContainer(winPtr)) {
childPtr = TkpGetOtherWindow(winPtr);
if (childPtr != NULL && Tk_IsMapped(childPtr)) {
- GenerateUpdates(updateRgn, updateBounds, childPtr);
+ GenerateUpdates(updateBounds, childPtr);
}
/*
@@ -552,29 +547,6 @@ GenerateActivateEvents(
/*
*----------------------------------------------------------------------
*
- * DoWindowActivate --
- *
- * Idle handler that calls GenerateActivateEvents().
- *
- * Results:
- * None.
- *
- * Side effects:
- * Additional events may be placed on the Tk event queue.
- *
- *----------------------------------------------------------------------
- */
-
-void
-DoWindowActivate(
- ClientData clientData)
-{
- GenerateActivateEvents(clientData, 1);
-}
-
-/*
- *----------------------------------------------------------------------
- *
* TkMacOSXGenerateFocusEvent --
*
* Given a Macintosh window activate event this function generates all
@@ -922,42 +894,31 @@ ConfigureRestrictProc(
return (eventPtr->type==ConfigureNotify ? TK_PROCESS_EVENT : TK_DEFER_EVENT);
}
-/*
- * If a window gets mapped inside the drawRect method, this will be run as an
- * idle task, after drawRect returns, to clean up the mess.
- */
+@implementation TKContentView(TKWindowEvent)
-static void
-RedisplayView(
- ClientData clientdata)
+- (void) addTkDirtyRect: (NSRect) rect
{
- NSView *view = (NSView *) clientdata;
-
- /*
- * Make sure that we are not trying to display a view that no longer
- * exists. Must call [NSApp windows] because [NSApp orderedWindows] excludes
- * floating/utility windows and other window panels.
- */
-
- for (NSWindow *w in [NSApp windows]) {
- if ([w contentView] == view) {
- [view setNeedsDisplay:YES];
- break;
- }
- }
+ _tkNeedsDisplay = YES;
+ _tkDirtyRect = NSUnionRect(_tkDirtyRect, rect);
+ [NSApp setNeedsToDraw:YES];
}
-@implementation TKContentView(TKWindowEvent)
+- (void) clearTkDirtyRect
+{
+ _tkNeedsDisplay = NO;
+ _tkDirtyRect = NSZeroRect;
+ [NSApp setNeedsToDraw:NO];
+}
- (void) drawRect: (NSRect) rect
{
- const NSRect *rectsBeingDrawn;
- NSInteger rectsBeingDrawnCount;
#ifdef TK_MAC_DEBUG_DRAWING
TkWindow *winPtr = TkMacOSXGetTkWindow([self window]);
- if (winPtr) fprintf(stderr, "drawRect: drawing %s\n",
- Tk_PathName(winPtr));
+ if (winPtr) {
+ fprintf(stderr, "drawRect: drawing %s in %s\n",
+ Tk_PathName(winPtr), NSStringFromRect(rect).UTF8String);
+ }
#endif
/*
@@ -973,30 +934,9 @@ RedisplayView(
}
[NSApp setIsDrawing: YES];
-
- [self getRectsBeingDrawn:&rectsBeingDrawn count:&rectsBeingDrawnCount];
- CGFloat height = [self bounds].size.height;
- HIMutableShapeRef drawShape = HIShapeCreateMutable();
-
- while (rectsBeingDrawnCount--) {
- CGRect r = NSRectToCGRect(*rectsBeingDrawn++);
-
-#ifdef TK_MAC_DEBUG_DRAWING
- fprintf(stderr, "drawRect: %dx%d@(%d,%d)\n", (int)r.size.width,
- (int)r.size.height, (int)r.origin.x, (int)r.origin.y);
-#endif
-
- r.origin.y = height - (r.origin.y + r.size.height);
- HIShapeUnionWithRect(drawShape, &r);
- }
- [self generateExposeEvents:(HIShapeRef)drawShape];
- CFRelease(drawShape);
- [NSApp setIsDrawing: NO];
-
- if ([self needsRedisplay]) {
- [self setNeedsRedisplay:NO];
- Tcl_DoWhenIdle(RedisplayView, self);
- }
+ [self clearTkDirtyRect];
+ [self generateExposeEvents:rect];
+ [NSApp setIsDrawing:NO];
#ifdef TK_MAC_DEBUG_DRAWING
fprintf(stderr, "drawRect: done.\n");
@@ -1056,9 +996,7 @@ RedisplayView(
* Generate and process expose events to redraw the window.
*/
- HIRect bounds = NSRectToCGRect([self bounds]);
- HIShapeRef shape = HIShapeCreateWithRect(&bounds);
- [self generateExposeEvents: shape];
+ [self generateExposeEvents: [self bounds]];
[w displayIfNeeded];
/*
@@ -1076,11 +1014,11 @@ RedisplayView(
* pending idle events are processed so the drawing will actually take place.
*/
-- (void) generateExposeEvents: (HIShapeRef) shape
+- (void) generateExposeEvents: (NSRect) rect
{
unsigned long serial;
- CGRect updateBounds;
int updatesNeeded;
+ CGRect updateBounds;
TkWindow *winPtr = TkMacOSXGetTkWindow([self window]);
ClientData oldArg;
Tk_RestrictProc *oldProc;
@@ -1089,26 +1027,25 @@ RedisplayView(
}
/*
- * Generate Tk Expose events.
- */
-
- HIShapeGetBounds(shape, &updateBounds);
-
- /*
- * All of these events will share the same serial number.
+ * Generate Tk Expose events. All of these events will share the same
+ * serial number.
*/
- serial = LastKnownRequestProcessed(Tk_Display(winPtr));
- updatesNeeded = GenerateUpdates(shape, &updateBounds, winPtr);
-
+ 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));
+
/*
- * First process all of the Expose events.
+ * Use the ExposeRestrictProc to process only the expose events. This
+ * will create idle drawing tasks, which we handle before we return.
*/
oldProc = Tk_RestrictEvents(ExposeRestrictProc, UINT2PTR(serial), &oldArg);
- while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {};
+ while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS|TCL_DONT_WAIT)) {};
Tk_RestrictEvents(oldProc, oldArg, &oldArg);
/*
@@ -1120,9 +1057,10 @@ RedisplayView(
* effect.)
*
* 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.
+ * So to run any display procs which were scheduled by the expose
+ * events we process all idle events before returning.
*/
+
while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {}
}
}
@@ -1202,10 +1140,15 @@ RedisplayView(
- (BOOL) isOpaque
{
NSWindow *w = [self window];
- return (w && (([w styleMask] & NSTexturedBackgroundWindowMask) ||
- ![w isOpaque]) ? NO : YES);
+ return (w && (([w styleMask] & NSTexturedBackgroundWindowMask) ||
+ ![w isOpaque]) ? NO : YES);
}
+/*
+ * On Catalina this is never called and drawRect clips to the rect that
+ * is passed to it by AppKit.
+ */
+
- (BOOL) wantsDefaultClipping
{
return NO;
diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c
index 8e52278..dca8686 100644
--- a/macosx/tkMacOSXWm.c
+++ b/macosx/tkMacOSXWm.c
@@ -3805,10 +3805,6 @@ WmWithdrawCmd(
TkpWmSetState(winPtr, WithdrawnState);
- NSWindow *win = TkMacOSXDrawableWindow(winPtr->window);
- [win orderOut:NSApp];
- [win setExcludedFromWindowsMenu:YES];
-
/*
* If this window has a transient, the transient must also be withdrawn.
*/
@@ -6201,13 +6197,14 @@ TkpRedrawWidget(Tk_Window tkwin) {
}
w = TkMacOSXDrawableWindow(winPtr->window);
if (w) {
- NSView *view = [w contentView];
+ TKContentView *view = [w contentView];
TkMacOSXWinBounds(winPtr, &tkBounds);
bounds = NSMakeRect(tkBounds.left,
[view bounds].size.height - tkBounds.bottom,
tkBounds.right - tkBounds.left,
tkBounds.bottom - tkBounds.top);
- [view setNeedsDisplayInRect:bounds];
+ [view setTkNeedsDisplay:YES];
+ [view setTkDirtyRect:bounds];
}
}
@@ -6412,6 +6409,7 @@ TkpWmSetState(
if (state == WithdrawnState) {
Tk_UnmapWindow((Tk_Window) winPtr);
} else if (state == IconicState) {
+
/*
* The window always gets unmapped. If we can show the icon version of
* the window we also collapse it.
@@ -6424,9 +6422,13 @@ TkpWmSetState(
Tk_UnmapWindow((Tk_Window) winPtr);
} else if (state == NormalState || state == ZoomState) {
Tk_MapWindow((Tk_Window) winPtr);
- if (macWin && ([macWin styleMask] & NSMiniaturizableWindowMask) &&
- [macWin isMiniaturized]) {
- [macWin deminiaturize:NSApp];
+ if (macWin && ([macWin styleMask] & NSMiniaturizableWindowMask)) {
+ if ([macWin isMiniaturized]) {
+ [macWin deminiaturize:NSApp];
+ }
+ else {
+ [macWin orderFront:nil];
+ }
}
TkMacOSXZoomToplevel(macWin, state == NormalState ? inZoomIn :
inZoomOut);
diff --git a/macosx/tkMacOSXXStubs.c b/macosx/tkMacOSXXStubs.c
index 6aeec36..107f712 100644
--- a/macosx/tkMacOSXXStubs.c
+++ b/macosx/tkMacOSXXStubs.c
@@ -752,7 +752,18 @@ XSync(
Display *display,
Bool discard)
{
- TkMacOSXFlushWindows();
+ /*
+ * The main use of XSync is by the update command, which alternates
+ * between running an event loop to process all events without waiting and
+ * calling XSync on all displays until no events are left. There is
+ * nothing for the mac to do with respect to syncing its one display but
+ * it can (and, during regression testing, frequently does) happen that
+ * timer events fire during the event loop. Processing those here seems
+ * to make the update command work in a way that is more consistent with
+ * its behavior on other platforms.
+ */
+
+ while (Tcl_DoOneEvent(TCL_TIMER_EVENTS|TCL_DONT_WAIT)){}
display->request++;
return 0;
}
@@ -945,7 +956,7 @@ XSynchronize(
Display *display,
Bool onoff)
{
- display->request++;
+ display->request++;
return NULL;
}