diff options
| author | jan.nijtmans <nijtmans@users.sourceforge.net> | 2024-10-25 21:06:25 (GMT) |
|---|---|---|
| committer | jan.nijtmans <nijtmans@users.sourceforge.net> | 2024-10-25 21:06:25 (GMT) |
| commit | 0d5336db012f45753abace489f18f0ca299c6961 (patch) | |
| tree | b1bf3280a9046df99226158978502eeb26f5b0a3 /macosx/tkMacOSXWindowEvent.c | |
| parent | e97381a6d921de403516d5b761539a450f4af83c (diff) | |
| parent | 1320b8a2a9c1269a345d44d673a7a35707fbbe9c (diff) | |
| download | tk-core-tip-626.zip tk-core-tip-626.tar.gz tk-core-tip-626.tar.bz2 | |
Merge 9.0core-tip-626
Diffstat (limited to 'macosx/tkMacOSXWindowEvent.c')
| -rw-r--r-- | macosx/tkMacOSXWindowEvent.c | 339 |
1 files changed, 151 insertions, 188 deletions
diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index e261bee..13dc916 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -134,13 +134,6 @@ extern NSString *NSWindowDidOrderOffScreenNotification; Tk_MapWindow((Tk_Window)winPtr); /* - * Process all Tk events generated by Tk_MapWindow(). - */ - - while (Tcl_ServiceEvent(0)) {} - while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} - - /* * NSWindowDidDeminiaturizeNotification is received after * NSWindowDidBecomeKeyNotification, so activate manually */ @@ -150,7 +143,7 @@ extern NSString *NSWindowDidOrderOffScreenNotification; } - (NSRect)windowWillUseStandardFrame:(NSWindow *)window - defaultFrame:(NSRect)newFrame + defaultFrame:(NSRect)newFrame { (void)window; @@ -238,15 +231,14 @@ extern NSString *NSWindowDidOrderOffScreenNotification; TkWindow *winPtr = TkMacOSXGetTkWindow(window); if (winPtr) { TKContentView *view = [window contentView]; + // fprintf(stderr, "Window %s became visible.\n", Tk_PathName(winPtr)); #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 if (@available(macOS 10.14, *)) { [view viewDidChangeEffectiveAppearance]; } #endif - [view addTkDirtyRect:[view bounds]]; - Tcl_CancelIdleCall(TkMacOSXDrawAllViews, NULL); - Tcl_DoWhenIdle(TkMacOSXDrawAllViews, NULL); + [view setNeedsDisplay:YES]; } } @@ -256,7 +248,7 @@ extern NSString *NSWindowDidOrderOffScreenNotification; TkWindow *winPtr = TkMacOSXGetTkWindow(w); if (winPtr) { - while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} + // fprintf(stderr, "Window %s was ordered on screen.\n", Tk_PathName(winPtr)); } } @@ -264,10 +256,10 @@ extern NSString *NSWindowDidOrderOffScreenNotification; { NSString *name = [notification name]; if ([name isEqualToString:NSWindowWillStartLiveResizeNotification]) { - // printf("Starting live resize.\n"); + // fprintf(stderr, "Starting live resize.\n"); } else if ([name isEqualToString:NSWindowDidEndLiveResizeNotification]) { [self setTkLiveResizeEnded:YES]; - // printf("Ending live resize\n"); + // fprintf(stderr, "Ending live resize\n"); } } @@ -284,9 +276,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 */ @@ -308,11 +302,8 @@ extern NSString *NSWindowDidOrderOffScreenNotification; observe(NSWindowDidOrderOnScreenNotification, windowBecameVisible:); observe(NSWindowWillStartLiveResizeNotification, windowLiveResize:); observe(NSWindowDidEndLiveResizeNotification, windowLiveResize:); - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 observe(NSWindowDidEnterFullScreenNotification, windowEnteredFullScreen:); observe(NSWindowDidExitFullScreenNotification, windowExitedFullScreen:); -#endif #ifdef TK_MAC_DEBUG_NOTIFICATIONS observe(NSWindowWillMoveNotification, windowDragStart:); @@ -396,7 +387,7 @@ static void RefocusGrabWindow(void *data) { } - (BOOL)applicationShouldHandleReopen:(NSApplication *)sender - hasVisibleWindows:(BOOL)flag + hasVisibleWindows:(BOOL)flag { (void)sender; (void)flag; @@ -447,7 +438,7 @@ static void RefocusGrabWindow(void *data) { @end #pragma mark - - + /* *---------------------------------------------------------------------- * @@ -473,33 +464,13 @@ static void RefocusGrabWindow(void *data) { * *---------------------------------------------------------------------- */ - +// This stub is no longer used, but is expected by the stub mechanism. 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 +498,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 +522,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, @@ -871,7 +849,7 @@ TkWmProtocolEventProc( Tcl_Preserve(protPtr); interp = protPtr->interp; Tcl_Preserve(interp); - result = Tcl_EvalEx(interp, protPtr->command, TCL_INDEX_NONE, TCL_EVAL_GLOBAL); + result = Tcl_EvalEx(interp, Tcl_GetString(protPtr->commandObj), TCL_INDEX_NONE, TCL_EVAL_GLOBAL); if (result != TCL_OK) { Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( "\n (command for \"%s\" window manager protocol)", @@ -952,40 +930,16 @@ ExposeRestrictProc( ? TK_PROCESS_EVENT : TK_DEFER_EVENT); } -/* - * Restrict event processing to ConfigureNotify events. - */ - -static Tk_RestrictAction -ConfigureRestrictProc( - TCL_UNUSED(void *), - XEvent *eventPtr) -{ - return (eventPtr->type==ConfigureNotify ? TK_PROCESS_EVENT : TK_DEFER_EVENT); -} - @implementation TKContentView(TKWindowEvent) - (id)initWithFrame:(NSRect)frame { 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,21 +949,40 @@ 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 && ![NSApp tkWillExit]) { + /* + * 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. + * Without this there are black flashes when a window opens. + */ + + while(Tcl_DoOneEvent(TCL_IDLE_EVENTS)){} + } } -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 - (void) viewDidChangeBackingProperties { @@ -1021,116 +994,62 @@ ConfigureRestrictProc( */ self.layer.contentsScale = self.window.screen.backingScaleFactor; -} -#endif - -- (void) addTkDirtyRect: (NSRect) rect -{ - _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 + [self resetTkLayerBitmapContext]; + // need to redraw + [self generateExposeEvents: self.bounds]; } -(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; - void *oldArg; - Tk_RestrictProc *oldProc; + unsigned int width = (unsigned int) newsize.width; + unsigned int height= (unsigned int) newsize.height; /* - * 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. */ TkGenWMConfigureEvent(tkwin, Tk_X(tkwin), Tk_Y(tkwin), width, height, TK_SIZE_CHANGED | TK_MACOSX_HANDLE_EVENT_IMMEDIATELY); - oldProc = Tk_RestrictEvents(ConfigureRestrictProc, NULL, &oldArg); - 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 +1057,14 @@ ConfigureRestrictProc( */ [NSApp _unlockAutoreleasePool]; + } + + /* + * Request a call to updateLayer. + */ + + [self setNeedsDisplay:YES]; } /* @@ -1150,53 +1076,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 +1177,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 +1186,8 @@ static const char *const accentNames[] = { effectiveAppearanceName.UTF8String, accentName, highlightName); Tk_SendVirtualEvent(tkwin, "AppearanceChanged", Tcl_NewStringObj(data, TCL_INDEX_NONE)); + // Force a redraw of the view. + [self setFrameSize:self.frame.size]; } - (void)observeValueForKeyPath:(NSString *)keyPath @@ -1352,6 +1289,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 /* |
