summaryrefslogtreecommitdiffstats
path: root/macosx/tkMacOSXWindowEvent.c
diff options
context:
space:
mode:
Diffstat (limited to 'macosx/tkMacOSXWindowEvent.c')
-rw-r--r--macosx/tkMacOSXWindowEvent.c506
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.
*/