diff options
Diffstat (limited to 'macosx/tkMacOSXWindowEvent.c')
-rw-r--r-- | macosx/tkMacOSXWindowEvent.c | 506 |
1 files changed, 260 insertions, 246 deletions
diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 71e687b..30a2d57 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,15 +90,12 @@ 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. + */ - TkGenWMConfigureEvent((Tk_Window) winPtr, x, y, width, height, flags); + flags |= TK_MACOSX_HANDLE_EVENT_IMMEDIATELY; + TkGenWMConfigureEvent((Tk_Window)winPtr, x, y, width, height, flags); } } @@ -114,25 +111,21 @@ extern NSString *NSWindowDidOrderOffScreenNotification; if (winPtr) { winPtr->wmInfoPtr->hints.initial_state = TkMacOSXIsWindowZoomed(winPtr) ? ZoomState : NormalState; - Tk_MapWindow((Tk_Window) winPtr); - if (Tcl_GetServiceMode() != TCL_SERVICE_NONE) { + Tk_MapWindow((Tk_Window)winPtr); - /* - * 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); } } @@ -154,6 +147,7 @@ extern NSString *NSWindowDidOrderOffScreenNotification; willUseFullScreenContentSize:(NSSize)proposedSize { (void)window; + /* * We don't need to change the proposed size, but we do need to implement * this method. Otherwise the full screen window will be sized to the @@ -188,7 +182,7 @@ extern NSString *NSWindowDidOrderOffScreenNotification; TkWindow *winPtr = TkMacOSXGetTkWindow(w); if (winPtr) { - Tk_UnmapWindow((Tk_Window) winPtr); + Tk_UnmapWindow((Tk_Window)winPtr); } } @@ -200,7 +194,7 @@ extern NSString *NSWindowDidOrderOffScreenNotification; TkWindow *winPtr = TkMacOSXGetTkWindow(w); if (winPtr) { - TkGenWMDestroyEvent((Tk_Window) winPtr); + TkGenWMDestroyEvent((Tk_Window)winPtr); } /* @@ -211,33 +205,45 @@ extern NSString *NSWindowDidOrderOffScreenNotification; return (winPtr ? NO : YES); } -#ifdef TK_MAC_DEBUG_NOTIFICATIONS - -- (void) windowDragStart: (NSNotification *) notification +- (void) windowBecameVisible: (NSNotification *) notification { - TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); -} + NSWindow *window = [notification object]; + TkWindow *winPtr = TkMacOSXGetTkWindow(window); + if (winPtr) { + TKContentView *view = [window contentView]; -- (void) windowLiveResize: (NSNotification *) notification -{ - TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); - //BOOL start = [[notification name] isEqualToString:NSWindowWillStartLiveResizeNotification]; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 + if (@available(macOS 10.15, *)) { + [view viewDidChangeEffectiveAppearance]; + } +#endif + [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 @@ -247,7 +253,7 @@ extern NSString *NSWindowDidOrderOffScreenNotification; TkWindow *winPtr = TkMacOSXGetTkWindow(w); if (winPtr) { - //Tk_UnmapWindow((Tk_Window) winPtr); + //Tk_UnmapWindow((Tk_Window)winPtr); } } @@ -266,6 +272,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:); @@ -276,8 +284,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 @@ -394,26 +400,53 @@ extern NSString *NSWindowDidOrderOffScreenNotification; /* *---------------------------------------------------------------------- * - * TkpAppIsDrawing -- + * TkpWillDrawWidget -- * * 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. + * being run inside of the drawRect method. If not, it may be desirable + * for the display procedure to simply clear the REDRAW_PENDING flag + * and return. The widget can be recorded in order to schedule a + * redraw, via and Expose event, from within drawRect. + * + * This is also 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; + * True if called from the drawRect method of a TKContentView with + * tkwin NULL or pointing to a widget in the current focusView. * * Side effects: - * None + * Currently none. One day the tkwin parameter may be recorded to + * handle redrawing the widget later. * *---------------------------------------------------------------------- */ -MODULE_SCOPE Bool -TkpAppIsDrawing(void) { - return [NSApp isDrawing]; +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; } /* @@ -421,55 +454,39 @@ TkpAppIsDrawing(void) { * * GenerateUpdates -- * - * Given a Macintosh update region and a Tk window this function geneates + * 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 recursivly have each damaged window generate Expose + * function will then recursively have each damaged window generate Expose * events for its child windows. * * Results: * True if event(s) are generated - false otherwise. * * Side effects: - * Additional events may be place on the Tk event queue. + * Additional events may be placed on the Tk event queue. * *---------------------------------------------------------------------- */ 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 occured in. + * 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); @@ -483,7 +500,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 @@ -496,7 +513,7 @@ GenerateUpdates( if (!Tk_IsMapped(childPtr) || Tk_IsTopLevel(childPtr)) { continue; } - GenerateUpdates(updateRgn, updateBounds, childPtr); + GenerateUpdates(updateBounds, childPtr); } /* @@ -506,7 +523,7 @@ GenerateUpdates( if (Tk_IsContainer(winPtr)) { childPtr = TkpGetOtherWindow(winPtr); if (childPtr != NULL && Tk_IsMapped(childPtr)) { - GenerateUpdates(updateRgn, updateBounds, childPtr); + GenerateUpdates(updateBounds, childPtr); } /* @@ -520,58 +537,6 @@ GenerateUpdates( /* *---------------------------------------------------------------------- * - * GenerateActivateEvents -- - * - * Given a Macintosh window activate event this function generates all the - * X Activate events needed by Tk. - * - * Results: - * True if event(s) are generated - false otherwise. - * - * Side effects: - * Additional events may be place on the Tk event queue. - * - *---------------------------------------------------------------------- - */ - -int -GenerateActivateEvents( - TkWindow *winPtr, - int activeFlag) -{ - TkGenerateActivateEvents(winPtr, activeFlag); - if (activeFlag || ![NSApp isActive]) { - TkMacOSXGenerateFocusEvent(winPtr, activeFlag); - } - return true; -} - -/* - *---------------------------------------------------------------------- - * - * DoWindowActivate -- - * - * Idle handler that calls GenerateActivateEvents(). - * - * Results: - * None. - * - * Side effects: - * Additional events may be place on the Tk event queue. - * - *---------------------------------------------------------------------- - */ - -void -DoWindowActivate( - ClientData clientData) -{ - GenerateActivateEvents(clientData, 1); -} - -/* - *---------------------------------------------------------------------- - * * TkMacOSXGenerateFocusEvent -- * * Given a Macintosh window activate event this function generates all @@ -581,12 +546,12 @@ DoWindowActivate( * True if event(s) are generated - false otherwise. * * Side effects: - * Additional events may be place on the Tk event queue. + * Additional events may be placed on the Tk event queue. * *---------------------------------------------------------------------- */ -MODULE_SCOPE int +static int TkMacOSXGenerateFocusEvent( TkWindow *winPtr, /* Root X window for event. */ int activeFlag) @@ -628,6 +593,35 @@ TkMacOSXGenerateFocusEvent( /* *---------------------------------------------------------------------- * + * GenerateActivateEvents -- + * + * Given a Macintosh window activate event this function generates all the + * X Activate events needed by Tk. + * + * Results: + * True if event(s) are generated - false otherwise. + * + * Side effects: + * Additional events may be placed on the Tk event queue. + * + *---------------------------------------------------------------------- + */ + +int +GenerateActivateEvents( + TkWindow *winPtr, + int activeFlag) +{ + TkGenerateActivateEvents(winPtr, activeFlag); + if (activeFlag || ![NSApp isActive]) { + TkMacOSXGenerateFocusEvent(winPtr, activeFlag); + } + return true; +} + +/* + *---------------------------------------------------------------------- + * * TkGenWMConfigureEvent -- * * Generate a ConfigureNotify event for Tk. Depending on the value of flag @@ -739,7 +733,7 @@ TkGenWMConfigureEvent( /* * Now set up the changes structure. Under X we wait for the - * ConfigureNotify to set these values. On the Mac we know imediatly that + * ConfigureNotify to set these values. On the Mac we know immediately that * this is what we want - so we just set them. However, we need to make * sure the windows clipping region is marked invalid so the change is * visible to the subwindow. @@ -830,7 +824,7 @@ TkWmProtocolEventProc( if (result != TCL_OK) { Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( "\n (command for \"%s\" window manager protocol)", - Tk_GetAtomName((Tk_Window) winPtr, protocol))); + Tk_GetAtomName((Tk_Window)winPtr, protocol))); Tcl_BackgroundException(interp, result); } Tcl_Release(interp); @@ -844,8 +838,8 @@ TkWmProtocolEventProc( * message then just destroy the window. */ - if (protocol == Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW")) { - Tk_DestroyWindow((Tk_Window) winPtr); + if (protocol == Tk_InternAtom((Tk_Window)winPtr, "WM_DELETE_WINDOW")) { + Tk_DestroyWindow((Tk_Window)winPtr); } } @@ -919,43 +913,32 @@ 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 displaying 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; (void)rect; #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 /* @@ -964,37 +947,16 @@ RedisplayView( */ if ([NSApp isDrawing]) { - if ([NSApp macMinorVersion] > 13) { + if ([NSApp macOSVersion] > 101300) { TKLog(@"WARNING: a recursive call to drawRect was aborted."); } return; } [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"); @@ -1006,7 +968,7 @@ RedisplayView( [super setFrameSize: newsize]; NSWindow *w = [self window]; TkWindow *winPtr = TkMacOSXGetTkWindow(w); - Tk_Window tkwin = (Tk_Window) winPtr; + Tk_Window tkwin = (Tk_Window)winPtr; if (![self inLiveResize] && [w respondsToSelector: @selector (tkLayoutChanged)]) { @@ -1051,13 +1013,14 @@ RedisplayView( TkMacOSXUpdateClipRgn(winPtr); /* - * Generate and process expose events to redraw the window. + * 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]. */ - HIRect bounds = NSRectToCGRect([self bounds]); - HIShapeRef shape = HIShapeCreateWithRect(&bounds); - [self generateExposeEvents: shape]; - [w displayIfNeeded]; + if([NSApp isDrawing] || [self inLiveResize]) { + [self generateExposeEvents: [self bounds]]; + } /* * Finally, unlock the main autoreleasePool. @@ -1074,11 +1037,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; @@ -1087,26 +1050,25 @@ RedisplayView( } /* - * Generate Tk Expose events. + * Generate Tk Expose events. All of these events will share the same + * serial number. */ - HIShapeGetBounds(shape, &updateBounds); - - /* - * 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); /* @@ -1118,51 +1080,104 @@ 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)) {} } } /* - * This method is called when a user changes between light and dark mode. The - * implementation here generates a Tk virtual event which can be bound to a - * function that redraws the window in an appropriate style. + * In macOS 10.14 and later this method is called when a user changes between + * light and dark mode or changes the accent color. The implementation + * generates two virtual events. The first is either <<LightAqua>> or + * <<DarkAqua>>, depending on the view's current effective appearance. The + * second is <<AppearnceChanged>> and has a data string describing the + * effective appearance of the view and the current accent and highlight + * colors. */ +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 + +static const char *const accentNames[] = { + "Graphite", + "Red", + "Orange", + "Yellow", + "Green", + "Blue", + "Purple", + "Pink" +}; + - (void) viewDidChangeEffectiveAppearance { - XVirtualEvent event; - int x, y; - NSWindow *w = [self window]; - TkWindow *winPtr = TkMacOSXGetTkWindow(w); - Tk_Window tkwin = (Tk_Window) winPtr; + Tk_Window tkwin = (Tk_Window)TkMacOSXGetTkWindow([self window]); + if (!tkwin) { + return; + } + NSAppearanceName effectiveAppearanceName = [[self effectiveAppearance] name]; + NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults]; + static const char *defaultColor = NULL; + + if (effectiveAppearanceName == NSAppearanceNameAqua) { + Tk_SendVirtualEvent(tkwin, "LightAqua", NULL); + } else if (effectiveAppearanceName == NSAppearanceNameDarkAqua) { + Tk_SendVirtualEvent(tkwin, "DarkAqua", NULL); + } + if ([NSApp macOSVersion] < 101500) { + + /* + * Mojave cannot handle the KVO shenanigans that we need for the + * highlight and accent color notifications. + */ - if (!winPtr) { return; } - bzero(&event, sizeof(XVirtualEvent)); - event.type = VirtualEvent; - event.serial = LastKnownRequestProcessed(Tk_Display(tkwin)); - event.send_event = false; - event.display = Tk_Display(tkwin); - event.event = Tk_WindowId(tkwin); - event.root = XRootWindow(Tk_Display(tkwin), 0); - event.subwindow = None; - event.time = TkpGetMS(); - XQueryPointer(NULL, winPtr->window, NULL, NULL, - &event.x_root, &event.y_root, &x, &y, &event.state); - Tk_TopCoordsToWindow(tkwin, x, y, &event.x, &event.y); - event.same_screen = true; - if (TkMacOSXInDarkMode(tkwin)) { - event.name = Tk_GetUid("DarkAqua"); - } else { - event.name = Tk_GetUid("LightAqua"); + if (!defaultColor) { + defaultColor = [NSApp macOSVersion] < 110000 ? "Blue" : "Multicolor"; + preferences = [[NSUserDefaults standardUserDefaults] retain]; + + /* + * AppKit calls this method when the user changes the Accent Color + * but not when the user changes the Highlight Color. So we register + * to receive KVO notifications for Highlight Color as well. + */ + + [preferences addObserver:self + forKeyPath:@"AppleHighlightColor" + options:NSKeyValueObservingOptionNew + context:NULL]; } - Tk_QueueWindowEvent((XEvent *) &event, TCL_QUEUE_TAIL); + NSString *accent = [preferences stringForKey:@"AppleAccentColor"]; + NSArray *words = [[preferences stringForKey:@"AppleHighlightColor"] + 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; + char data[256]; + snprintf(data, 256, "Appearance %s Accent %s Highlight %s", + effectiveAppearanceName.UTF8String, accentName, + highlightName); + Tk_SendVirtualEvent(tkwin, "AppearanceChanged", Tcl_NewStringObj(data, -1)); } +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context +{ + NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults]; + if (object == preferences && [keyPath isEqualToString:@"AppleHighlightColor"]) { + if (@available(macOS 10.14, *)) { + [self viewDidChangeEffectiveAppearance]; + } + } +} + +#endif + /* * This is no-op on 10.7 and up because Apple has removed this widget, but we * are leaving it here for backwards compatibility. @@ -1176,7 +1191,7 @@ RedisplayView( XVirtualEvent event; int x, y; TkWindow *winPtr = TkMacOSXGetTkWindow([self window]); - Tk_Window tkwin = (Tk_Window) winPtr; + Tk_Window tkwin = (Tk_Window)winPtr; (void)sender; if (!winPtr){ @@ -1199,12 +1214,10 @@ RedisplayView( Tk_QueueWindowEvent((XEvent *) &event, TCL_QUEUE_TAIL); } -- (BOOL) isOpaque -{ - NSWindow *w = [self window]; - 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 { @@ -1223,7 +1236,8 @@ RedisplayView( - (void) keyDown: (NSEvent *) theEvent { - (void)theEvent; + (void)theEvent; + #ifdef TK_MAC_DEBUG_EVENTS TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, theEvent); #endif @@ -1231,7 +1245,7 @@ RedisplayView( /* * When the services menu is opened this is called for each Responder in - * the Responder chain until a service provider is found. The TkContentView + * the Responder chain until a service provider is found. The TKContentView * should be the first (and generally only) Responder in the chain. We * return the TkServices object that was created in TkpInit. */ |