summaryrefslogtreecommitdiffstats
path: root/macosx/tkMacOSXWindowEvent.c
diff options
context:
space:
mode:
authorjan.nijtmans <nijtmans@users.sourceforge.net>2024-10-25 21:06:25 (GMT)
committerjan.nijtmans <nijtmans@users.sourceforge.net>2024-10-25 21:06:25 (GMT)
commit0d5336db012f45753abace489f18f0ca299c6961 (patch)
treeb1bf3280a9046df99226158978502eeb26f5b0a3 /macosx/tkMacOSXWindowEvent.c
parente97381a6d921de403516d5b761539a450f4af83c (diff)
parent1320b8a2a9c1269a345d44d673a7a35707fbbe9c (diff)
downloadtk-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.c339
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
/*