summaryrefslogtreecommitdiffstats
path: root/macosx
diff options
context:
space:
mode:
authorfvogel <fvogelnew1@free.fr>2021-11-16 20:39:47 (GMT)
committerfvogel <fvogelnew1@free.fr>2021-11-16 20:39:47 (GMT)
commitd8f50ac067d2f0df387ca0b0c46278c2464efc8b (patch)
tree4c40167ca8b673d38459b4478944b8bc60cc4a7f /macosx
parentc56ea97892bf4b5716fd0c88709b0ef0cb130995 (diff)
parenta9827f93eadc2978316098553e09406dae1f5520 (diff)
downloadtk-bug-b1d115fa60.zip
tk-bug-b1d115fa60.tar.gz
tk-bug-b1d115fa60.tar.bz2
Diffstat (limited to 'macosx')
-rw-r--r--macosx/Credits.html.in4
-rw-r--r--macosx/GNUmakefile10
-rw-r--r--macosx/README2
-rw-r--r--macosx/tkMacOSXButton.c10
-rw-r--r--macosx/tkMacOSXColor.c15
-rw-r--r--macosx/tkMacOSXColor.h4
-rw-r--r--macosx/tkMacOSXConstants.h1
-rw-r--r--macosx/tkMacOSXDefault.h27
-rw-r--r--macosx/tkMacOSXDialog.c61
-rw-r--r--macosx/tkMacOSXDraw.c120
-rw-r--r--macosx/tkMacOSXEmbed.c3
-rw-r--r--macosx/tkMacOSXFont.c5
-rw-r--r--macosx/tkMacOSXImage.c398
-rw-r--r--macosx/tkMacOSXInit.c44
-rw-r--r--macosx/tkMacOSXKeyEvent.c45
-rw-r--r--macosx/tkMacOSXMenu.c28
-rw-r--r--macosx/tkMacOSXMouseEvent.c27
-rw-r--r--macosx/tkMacOSXNotify.c8
-rw-r--r--macosx/tkMacOSXPort.h27
-rw-r--r--macosx/tkMacOSXPrivate.h5
-rw-r--r--macosx/tkMacOSXRegion.c49
-rw-r--r--macosx/tkMacOSXSubwindows.c33
-rw-r--r--macosx/tkMacOSXWindowEvent.c76
-rw-r--r--macosx/tkMacOSXWm.c34
-rw-r--r--macosx/ttkMacOSXTheme.c100
25 files changed, 816 insertions, 320 deletions
diff --git a/macosx/Credits.html.in b/macosx/Credits.html.in
index cc409b1..1c14367 100644
--- a/macosx/Credits.html.in
+++ b/macosx/Credits.html.in
@@ -5,8 +5,8 @@
<body style="font-size:120%;font-family:Arial,sans-serif;">
<p>
Tcl and Tk are distributed under a modified BSD license:<br>
-<a href="https:www.tcl.tk/software/tcltk/license.html">
- https:www.tcl.tk/software/tcltk/license.html
+<a href="https://www.tcl-lang.org/software/tcltk/license.html">
+ https://www.tcl-lang.org/software/tcltk/license.html
</a>
</p>
<ul style="list-style-type:none;">
diff --git a/macosx/GNUmakefile b/macosx/GNUmakefile
index ff028b5..dd4c025 100644
--- a/macosx/GNUmakefile
+++ b/macosx/GNUmakefile
@@ -46,12 +46,6 @@ TK_X11 ?=
# Checks and overrides for subframework builds
ifeq (${SUBFRAMEWORK}_${TK_X11},1_)
-ifeq (${DYLIB_INSTALL_DIR},)
- @echo "Cannot install subframework with empty DYLIB_INSTALL_DIR !" && false
-endif
-ifeq (${DESTDIR},)
- @echo "Cannot install subframework with empty DESTDIR !" && false
-endif
override BUILD_DIR = ${DESTDIR}/build
override INSTALL_PATH = /Frameworks
endif
@@ -145,9 +139,9 @@ wish := ${wish}-X11
override EMBEDDED_BUILD :=
endif
-INSTALL_TARGETS = install-binaries install-libraries
+INSTALL_TARGETS = install-binaries install-libraries install-headers
ifeq (${EMBEDDED_BUILD},)
-INSTALL_TARGETS += install-private-headers install-headers install-demos
+INSTALL_TARGETS += install-private-headers install-demos
endif
ifeq (${INSTALL_BUILD}_${EMBEDDED_BUILD}_${BUILD_STYLE},1__Deployment)
INSTALL_TARGETS += html-tk
diff --git a/macosx/README b/macosx/README
index 834dea4..4ed334f 100644
--- a/macosx/README
+++ b/macosx/README
@@ -681,7 +681,7 @@ conditional code which is only used for macOS.
The 10.14 release added support for system appearance changes,
including a "Dark Mode" that renders all window frames and menus in
-dark colors. Tk 8.6.11 provides three virtual events <<LightAqua>>,
+dark colors. Tk 8.6 provides three virtual events <<LightAqua>>,
<<DarkAqua>> and <<AppearanceChanged>>, to allow you to update your Tk
app's appearance when the system appearance changes. These events are
generated in [NSView effectiveAppearanceChanged], which is called by
diff --git a/macosx/tkMacOSXButton.c b/macosx/tkMacOSXButton.c
index b083531..f8f60f8 100644
--- a/macosx/tkMacOSXButton.c
+++ b/macosx/tkMacOSXButton.c
@@ -765,10 +765,12 @@ TkMacOSXDrawButton(
* Using a ttk::button would be a much better choice, however.
*/
- if (TkMacOSXInDarkMode(butPtr->tkwin) &&
- mbPtr->drawinfo.state != kThemeStatePressed &&
- !(mbPtr->drawinfo.adornment & kThemeAdornmentDefault)) {
- hiinfo.state = kThemeStateInactive;
+ if ([NSApp macOSVersion] < 101500) {
+ if (TkMacOSXInDarkMode(butPtr->tkwin) &&
+ mbPtr->drawinfo.state != kThemeStatePressed &&
+ !(mbPtr->drawinfo.adornment & kThemeAdornmentDefault)) {
+ hiinfo.state = kThemeStateInactive;
+ }
}
HIThemeDrawButton(&cntrRect, &hiinfo, dc.context,
kHIThemeOrientationNormal, &contHIRec);
diff --git a/macosx/tkMacOSXColor.c b/macosx/tkMacOSXColor.c
index edcd5d3..3951683 100644
--- a/macosx/tkMacOSXColor.c
+++ b/macosx/tkMacOSXColor.c
@@ -24,6 +24,7 @@ static int numSystemColors;
static int rgbColorIndex;
static int controlAccentIndex;
static int selectedTabTextIndex;
+static int pressedButtonTextIndex;
static Bool useFakeAccentColor = NO;
static SystemColorDatum **systemColorIndex;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400
@@ -67,7 +68,8 @@ void initColorTable()
if (![NSColor respondsToSelector:colorSelector]) {
if ([colorName isEqualToString:@"controlAccentColor"]) {
useFakeAccentColor = YES;
- } else if (![colorName isEqualToString:@"selectedTabTextColor"]) {
+ } else if ( ![colorName isEqualToString:@"selectedTabTextColor"]
+ && ![colorName isEqualToString:@"pressedButtonTextColor"]) {
/* Uncomment to print all unsupported colors: */
/* printf("Unsupported color %s\n", colorName.UTF8String); */
continue;
@@ -147,6 +149,9 @@ void initColorTable()
hPtr = Tcl_FindHashEntry(&systemColors, "SelectedTabTextColor");
entry = (SystemColorDatum *) Tcl_GetHashValue(hPtr);
selectedTabTextIndex = entry->index;
+ hPtr = Tcl_FindHashEntry(&systemColors, "PressedButtonTextColor");
+ entry = (SystemColorDatum *) Tcl_GetHashValue(hPtr);
+ pressedButtonTextIndex = entry->index;
[pool drain];
}
@@ -278,6 +283,7 @@ GetRGBA(
CGFloat *rgba)
{
NSColor *bgColor, *color = nil;
+ int OSVersion = [NSApp macOSVersion];
if (!sRGB) {
sRGB = [NSColorSpace sRGBColorSpace];
@@ -325,12 +331,17 @@ GetRGBA(
colorUsingColorSpace:sRGB];
#endif
} else if (entry->index == selectedTabTextIndex) {
- int OSVersion = [NSApp macOSVersion];
if (OSVersion > 100600 && OSVersion < 110000) {
color = [[NSColor whiteColor] colorUsingColorSpace:sRGB];
} else {
color = [[NSColor textColor] colorUsingColorSpace:sRGB];
}
+ } else if (entry->index == pressedButtonTextIndex) {
+ if (OSVersion < 120000) {
+ color = [[NSColor whiteColor] colorUsingColorSpace:sRGB];
+ } else {
+ color = [[NSColor blackColor] colorUsingColorSpace:sRGB];
+ }
} else {
color = [[NSColor valueForKey:entry->selector] colorUsingColorSpace:sRGB];
}
diff --git a/macosx/tkMacOSXColor.h b/macosx/tkMacOSXColor.h
index deffbbc..bc9d307 100644
--- a/macosx/tkMacOSXColor.h
+++ b/macosx/tkMacOSXColor.h
@@ -241,8 +241,10 @@ static SystemColorDatum systemColorData[] = {
{"WindowBackgroundColor7", ttkBackground, 7, NULL, 0, NULL },
/* Apple's SecondaryLabelColor is the same as their LabelColor so we roll our own. */
{"SecondaryLabelColor", ttkBackground, 14, NULL, 0, NULL },
-/* Color to use for notebook tab labels -- depends on OS version. */
+/* Color to use for notebook tab label text -- depends on OS version. */
{"SelectedTabTextColor", semantic, 0, "textColor", 0, NULL },
+/* Color to use for selected button labels -- depends on OS version. */
+{"PressedButtonTextColor", semantic, 0, "textColor", 0, NULL },
/* Semantic colors that we simulate on older systems which don't supoort them. */
{"ControlAccentColor", semantic, 0, "controlAccentColor", 0, NULL },
{"LabelColor", semantic, 0, "blackColor", 0, NULL },
diff --git a/macosx/tkMacOSXConstants.h b/macosx/tkMacOSXConstants.h
index 0b6ae2b..9d4079c 100644
--- a/macosx/tkMacOSXConstants.h
+++ b/macosx/tkMacOSXConstants.h
@@ -96,6 +96,7 @@ typedef NSInteger NSModalResponse;
#define NSMiniaturizableWindowMask NSWindowStyleMaskMiniaturizable
#define NSBorderlessWindowMask NSWindowStyleMaskBorderless
#define NSFullScreenWindowMask NSWindowStyleMaskFullScreen
+#define NSAlphaFirstBitmapFormat NSBitmapFormatAlphaFirst
#endif
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101400
diff --git a/macosx/tkMacOSXDefault.h b/macosx/tkMacOSXDefault.h
index af0a06b..5a5ccca 100644
--- a/macosx/tkMacOSXDefault.h
+++ b/macosx/tkMacOSXDefault.h
@@ -55,7 +55,7 @@
#define DEF_BUTTON_ANCHOR "center"
#define DEF_BUTTON_ACTIVE_BG_COLOR ACTIVE_BG
#define DEF_BUTTON_ACTIVE_BG_MONO BLACK
-#define DEF_BUTTON_ACTIVE_FG_COLOR WHITE
+#define DEF_BUTTON_ACTIVE_FG_COLOR "systemPressedButtonTextColor"
#define DEF_CHKRAD_ACTIVE_FG_COLOR ACTIVE_FG
#define DEF_BUTTON_ACTIVE_FG_MONO WHITE
#define DEF_BUTTON_BG_COLOR NORMAL_BG
@@ -68,13 +68,14 @@
#define DEF_BUTTON_DEFAULT "disabled"
#define DEF_BUTTON_DISABLED_FG_COLOR DISABLED
#define DEF_BUTTON_DISABLED_FG_MONO ""
-#define DEF_BUTTON_FG NORMAL_FG
-#define DEF_CHKRAD_FG DEF_BUTTON_FG
+#define DEF_BUTTON_FG BLACK
+#define DEF_LABEL_FG NORMAL_FG
+#define DEF_CHKRAD_FG DEF_LABEL_FG
#define DEF_BUTTON_FONT "TkDefaultFont"
#define DEF_BUTTON_HEIGHT "0"
#define DEF_BUTTON_HIGHLIGHT_BG_COLOR DEF_BUTTON_BG_COLOR
#define DEF_BUTTON_HIGHLIGHT_BG_MONO DEF_BUTTON_BG_MONO
-#define DEF_BUTTON_HIGHLIGHT "systemButtonFrame"
+#define DEF_BUTTON_HIGHLIGHT NORMAL_FG
#define DEF_LABEL_HIGHLIGHT_WIDTH "0"
//#if TK_MAC_BUTTON_USE_COMPATIBILITY_METRICS
//#define DEF_BUTTON_HIGHLIGHT_WIDTH "4"
@@ -124,7 +125,7 @@
#define DEF_CANVAS_CURSOR ""
#define DEF_CANVAS_HEIGHT "7c"
#define DEF_CANVAS_HIGHLIGHT_BG NORMAL_BG
-#define DEF_CANVAS_HIGHLIGHT BLACK
+#define DEF_CANVAS_HIGHLIGHT NORMAL_FG
#define DEF_CANVAS_HIGHLIGHT_WIDTH "3"
#define DEF_CANVAS_INSERT_BG BLACK
#define DEF_CANVAS_INSERT_BD_COLOR "0"
@@ -174,7 +175,7 @@
#define DEF_ENTRY_FONT "TkTextFont"
#define DEF_ENTRY_FG NORMAL_FG
#define DEF_ENTRY_HIGHLIGHT_BG NORMAL_BG
-#define DEF_ENTRY_HIGHLIGHT BLACK
+#define DEF_ENTRY_HIGHLIGHT NORMAL_FG
#define DEF_ENTRY_HIGHLIGHT_WIDTH "3"
#define DEF_ENTRY_INSERT_BG NORMAL_FG
#define DEF_ENTRY_INSERT_BD_COLOR "0"
@@ -212,7 +213,7 @@
#define DEF_FRAME_CURSOR ""
#define DEF_FRAME_HEIGHT "0"
#define DEF_FRAME_HIGHLIGHT_BG NORMAL_BG
-#define DEF_FRAME_HIGHLIGHT BLACK
+#define DEF_FRAME_HIGHLIGHT NORMAL_FG
#define DEF_FRAME_HIGHLIGHT_WIDTH "0"
#define DEF_FRAME_PADX "0"
#define DEF_FRAME_PADY "0"
@@ -248,7 +249,7 @@
#define DEF_LISTBOX_FG NORMAL_FG
#define DEF_LISTBOX_HEIGHT "10"
#define DEF_LISTBOX_HIGHLIGHT_BG NORMAL_BG
-#define DEF_LISTBOX_HIGHLIGHT BLACK
+#define DEF_LISTBOX_HIGHLIGHT NORMAL_FG
#define DEF_LISTBOX_HIGHLIGHT_WIDTH "0"
#define DEF_LISTBOX_JUSTIFY "left"
#define DEF_LISTBOX_RELIEF "solid"
@@ -343,7 +344,7 @@
#define DEF_MENUBUTTON_HEIGHT "0"
#define DEF_MENUBUTTON_HIGHLIGHT_BG_COLOR DEF_MENUBUTTON_BG_COLOR
#define DEF_MENUBUTTON_HIGHLIGHT_BG_MONO DEF_MENUBUTTON_BG_MONO
-#define DEF_MENUBUTTON_HIGHLIGHT BLACK
+#define DEF_MENUBUTTON_HIGHLIGHT NORMAL_FG
#define DEF_MENUBUTTON_HIGHLIGHT_WIDTH "0"
#define DEF_MENUBUTTON_IMAGE NULL
#define DEF_MENUBUTTON_INDICATOR "1"
@@ -373,7 +374,7 @@
#define DEF_MESSAGE_FG NORMAL_FG
#define DEF_MESSAGE_FONT "TkDefaultFont"
#define DEF_MESSAGE_HIGHLIGHT_BG NORMAL_BG
-#define DEF_MESSAGE_HIGHLIGHT BLACK
+#define DEF_MESSAGE_HIGHLIGHT NORMAL_FG
#define DEF_MESSAGE_HIGHLIGHT_WIDTH "0"
#define DEF_MESSAGE_JUSTIFY "left"
#define DEF_MESSAGE_PADX "-1"
@@ -439,7 +440,7 @@
#define DEF_SCALE_FROM "0"
#define DEF_SCALE_HIGHLIGHT_BG_COLOR DEF_SCALE_BG_COLOR
#define DEF_SCALE_HIGHLIGHT_BG_MONO DEF_SCALE_BG_MONO
-#define DEF_SCALE_HIGHLIGHT BLACK
+#define DEF_SCALE_HIGHLIGHT NORMAL_FG
#define DEF_SCALE_HIGHLIGHT_WIDTH "0"
#define DEF_SCALE_LABEL ""
#define DEF_SCALE_LENGTH "100"
@@ -474,7 +475,7 @@
#define DEF_SCROLLBAR_CURSOR ""
#define DEF_SCROLLBAR_EL_BORDER_WIDTH "-1"
#define DEF_SCROLLBAR_HIGHLIGHT_BG NORMAL_BG
-#define DEF_SCROLLBAR_HIGHLIGHT BLACK
+#define DEF_SCROLLBAR_HIGHLIGHT NORMAL_FG
#define DEF_SCROLLBAR_HIGHLIGHT_WIDTH "0"
#define DEF_SCROLLBAR_JUMP "0"
#define DEF_SCROLLBAR_ORIENT "vertical"
@@ -501,7 +502,7 @@
#define DEF_TEXT_FONT "TkFixedFont"
#define DEF_TEXT_HEIGHT "24"
#define DEF_TEXT_HIGHLIGHT_BG NORMAL_BG
-#define DEF_TEXT_HIGHLIGHT BLACK
+#define DEF_TEXT_HIGHLIGHT NORMAL_FG
#define DEF_TEXT_HIGHLIGHT_WIDTH "3"
#define DEF_TEXT_INSERT_BG NORMAL_FG
#define DEF_TEXT_INSERT_BD_COLOR "0"
diff --git a/macosx/tkMacOSXDialog.c b/macosx/tkMacOSXDialog.c
index 4bcaedf..f6b8357 100644
--- a/macosx/tkMacOSXDialog.c
+++ b/macosx/tkMacOSXDialog.c
@@ -350,49 +350,42 @@ static NSInteger showOpenSavePanel(
FilePanelCallbackInfo *callbackInfo)
{
NSInteger modalReturnCode;
+ int OSVersion = [NSApp macOSVersion];
- if (parent && ![parent attachedSheet]) {
- [panel beginSheetModalForWindow:parent
- completionHandler:^(NSModalResponse returnCode) {
- [NSApp tkFilePanelDidEnd:panel
- returnCode:returnCode
- contextInfo:callbackInfo ];
- }];
-
- /*
- * The sheet has been prepared, so now we have to run it as a modal
- * window. Using [NSApp runModalForWindow:] on macOS 10.15 or later
- * generates warnings on stderr. But using [NSOpenPanel runModal] or
- * [NSSavePanel runModal] on 10.14 or earler does not cause the
- * completion handler to run when the panel is closed.
- */
+ /*
+ * Use a sheet if -parent is specified (unless there is already a sheet).
+ */
- if ([NSApp macOSVersion] > 101400) {
- modalReturnCode = [panel runModal];
- } else {
+ if (parent && ![parent attachedSheet]) {
+ if (OSVersion < 101500) {
+ [panel beginSheetModalForWindow:parent
+ completionHandler:^(NSModalResponse returnCode) {
+ [NSApp tkFilePanelDidEnd:panel
+ returnCode:returnCode
+ contextInfo:callbackInfo ];
+ }];
modalReturnCode = [NSApp runModalForWindow:panel];
- }
- } else {
-
- /*
- * For the standalone file dialog, completion handlers do not work
- * at all on macOS 10.14 and earlier.
- */
-
- if ([NSApp macOSVersion] > 101400) {
- [panel beginWithCompletionHandler:^(NSModalResponse returnCode) {
+ } else if (OSVersion < 110000) {
+ [panel beginSheetModalForWindow:parent
+ completionHandler:^(NSModalResponse returnCode) {
[NSApp tkFilePanelDidEnd:panel
- returnCode:returnCode
- contextInfo:callbackInfo ];
- }];
+ returnCode:returnCode
+ contextInfo:callbackInfo ];
+ }];
modalReturnCode = [panel runModal];
} else {
+ [parent beginSheet: panel completionHandler:nil];
modalReturnCode = [panel runModal];
[NSApp tkFilePanelDidEnd:panel
- returnCode:modalReturnCode
- contextInfo:callbackInfo ];
- [panel close];
+ returnCode:modalReturnCode
+ contextInfo:callbackInfo ];
+ [parent endSheet:panel];
}
+ } else {
+ modalReturnCode = [panel runModal];
+ [NSApp tkFilePanelDidEnd:panel
+ returnCode:modalReturnCode
+ contextInfo:callbackInfo ];
}
return callbackInfo->cmdObj ? modalOther : modalReturnCode;
}
diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c
index ac5c721..d5396eb 100644
--- a/macosx/tkMacOSXDraw.c
+++ b/macosx/tkMacOSXDraw.c
@@ -27,6 +27,7 @@
#ifdef TK_MAC_DEBUG
#define TK_MAC_DEBUG_DRAWING
#define TK_MAC_DEBUG_IMAGE_DRAWING
+#define TK_MAC_DEBUG_CG
#endif
*/
@@ -513,7 +514,7 @@ XDrawSegments(
*
* XFillPolygon --
*
- * Draws a filled polygon.
+ * Draws a filled polygon using the even-odd fill algorithm,
*
* Results:
* None.
@@ -531,7 +532,7 @@ XFillPolygon(
GC gc, /* Use this GC. */
XPoint *points, /* Array of points. */
int npoints, /* Number of points. */
- TCL_UNUSED(int), /* Shape to draw. */
+ TCL_UNUSED(int), /* Shape to draw. */
int mode) /* Drawing mode. */
{
MacDrawable *macWin = (MacDrawable *)d;
@@ -1265,6 +1266,12 @@ TkMacOSXSetupDrawingContext(
Bool canDraw = true;
TKContentView *view = nil;
TkMacOSXDrawingContext dc = {};
+ CGFloat drawingHeight;
+
+#ifdef TK_MAC_DEBUG_CG
+ fprintf(stderr, "TkMacOSXSetupDrawingContext: %s\n",
+ macDraw->winPtr ? Tk_PathName(macDraw->winPtr) : "None");
+#endif
/*
* If the drawable is not a pixmap, get the associated NSView.
@@ -1296,14 +1303,10 @@ TkMacOSXSetupDrawingContext(
*/
dc.context = TkMacOSXGetCGContextForDrawable(d);
- if (dc.context) {
- dc.portBounds = CGContextGetClipBoundingBox(dc.context);
- } else {
+ if (!dc.context) {
NSRect drawingBounds, currentBounds;
-
dc.view = view;
dc.context = GET_CGCONTEXT;
- dc.portBounds = NSRectToCGRect([view bounds]);
if (dc.clipRgn) {
CGRect clipBounds;
CGAffineTransform t = { .a = 1, .b = 0, .c = 0, .d = -1, .tx = 0,
@@ -1355,37 +1358,66 @@ TkMacOSXSetupDrawingContext(
* Finish configuring the drawing context.
*/
- {
- CGAffineTransform t = {
- .a = 1, .b = 0,
- .c = 0, .d = -1,
- .tx = 0,
- .ty = dc.portBounds.size.height
- };
+ drawingHeight = view ? [view bounds].size.height :
+ CGContextGetClipBoundingBox(dc.context).size.height;
+ CGAffineTransform t = {
+ .a = 1, .b = 0,
+ .c = 0, .d = -1,
+ .tx = 0,
+ .ty = drawingHeight
+ };
+
+#ifdef TK_MAC_DEBUG_CG
+ fprintf(stderr, "TkMacOSXSetupDrawingContext: pushing GState for %s\n",
+ macDraw->winPtr ? Tk_PathName(macDraw->winPtr) : "None");
+#endif
- dc.portBounds.origin.x += macDraw->xOff;
- dc.portBounds.origin.y += macDraw->yOff;
- CGContextSaveGState(dc.context);
- CGContextSetTextDrawingMode(dc.context, kCGTextFill);
- CGContextConcatCTM(dc.context, t);
- if (dc.clipRgn) {
+ CGContextSaveGState(dc.context);
+ 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);
- CGContextSetRGBFillColor(dc.context, 1.0, 0.0, 0.0, 0.1);
- CGContextEOFillPath(dc.context);
- CGContextRestoreGState(dc.context);
+ CGContextSaveGState(dc.context);
+ ChkErr(HIShapeReplacePathInCGContext, dc.clipRgn, dc.context);
+ CGContextSetRGBFillColor(dc.context, 1.0, 0.0, 0.0, 0.1);
+ CGContextEOFillPath(dc.context);
+ CGContextRestoreGState(dc.context);
#endif /* TK_MAC_DEBUG_DRAWING */
+ if (!HIShapeIsRectangular(dc.clipRgn)) {
+
+ /*
+ * We expect the clipping path dc.clipRgn to consist of the
+ * bounding rectangle of the drawable window, together with
+ * disjoint smaller rectangles inside of it which bound its
+ * geometric children. In that case the even-odd rule will
+ * clip to the region inside the large rectangle and outside
+ * of the smaller rectangles.
+ */
+
+ ChkErr(HIShapeReplacePathInCGContext, dc.clipRgn, dc.context);
+
+#ifdef TK_MAC_DEBUG_CG
+ fprintf(stderr, "Setting complex clip for %s to:\n",
+ macDraw->winPtr ? Tk_PathName(macDraw->winPtr) : "None");
+ TkMacOSXPrintRectsInRegion(dc.clipRgn);
+#endif
+
+ CGContextEOClip(dc.context);
+ } else {
CGRect r;
- CGRect b = CGRectApplyAffineTransform(
- CGContextGetClipBoundingBox(dc.context), t);
- if (!HIShapeIsRectangular(dc.clipRgn) ||
- !CGRectContainsRect(*HIShapeGetBounds(dc.clipRgn, &r), b)) {
- ChkErr(HIShapeReplacePathInCGContext, dc.clipRgn, dc.context);
- CGContextEOClip(dc.context);
- }
+ HIShapeGetBounds(dc.clipRgn, &r);
+
+#ifdef TK_MAC_DEBUG_CG
+ fprintf(stderr, "Current clip BBox is %s\n",
+ NSStringFromRect(CGContextGetClipBoundingBox(GET_CGCONTEXT)).UTF8String);
+ fprintf(stderr, "Setting clip for %s to rect %s:\n",
+ macDraw->winPtr ? Tk_PathName(macDraw->winPtr) : "None",
+ NSStringFromRect(r).UTF8String);
+#endif
+
+ CGContextClipToRect(dc.context, r);
}
}
if (gc) {
@@ -1405,8 +1437,8 @@ TkMacOSXSetupDrawingContext(
TkMacOSXSetColorInContext(gc, gc->foreground, dc.context);
if (view) {
- CGContextSetPatternPhase(dc.context, CGSizeMake(
- dc.portBounds.size.width, dc.portBounds.size.height));
+ CGSize size = NSSizeToCGSize([view bounds].size);
+ CGContextSetPatternPhase(dc.context, size);
}
if (gc->function != GXcopy) {
TkMacOSXDbgMsg("Logical functions other than GXcopy are "
@@ -1446,13 +1478,9 @@ TkMacOSXSetupDrawingContext(
end:
#ifdef TK_MAC_DEBUG_DRAWING
- if (!canDraw && win != NULL) {
- TkWindow *winPtr = TkMacOSXGetTkWindow(win);
-
- if (winPtr) {
- fprintf(stderr, "Cannot draw in %s - postponing.\n",
- Tk_PathName(winPtr));
- }
+ if (!canDraw && macDraw->winPtr != NULL) {
+ fprintf(stderr, "Cannot draw in %s - postponing.\n",
+ Tk_PathName(macDraw->winPtr));
}
#endif
@@ -1487,13 +1515,21 @@ TkMacOSXRestoreDrawingContext(
if (dcPtr->context) {
CGContextSynchronize(dcPtr->context);
CGContextRestoreGState(dcPtr->context);
+
+#ifdef TK_MAC_DEBUG_CG
+ fprintf(stderr, "TkMacOSXRestoreDrawingContext: popped GState\n");
+#endif
+
}
if (dcPtr->clipRgn) {
CFRelease(dcPtr->clipRgn);
+ dcPtr->clipRgn = NULL;
}
+
#ifdef TK_MAC_DEBUG
bzero(dcPtr, sizeof(TkMacOSXDrawingContext));
-#endif /* TK_MAC_DEBUG */
+#endif
+
}
/*
diff --git a/macosx/tkMacOSXEmbed.c b/macosx/tkMacOSXEmbed.c
index 4af2e75..2f04e74 100644
--- a/macosx/tkMacOSXEmbed.c
+++ b/macosx/tkMacOSXEmbed.c
@@ -1116,6 +1116,9 @@ EmbedWindowDeleted(
prevPtr = NULL;
containerPtr = firstContainerPtr;
while (1) {
+ if (containerPtr == NULL) {
+ return;
+ }
if (containerPtr->embeddedPtr == winPtr) {
/*
* We also have to destroy our parent, to clean up the container.
diff --git a/macosx/tkMacOSXFont.c b/macosx/tkMacOSXFont.c
index 2a28f73..d9c1c01 100644
--- a/macosx/tkMacOSXFont.c
+++ b/macosx/tkMacOSXFont.c
@@ -1208,16 +1208,18 @@ TkpDrawAngledCharsInContext(
TkSetMacColor(gc->foreground, &fg);
attributes = [fontPtr->nsAttributes mutableCopy];
[attributes setObject:(id)fg forKey:(id)kCTForegroundColorAttributeName];
+ CFRelease(fg);
nsFont = [attributes objectForKey:NSFontAttributeName];
[nsFont setInContext:GET_NSCONTEXT(context, NO)];
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
attributedString = [[NSAttributedString alloc] initWithString:string
attributes:attributes];
+ [string release];
typesetter = CTTypesetterCreateWithAttributedString(
(CFAttributedStringRef)attributedString);
textX += (CGFloat) macWin->xOff;
textY += (CGFloat) macWin->yOff;
- height = drawingContext.portBounds.size.height;
+ height = [drawingContext.view bounds].size.height;
textY = height - textY;
t = CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, height);
if (angle != 0.0) {
@@ -1249,7 +1251,6 @@ TkpDrawAngledCharsInContext(
CFRelease(line);
CFRelease(typesetter);
[attributedString release];
- [string release];
[attributes release];
TkMacOSXRestoreDrawingContext(&drawingContext);
}
diff --git a/macosx/tkMacOSXImage.c b/macosx/tkMacOSXImage.c
index 69967af..f256d7a 100644
--- a/macosx/tkMacOSXImage.c
+++ b/macosx/tkMacOSXImage.c
@@ -4,26 +4,92 @@
* The code in this file provides an interface for XImages,
*
* Copyright (c) 1995-1997 Sun Microsystems, Inc.
- * Copyright 2001-2009, Apple Inc.
+ * Copyright (c) 2001-2009, Apple Inc.
* Copyright (c) 2005-2009 Daniel A. Steffen <das@users.sourceforge.net>
- * Copyright (c) 2017-2020 Marc Culler.
+ * Copyright (c) 2017-2021 Marc Culler.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*/
#include "tkMacOSXPrivate.h"
+#include "tkMacOSXConstants.h"
#include "xbytes.h"
static CGImageRef CreateCGImageFromPixmap(Drawable pixmap);
static CGImageRef CreateCGImageFromDrawableRect( Drawable drawable,
int x, int y, unsigned int width, unsigned int height);
+/* Pixel formats
+ *
+ * Tk uses the XImage structure defined in Xlib.h for storing images. The
+ * image data in an XImage is a 32-bit aligned array of bytes. Interpretation
+ * of that data is not specified, but the structure includes parameters which
+ * provide interpretation hints so that an application can use a family of
+ * different data structures.
+ *
+ * The possible values for the XImage format field are XYBitmap, XYPixmap and
+ * ZPixmap. The macOS port does not support the XYPixmap format. This means
+ * that bitmap images are stored as a single bit plane (XYBitmap) and that
+ * color images are stored as a sequence of pixel values (ZPixmap).
+ *
+ * For a ZPixmap, the number of bits allocated to each pixel is specified by
+ * the bits_per_pixel field of the XImage structure. The functions in this
+ * module which convert between XImage and native CGImage or NSImage structures
+ * only support XImages with 32 bits per pixel. The ImageGetPixel and PutPixel
+ * implementations in this file allow 1, 4, 8, 16 or 32 bits per pixel, however.
+ *
+ * In tkImgPhInstance.c the layout used for pixels is determined by the values
+ * of the red_mask, blue_mask and green_mask fields in the XImage structure.
+ * The Aqua port always sets red_mask = 0xFF0000, green_mask = 0xFF00, and
+ * blue_mask = 0xFF. This means that a 32bpp ZPixmap XImage uses ARGB32 pixels,
+ * with small-endian byte order BGRA. The data array for such an XImage can be
+ * passed directly to construct a CGBitmapImageRep if one specifies the
+ * bitmapInfo as kCGBitmapByteOrder32Big | kCGImageAlphaLast.
+ *
+ * The structures below describe the bitfields in two common 32 bpp pixel
+ * layouts. Note that bit field layouts are compiler dependent. The layouts
+ * shown in the comments are those produced by clang and gcc. Also note
+ * that kCGBitmapByteOrder32Big is consistently set when creating CGImages or
+ * CGImageBitmapReps.
+ */
+
+/* RGBA32 0xRRGGBBAA (Byte order is RGBA on big-endian systems.)
+ * This is used by NSBitmapImageRep when the bitmapFormat property is 0,
+ * the default value.
+ */
+
+typedef struct RGBA32pixel_t {
+ unsigned red: 8;
+ unsigned green: 8;
+ unsigned blue: 8;
+ unsigned alpha: 8;
+} RGBA32pixel;
+
+/*
+ * ARGB32 0xAARRGGBB (Byte order is ARGB on big-endian systems.)
+ * This is used by Aqua Tk for XImages and by NSBitmapImageReps whose
+ * bitmapFormat property is NSAlphaFirstBitmapFormat.
+ */
+
+typedef struct ARGB32pixel_t {
+ unsigned blue: 8;
+ unsigned green: 8;
+ unsigned red: 8;
+ unsigned alpha: 8;
+} ARGB32pixel;
+
+typedef union pixel32_t {
+ unsigned int uint;
+ RGBA32pixel rgba;
+ ARGB32pixel argb;
+} pixel32;
+
#pragma mark XImage handling
int
_XInitImageFuncPtrs(
- XImage *image)
+ TCL_UNUSED(XImage *)) /* image */
{
return 0;
}
@@ -45,13 +111,18 @@ _XInitImageFuncPtrs(
*----------------------------------------------------------------------
*/
-static void ReleaseData(void *info, const void *data, size_t size) {
+static void ReleaseData(
+ void *info,
+ TCL_UNUSED(const void *), /* data */
+ TCL_UNUSED(size_t)) /* size */
+{
ckfree(info);
}
CGImageRef
TkMacOSXCreateCGImageWithXImage(
- XImage *image)
+ XImage *image,
+ uint32_t alphaInfo)
{
CGImageRef img = NULL;
size_t bitsPerComponent, bitsPerPixel;
@@ -76,7 +147,7 @@ TkMacOSXCreateCGImageWithXImage(
if (image->bitmap_bit_order != MSBFirst) {
char *srcPtr = image->data + image->xoffset;
char *endPtr = srcPtr + len;
- char *destPtr = (data = ckalloc(len));
+ char *destPtr = (data = (char *)ckalloc(len));
while (srcPtr < endPtr) {
*destPtr++ = xBitReverseTable[(unsigned char)(*(srcPtr++))];
@@ -94,6 +165,7 @@ TkMacOSXCreateCGImageWithXImage(
provider, decode, 0);
}
} else if ((image->format == ZPixmap) && (image->bits_per_pixel == 32)) {
+
/*
* Color image
*/
@@ -101,6 +173,7 @@ TkMacOSXCreateCGImageWithXImage(
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
if (image->width == 0 && image->height == 0) {
+
/*
* CGCreateImage complains on early macOS releases.
*/
@@ -109,9 +182,7 @@ TkMacOSXCreateCGImageWithXImage(
}
bitsPerComponent = 8;
bitsPerPixel = 32;
- bitmapInfo = (image->byte_order == MSBFirst ?
- kCGBitmapByteOrder32Little : kCGBitmapByteOrder32Big);
- bitmapInfo |= kCGImageAlphaLast;
+ bitmapInfo = kCGBitmapByteOrder32Big | alphaInfo;
data = (char *)memcpy(ckalloc(len), image->data + image->xoffset, len);
if (data) {
provider = CGDataProviderCreateWithData(data, data, len,
@@ -201,14 +272,12 @@ ImageGetPixel(
switch (image->bits_per_pixel) {
case 32: /* 8 bits per channel */
- r = (*((unsigned int*) srcPtr) >> 16) & 0xff;
- g = (*((unsigned int*) srcPtr) >> 8) & 0xff;
- b = (*((unsigned int*) srcPtr) ) & 0xff;
- /*if (image->byte_order == LSBFirst) {
- r = srcPtr[2]; g = srcPtr[1]; b = srcPtr[0];
- } else {
- r = srcPtr[1]; g = srcPtr[2]; b = srcPtr[3];
- }*/
+ {
+ ARGB32pixel *pixel = (ARGB32pixel *)srcPtr;
+ r = pixel->red;
+ g = pixel->green;
+ b = pixel->blue;
+ }
break;
case 16: /* 5 bits per channel */
r = (*((unsigned short*) srcPtr) >> 7) & 0xf8;
@@ -245,7 +314,10 @@ ImageGetPixel(
*
* ImagePutPixel --
*
- * Set a single pixel in an image.
+ * Set a single pixel in an image. The pixel is provided as an unsigned
+ * 32-bit integer. The value of that integer is interpreted by assuming
+ * that its low-order N bits have the format specified by the XImage,
+ * where N is equal to the bits_per_pixel field of the XImage.
*
* Results:
* None.
@@ -271,27 +343,20 @@ ImagePutPixel(
if (image->bits_per_pixel == 32) {
*((unsigned int*) dstPtr) = pixel;
} else {
- unsigned char r = ((pixel & image->red_mask) >> 16) & 0xff;
- unsigned char g = ((pixel & image->green_mask) >> 8) & 0xff;
- unsigned char b = ((pixel & image->blue_mask) ) & 0xff;
switch (image->bits_per_pixel) {
case 16:
- *((unsigned short*) dstPtr) = ((r & 0xf8) << 7) |
- ((g & 0xf8) << 2) | ((b & 0xf8) >> 3);
+ *((unsigned short*) dstPtr) = pixel & 0xffff;
break;
case 8:
- *dstPtr = ((r & 0xc0) >> 2) | ((g & 0xc0) >> 4) |
- ((b & 0xc0) >> 6);
+ *dstPtr = pixel & 0xff;
break;
case 4: {
- unsigned char c = ((r & 0x80) >> 5) | ((g & 0x80) >> 6) |
- ((b & 0x80) >> 7);
- *dstPtr = (x % 2) ? ((*dstPtr & 0xf0) | (c & 0x0f)) :
- ((*dstPtr & 0x0f) | ((c << 4) & 0xf0));
+ *dstPtr = (x % 2) ? ((*dstPtr & 0xf0) | (pixel & 0x0f)) :
+ ((*dstPtr & 0x0f) | ((pixel << 4) & 0xf0));
break;
}
case 1:
- *dstPtr = ((r|g|b) & 0x80) ? (*dstPtr | (0x80 >> (x % 8))) :
+ *dstPtr = pixel ? (*dstPtr | (0x80 >> (x % 8))) :
(*dstPtr & ~(0x80 >> (x % 8)));
break;
}
@@ -319,7 +384,7 @@ ImagePutPixel(
XImage *
XCreateImage(
Display* display,
- Visual* visual,
+ TCL_UNUSED(Visual*), /* visual */
unsigned int depth,
int format,
int offset,
@@ -388,14 +453,25 @@ XCreateImage(
/*
*----------------------------------------------------------------------
*
- * TkPutImage, XPutImage --
+ * TkPutImage, XPutImage, TkpPutRGBAImage --
+ *
+ * These functions, which all have the same signature, copy a rectangular
+ * subimage of an XImage into a drawable. The first two are identical on
+ * macOS. They assume that the XImage data has the structure of a 32bpp
+ * ZPixmap in which the image data is an array of 32bit integers packed
+ * with 8 bit values for the Red Green and Blue channels. They ignore the
+ * fourth byte. The function TkpPutRGBAImage assumes that the XImage data
+ * has been extended by using the fourth byte to store an 8-bit Alpha
+ * value. (The Alpha data is assumed not to pre-multiplied). The image
+ * is then drawn into the drawable using standard Porter-Duff Source Atop
+ * Composition (kCGBlendModeSourceAtop in Apple's Core Graphics).
*
- * Copies a rectangular subimage of an XImage into a drawable. Currently
- * this is only called by TkImgPhotoDisplay, using a Window as the
- * drawable.
+ * The TkpPutRGBAImage function is used by TkImgPhotoDisplay to render photo
+ * images if the compile-time variable TK_CAN_RENDER_RGBA is defined in
+ * a platform's tkXXXXPort.h header, as is the case for the macOS Aqua port.
*
* Results:
- * None.
+ * These functions return either BadDrawable or Success.
*
* Side effects:
* Draws the image on the specified drawable.
@@ -403,8 +479,12 @@ XCreateImage(
*----------------------------------------------------------------------
*/
-int
-XPutImage(
+#define USE_ALPHA kCGImageAlphaLast
+#define IGNORE_ALPHA kCGImageAlphaNoneSkipLast
+
+static int
+TkMacOSXPutImage(
+ uint32_t pixelFormat,
Display* display, /* Display. */
Drawable drawable, /* Drawable to place image on. */
GC gc, /* GC to use. */
@@ -418,14 +498,14 @@ XPutImage(
{
TkMacOSXDrawingContext dc;
MacDrawable *macDraw = (MacDrawable *)drawable;
-
+ int result = Success;
display->request++;
if (!TkMacOSXSetupDrawingContext(drawable, gc, &dc)) {
return BadDrawable;
}
if (dc.context) {
CGRect bounds, srcRect, dstRect;
- CGImageRef img = TkMacOSXCreateCGImageWithXImage(image);
+ CGImageRef img = TkMacOSXCreateCGImageWithXImage(image, pixelFormat);
/*
* The CGContext for a pixmap is RGB only, with A = 0.
@@ -435,7 +515,6 @@ XPutImage(
CGContextSetBlendMode(dc.context, kCGBlendModeSourceAtop);
}
if (img) {
-
bounds = CGRectMake(0, 0, image->width, image->height);
srcRect = CGRectMake(src_x, src_y, width, height);
dstRect = CGRectMake(dest_x, dest_y, width, height);
@@ -445,63 +524,103 @@ XPutImage(
CFRelease(img);
} else {
TkMacOSXDbgMsg("Invalid source drawable");
+ result = BadDrawable;
}
} else {
TkMacOSXDbgMsg("Invalid destination drawable");
+ result = BadDrawable;
}
TkMacOSXRestoreDrawingContext(&dc);
- return Success;
+ return result;
}
-int
-TkPutImage(
- unsigned long *colors, /* Array of pixel values used by this image.
- * May be NULL. */
- int ncolors, /* Number of colors used, or 0. */
- Display *display,
- Drawable d, /* Destination drawable. */
+int XPutImage(
+ Display* display,
+ Drawable drawable,
GC gc,
- XImage *image, /* Source image. */
- int src_x, int src_y, /* Offset of subimage. */
- int dest_x, int dest_y, /* Position of subimage origin in drawable. */
- unsigned int width, unsigned int height)
- /* Dimensions of subimage. */
-{
- return XPutImage(display, d, gc, image, src_x, src_y, dest_x, dest_y, width, height);
+ XImage* image,
+ int src_x,
+ int src_y,
+ int dest_x,
+ int dest_y,
+ unsigned int width,
+ unsigned int height) {
+ return TkMacOSXPutImage(IGNORE_ALPHA, display, drawable, gc, image,
+ src_x, src_y, dest_x, dest_y, width, height);
+}
+
+int TkPutImage(
+ TCL_UNUSED(unsigned long *),
+ TCL_UNUSED(int),
+ Display* display,
+ Drawable drawable,
+ GC gc,
+ XImage* image,
+ int src_x,
+ int src_y,
+ int dest_x,
+ int dest_y,
+ unsigned int width,
+ unsigned int height) {
+ return TkMacOSXPutImage(IGNORE_ALPHA, display, drawable, gc, image,
+ src_x, src_y, dest_x, dest_y, width, height);
+}
+
+int TkpPutRGBAImage(
+ Display* display,
+ Drawable drawable,
+ GC gc,
+ XImage* image,
+ int src_x,
+ int src_y,
+ int dest_x,
+ int dest_y,
+ unsigned int width,
+ unsigned int height) {
+ return TkMacOSXPutImage(USE_ALPHA, display, drawable, gc, image,
+ src_x, src_y, dest_x, dest_y, width, height);
}
+
/*
*----------------------------------------------------------------------
*
* CreateCGImageFromDrawableRect
*
- * Extract image data from a MacOSX drawable as a CGImage.
+ * Extract image data from a MacOSX drawable as a CGImage. The drawable
+ * may be either a pixmap or a window, but there issues in the case of
+ * a window.
*
- * This is only called by XGetImage and XCopyArea. The Tk core uses
- * these functions on some platforms, but on macOS the core does not
- * call them with a source drawable which is a window. Such calls are
- * used only for double-buffered drawing. Since macOS defines the
- * macro TK_NO_DOUBLE_BUFFERING, the generic code never calls XGetImage
- * or XCopyArea on macOS. Nonetheless, these function are in the stubs
- * table and therefore could be used by extensions.
+ * CreateCGImageFromDrawableRect is called by XGetImage and XCopyArea.
+ * The Tk core uses these two functions on some platforms in order to
+ * implement explicit double-buffered drawing -- a pixmap is copied from a
+ * window, modified using CPU-based graphics composition, and then copied
+ * back to the window. Platforms, such as macOS, on which the system
+ * provides double-buffered drawing and GPU-based composition operations
+ * can avoid calls to XGetImage and XCopyArea from the core by defining
+ * the compile-time variable TK_NO_DOUBLE_BUFFERING. Nonetheless, these
+ * two functions are in the stubs table and therefore could be used by
+ * extensions.
*
- * This implementation does not work correctly. Originally it relied on
+ * The implementation here does not always work correctly when the source
+ * is a window. The original version of this function relied on
* [NSBitmapImageRep initWithFocusedViewRect:view_rect] which was
* deprecated by Apple in OSX 10.14 and also required the use of other
* deprecated functions such as [NSView lockFocus]. Apple's suggested
* replacement is [NSView cacheDisplayInRect: toBitmapImageRep:] and that
- * is what is being used here. However, that method only works when the
- * view has a valid CGContext, and a view is only guaranteed to have a
- * valid context during a call to [NSView drawRect]. To further complicate
- * matters, cacheDisplayInRect calls [NSView drawRect]. Essentially it is
- * asking the view to draw a subrectangle of itself using a special
- * graphics context which is linked to the BitmapImageRep. But our
- * implementation of [NSView drawRect] does not allow recursive calls. If
- * called recursively it returns immediately without doing any drawing.
- * So the bottom line is that this function either returns a NULL pointer
- * or a black image. To make it useful would require a significant amount
- * of rewriting of the drawRect method. Perhaps the next release of OSX
- * will include some more helpful ways of doing this.
+ * is being used here. However, cacheDisplayInRect works by calling
+ * [NSView drawRect] after setting the current graphics context to be one
+ * which draws to a bitmap. There are situations in which this can be
+ * used, e.g. when taking a screenshot of a window. But it cannot be used
+ * as part of a normal display procedure, using the copy-modify-paste
+ * paradigm that is the basis of the explicit double-buffering. Since the
+ * copy operation will call the same display procedure that is calling
+ * this function via XGetImage or XCopyArea, this would create an infinite
+ * recursion.
+ *
+ * An alternative to the copy-modify-paste paradigm is to use GPU-based
+ * graphics composition, clipping to the specified rectangle. That is
+ * the approach that must be followed by display procedures on macOS.
*
* Results:
* Returns an NSBitmapRep representing the image of the given rectangle of
@@ -528,51 +647,43 @@ CreateCGImageFromDrawableRect(
{
MacDrawable *mac_drawable = (MacDrawable *)drawable;
CGContextRef cg_context = NULL;
+ CGRect image_rect = CGRectMake(x, y, width, height);
CGImageRef cg_image = NULL, result = NULL;
- NSBitmapImageRep *bitmapRep = nil;
- NSView *view = nil;
+ unsigned char *imageData = NULL;
if (mac_drawable->flags & TK_IS_PIXMAP) {
- /*
- * This MacDrawable is a bitmap, so its view is NULL.
- */
-
- CGRect image_rect = CGRectMake(x, y, width, height);
-
cg_context = TkMacOSXGetCGContextForDrawable(drawable);
- cg_image = CGBitmapContextCreateImage((CGContextRef) cg_context);
- if (cg_image) {
- result = CGImageCreateWithImageInRect(cg_image, image_rect);
- CGImageRelease(cg_image);
- }
- } else if (TkMacOSXGetNSViewForDrawable(mac_drawable) != nil) {
-
- /*
- * Convert Tk top-left to NSView bottom-left coordinates.
- */
-
- int view_height = [view bounds].size.height;
- NSRect view_rect = NSMakeRect(x + mac_drawable->xOff,
- view_height - height - y - mac_drawable->yOff,
- width, height);
-
- /*
- * Attempt to copy from the view to a bitmapImageRep. If the view does
- * not have a valid CGContext, doing this will silently corrupt memory
- * and make a big mess. So, in that case, we just return NULL.
- */
-
- if (view == [NSView focusView]) {
- bitmapRep = [view bitmapImageRepForCachingDisplayInRect: view_rect];
- [view cacheDisplayInRect:view_rect toBitmapImageRep:bitmapRep];
- result = [bitmapRep CGImage];
- CFRelease(bitmapRep);
- } else {
- TkMacOSXDbgMsg("No CGContext - cannot copy from screen to bitmap.");
- result = NULL;
+ if (cg_context) {
+ cg_image = CGBitmapContextCreateImage((CGContextRef) cg_context);
}
} else {
- TkMacOSXDbgMsg("Invalid source drawable");
+ NSView *view = TkMacOSXGetNSViewForDrawable(mac_drawable);
+ if (view == nil) {
+ TkMacOSXDbgMsg("Invalid source drawable");
+ return NULL;
+ }
+ NSSize size = view.frame.size;
+ NSUInteger view_width = size.width, view_height = size.height;
+ NSUInteger bytesPerPixel = 4,
+ bytesPerRow = bytesPerPixel * view_width,
+ bitsPerComponent = 8;
+ imageData = ckalloc(view_height * bytesPerRow);
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+ cg_context = CGBitmapContextCreate(imageData, view_width, view_height,
+ bitsPerComponent, bytesPerRow, colorSpace,
+ kCGImageAlphaPremultipliedLast |
+ kCGBitmapByteOrder32Big);
+ CFRelease(colorSpace);
+ [view.layer renderInContext:cg_context];
+ }
+ if (cg_context) {
+ cg_image = CGBitmapContextCreateImage(cg_context);
+ CGContextRelease(cg_context);
+ }
+ if (cg_image) {
+ result = CGImageCreateWithImageInRect(cg_image, image_rect);
+ CGImageRelease(cg_image);
}
+ ckfree(imageData);
return result;
}
@@ -627,9 +738,6 @@ CreateCGImageFromPixmap(
*
*----------------------------------------------------------------------
*/
-struct pixel_fmt {int r; int g; int b; int a;};
-static struct pixel_fmt bgra = {2, 1, 0, 3};
-static struct pixel_fmt abgr = {3, 2, 1, 0};
XImage *
XGetImage(
@@ -639,14 +747,13 @@ XGetImage(
int y,
unsigned int width,
unsigned int height,
- unsigned long plane_mask,
+ TCL_UNUSED(unsigned long), /* plane_mask */
int format)
{
NSBitmapImageRep* bitmapRep = nil;
NSUInteger bitmap_fmt = 0;
XImage* imagePtr = NULL;
char *bitmap = NULL;
- char R, G, B, A;
int depth = 32, offset = 0, bitmap_pad = 0;
unsigned int bytes_per_row, size, row, n, m;
@@ -669,48 +776,49 @@ XGetImage(
size = [bitmapRep bytesPerPlane];
bytes_per_row = [bitmapRep bytesPerRow];
bitmap = (char *)ckalloc(size);
- if (!bitmap
- || (bitmap_fmt != 0 && bitmap_fmt != 1)
- || [bitmapRep samplesPerPixel] != 4
- || [bitmapRep isPlanar] != 0
- || bytes_per_row < 4 * width
- || size != bytes_per_row * height) {
+ if ((bitmap_fmt != 0 && bitmap_fmt != NSAlphaFirstBitmapFormat)
+ || [bitmapRep samplesPerPixel] != 4
+ || [bitmapRep isPlanar] != 0
+ || bytes_per_row < 4 * width
+ || size != bytes_per_row * height) {
TkMacOSXDbgMsg("XGetImage: Unrecognized bitmap format");
- CFRelease(bitmapRep);
+ [bitmapRep release];
return NULL;
}
memcpy(bitmap, (char *)[bitmapRep bitmapData], size);
- CFRelease(bitmapRep);
-
- /*
- * When Apple extracts a bitmap from an NSView, it may be in either
- * BGRA or ABGR format. For an XImage we need RGBA.
- */
-
- struct pixel_fmt pixel = bitmap_fmt == 0 ? bgra : abgr;
+ [bitmapRep release];
for (row = 0, n = 0; row < height; row++, n += bytes_per_row) {
for (m = n; m < n + 4*width; m += 4) {
- R = *(bitmap + m + pixel.r);
- G = *(bitmap + m + pixel.g);
- B = *(bitmap + m + pixel.b);
- A = *(bitmap + m + pixel.a);
-
- *(bitmap + m) = R;
- *(bitmap + m + 1) = G;
- *(bitmap + m + 2) = B;
- *(bitmap + m + 3) = A;
+ pixel32 pixel = *((pixel32 *)(bitmap + m));
+ if (bitmap_fmt == 0) { // default format
+
+ /*
+ * This pixel is in ARGB32 format. We need RGBA32.
+ */
+
+ pixel32 flipped;
+ flipped.rgba.red = pixel.argb.red;
+ flipped.rgba.green = pixel.argb.green;
+ flipped.rgba.blue = pixel.argb.blue;
+ flipped.rgba.alpha = pixel.argb.alpha;
+ *((pixel32 *)(bitmap + m)) = flipped;
+ } else { // bitmap_fmt = NSAlphaFirstBitmapFormat
+ *((pixel32 *)(bitmap + m)) = pixel;
+ }
}
}
imagePtr = XCreateImage(display, NULL, depth, format, offset,
(char*) bitmap, width, height,
bitmap_pad, bytes_per_row);
} else {
+
/*
* There are some calls to XGetImage in the generic Tk code which pass
* an XYPixmap rather than a ZPixmap. XYPixmaps should be handled
* here.
*/
+
TkMacOSXDbgMsg("XGetImage does not handle XYPixmaps at the moment.");
}
return imagePtr;
diff --git a/macosx/tkMacOSXInit.c b/macosx/tkMacOSXInit.c
index 9100d45..53508aa 100644
--- a/macosx/tkMacOSXInit.c
+++ b/macosx/tkMacOSXInit.c
@@ -5,7 +5,7 @@
* functions.
*
* Copyright (c) 1995-1997 Sun Microsystems, Inc.
- * Copyright 2001-2009, Apple Inc.
+ * Copyright (c) 2001-2009, Apple Inc.
* Copyright (c) 2005-2009 Daniel A. Steffen <das@users.sourceforge.net>
* Copyright (c) 2017 Marc Culler
*
@@ -17,6 +17,7 @@
#include <dlfcn.h>
#include <objc/objc-auto.h>
#include <sys/stat.h>
+#include <sys/utsname.h>
static char tkLibPath[PATH_MAX + 1] = "";
@@ -103,18 +104,38 @@ static int TkMacOSXGetAppPathCmd(ClientData cd, Tcl_Interp *ip,
#endif
[self _setupWindowNotifications];
[self _setupApplicationNotifications];
+
+ if ([NSApp macOSVersion] >= 110000) {
+
+ /*
+ * Initialize Apple Event processing. Apple's docs (see
+ * https://developer.apple.com/documentation/appkit/nsapplication)
+ * recommend doing this here, although historically we have
+ * done this in applicationWillFinishLaunching. In response to
+ * bug 7bb246b072.
+ */
+
+ TkMacOSXInitAppleEvents(_eventInterp);
+
+ }
}
-(void)applicationDidFinishLaunching:(NSNotification *)notification
{
(void)notification;
+ if ([NSApp macOSVersion] < 110000) {
+
/*
- * Initialize event processing.
+ * Initialize Apple Event processing on macOS versions
+ * older than Big Sur (11).
*/
TkMacOSXInitAppleEvents(_eventInterp);
+ }
+
+
/*
* Initialize the graphics context.
*/
@@ -168,6 +189,7 @@ static int TkMacOSXGetAppPathCmd(ClientData cd, Tcl_Interp *ip,
*/
int minorVersion, majorVersion;
+
#if MAC_OS_X_VERSION_MAX_ALLOWED < 101000
Gestalt(gestaltSystemVersionMinor, (SInt32*)&minorVersion);
majorVersion = 10;
@@ -177,6 +199,24 @@ static int TkMacOSXGetAppPathCmd(ClientData cd, Tcl_Interp *ip,
majorVersion = systemVersion.majorVersion;
minorVersion = systemVersion.minorVersion;
#endif
+
+ if (majorVersion == 10 && minorVersion == 16) {
+
+ /*
+ * If a program compiled with a macOS 10.XX SDK is run on macOS 11.0 or
+ * later then it will report majorVersion 10 and minorVersion 16, no
+ * matter what the actual OS version of the host may be. And of course
+ * Apple never released macOS 10.16. To work around this we guess the
+ * OS version from the kernel release number, as reported by uname.
+ */
+
+ struct utsname name;
+ char *endptr;
+ if (uname(&name) == 0) {
+ majorVersion = strtol(name.release, &endptr, 10) - 9;
+ minorVersion = 0;
+ }
+ }
[NSApp setMacOSVersion: 10000*majorVersion + 100*minorVersion];
/*
diff --git a/macosx/tkMacOSXKeyEvent.c b/macosx/tkMacOSXKeyEvent.c
index d092749..961072f 100644
--- a/macosx/tkMacOSXKeyEvent.c
+++ b/macosx/tkMacOSXKeyEvent.c
@@ -72,6 +72,18 @@ static NSUInteger textInputModifiers;
}
/*
+ * Discard repeating KeyDown events if the repeat speed has been set to
+ * "off" in System Preferences. It is unclear why we get these, but we do.
+ * See ticket [2ecb09d118].
+ */
+
+ if ([theEvent type] == NSKeyDown &&
+ [theEvent isARepeat] &&
+ [NSEvent keyRepeatDelay] < 0) {
+ return theEvent;
+ }
+
+ /*
* If a local grab is in effect, key events for windows in the
* grabber's application are redirected to the grabber. Key events
* for other applications are delivered normally. If a global
@@ -82,7 +94,10 @@ static NSUInteger textInputModifiers;
if (grabWinPtr) {
if (winPtr->dispPtr->grabFlags || /* global grab */
grabWinPtr->mainPtr == winPtr->mainPtr){ /* same application */
- winPtr =winPtr->dispPtr->focusPtr;
+ winPtr = winPtr->dispPtr->focusPtr;
+ if (!winPtr) {
+ return theEvent;
+ }
tkwin = (Tk_Window)winPtr;
}
}
@@ -92,26 +107,33 @@ static NSUInteger textInputModifiers;
*/
if (type == NSKeyUp || type == NSKeyDown) {
- if ([[theEvent characters] length] > 0) {
- keychar = [[theEvent characters] characterAtIndex:0];
+ NSString *characters = [theEvent characters];
+ if (characters.length > 0) {
+ keychar = [characters characterAtIndex:0];
/*
* Currently, real keys always send BMP characters, but who knows?
*/
if (CFStringIsSurrogateHighCharacter(keychar)) {
- UniChar lowChar = [[theEvent characters] characterAtIndex:1];
+ UniChar lowChar = [characters characterAtIndex:1];
keychar = CFStringGetLongCharacterForSurrogatePair(
keychar, lowChar);
}
} else {
/*
- * This is a dead key, such as Option-e, so it should go to the
- * TextInputClient.
+ * This is a dead key, such as Option-e, so it usually should get
+ * passed to the TextInputClient. But if it has a Command modifier
+ * then it is not functioning as a dead key and should not be
+ * handled by the TextInputClient. See ticket [1626ed65b8] and the
+ * method performKeyEquivalent which is implemented in
+ * tkMacOSXMenu.c.
*/
- use_text_input = YES;
+ if (!(modifiers & NSCommandKeyMask)) {
+ use_text_input = YES;
+ }
}
/*
@@ -255,7 +277,6 @@ static NSUInteger textInputModifiers;
*/
if (type == NSKeyDown && [theEvent isARepeat]) {
-
xEvent.xany.type = KeyRelease;
Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
xEvent.xany.type = KeyPress;
@@ -692,8 +713,12 @@ XGrabKeyboard(
MacDrawable *macWin = (MacDrawable *)grab_window;
if (w && macWin->toplevel->winPtr == (TkWindow *) captureWinPtr) {
- if (modalSession) {
- Tcl_Panic("XGrabKeyboard: already grabbed");
+ if (modalSession ) {
+ if (keyboardGrabNSWindow == w) {
+ return GrabSuccess;
+ } else {
+ Tcl_Panic("XGrabKeyboard: already grabbed");
+ }
}
keyboardGrabNSWindow = w;
[w retain];
diff --git a/macosx/tkMacOSXMenu.c b/macosx/tkMacOSXMenu.c
index 1af18c4..09d91f0 100644
--- a/macosx/tkMacOSXMenu.c
+++ b/macosx/tkMacOSXMenu.c
@@ -192,6 +192,34 @@ TKBackgroundLoop *backgroundLoop = nil;
{
return (_tkSpecial == special);
}
+
+/*
+ * There are cases where a KeyEquivalent (aka menu accelerator) is defined for
+ * a "dead key", i.e. a key which does not have an associated character but is
+ * only meant to be the start of a composition sequence. For example, on a
+ * Spanish keyboard both the ' and the ` keys are dead keys used to place
+ * accents over letters. But ⌘` is a standard KeyEquivalent which cycles
+ * through the open windows of an application, changing the focus to the next
+ * window.
+ *
+ * The performKeyEquivalent callback method is being overridden here to work
+ * around a bug reported in [1626ed65b8]. When a dead key that is also as a
+ * KeyEquivalent is pressed, a KeyDown event with no characters is passed to
+ * performKeyEquivalent. The default implementation provided by Apple will
+ * cause that event to be routed to some private methods of NSMenu which raise
+ * NSInvalidArgumentException, causing an abort. Returning NO in such a case
+ * prevents the abort, but does not prevent the KeyEquivalent action from being
+ * invoked, presumably because the event does get correctly handled higher in
+ * the responder chain.
+ */
+
+- (BOOL)performKeyEquivalent:(NSEvent *)event
+{
+ if (event.characters.length == 0) {
+ return NO;
+ }
+ return [super performKeyEquivalent:event];
+}
@end
@implementation TKMenu(TKMenuPrivate)
diff --git a/macosx/tkMacOSXMouseEvent.c b/macosx/tkMacOSXMouseEvent.c
index 03d5730..2fcdeb9 100644
--- a/macosx/tkMacOSXMouseEvent.c
+++ b/macosx/tkMacOSXMouseEvent.c
@@ -114,6 +114,17 @@ enum {
}
case NSLeftMouseUp:
case NSLeftMouseDown:
+
+ /*
+ * Ignore mouse button events which arrive while the app is inactive.
+ * These events will be resent after activation, causing duplicate
+ * actions when an app is activated by a bound mouse event. See ticket
+ * [7bda9882cb].
+ */
+
+ if (! [NSApp isActive]) {
+ return theEvent;
+ }
case NSMouseMoved:
case NSScrollWheel:
#if 0
@@ -170,20 +181,18 @@ enum {
*/
capture = TkMacOSXGetCapture();
- if (capture) {
+ if (eventWindow) {
+ winPtr = TkMacOSXGetTkWindow(eventWindow);
+ } else if (capture) {
winPtr = (TkWindow *) capture;
eventWindow = TkMacOSXGetNSWindowForDrawable(winPtr->window);
if (!eventWindow) {
return theEvent;
}
- } else {
- if (eventWindow) {
- winPtr = TkMacOSXGetTkWindow(eventWindow);
- }
- if (!winPtr) {
- eventWindow = [NSApp mainWindow];
- winPtr = TkMacOSXGetTkWindow(eventWindow);
- }
+ }
+ if (!winPtr) {
+ eventWindow = [NSApp mainWindow];
+ winPtr = TkMacOSXGetTkWindow(eventWindow);
}
if (!winPtr) {
diff --git a/macosx/tkMacOSXNotify.c b/macosx/tkMacOSXNotify.c
index f32fa76..208d846 100644
--- a/macosx/tkMacOSXNotify.c
+++ b/macosx/tkMacOSXNotify.c
@@ -340,8 +340,9 @@ TkMacOSXNotifyExitHandler(
* 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.
+ * only times when any NSViews have the needsDisplay property set to YES
+ * are during execution of this function or in the addDirtyRect method
+ * of TKContentView.
*
* 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
@@ -377,7 +378,8 @@ TkMacOSXDrawAllViews(
if (dirtyCount) {
continue;
}
- [view setNeedsDisplayInRect:[view tkDirtyRect]];
+ [[view layer] setNeedsDisplayInRect:[view tkDirtyRect]];
+ [view setNeedsDisplay:YES];
}
} else {
[window displayIfNeeded];
diff --git a/macosx/tkMacOSXPort.h b/macosx/tkMacOSXPort.h
index 461204e..088d402 100644
--- a/macosx/tkMacOSXPort.h
+++ b/macosx/tkMacOSXPort.h
@@ -49,6 +49,9 @@
# include <inttypes.h>
#endif
#include <unistd.h>
+#if defined(__GNUC__) && !defined(__cplusplus)
+# pragma GCC diagnostic ignored "-Wc++-compat"
+#endif
#include <X11/Xlib.h>
#include <X11/cursorfont.h>
#include <X11/keysym.h>
@@ -75,6 +78,19 @@
#endif
/*
+ * Used to tag functions that are only to be visible within the module being
+ * built and not outside it (where this is supported by the linker).
+ */
+
+#ifndef MODULE_SCOPE
+# ifdef __cplusplus
+# define MODULE_SCOPE extern "C"
+# else
+# define MODULE_SCOPE extern
+# endif
+#endif
+
+/*
* The following macro defines the number of fd_masks in an fd_set:
*/
@@ -163,11 +179,16 @@
#define TK_DYNAMIC_COLORMAP 0x0fffffff
/*
- * Inform tkImgPhInstance.c that our tkPutImage can render an image with an
- * alpha channel directly into a window.
+ * Inform tkImgPhInstance.c that we implement TkpPutRGBAImage to render RGBA
+ * images directly into a window.
*/
-#define TKPUTIMAGE_CAN_BLEND
+#define TK_CAN_RENDER_RGBA
+
+MODULE_SCOPE int TkpPutRGBAImage(
+ Display* display, Drawable drawable, GC gc,XImage* image,
+ int src_x, int src_y, int dest_x, int dest_y,
+ unsigned int width, unsigned int height);
/*
* Used by xcolor.c
diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h
index ed24255..be2264f 100644
--- a/macosx/tkMacOSXPrivate.h
+++ b/macosx/tkMacOSXPrivate.h
@@ -27,6 +27,7 @@
#define TextStyle MacTextStyle
#import <ApplicationServices/ApplicationServices.h>
#import <Cocoa/Cocoa.h>
+#import <QuartzCore/QuartzCore.h>
#ifndef NO_CARBON_H
#import <Carbon/Carbon.h>
#endif
@@ -203,7 +204,6 @@ typedef struct TkMacOSXDrawingContext {
CGContextRef context;
NSView *view;
HIShapeRef clipRgn;
- CGRect portBounds;
} TkMacOSXDrawingContext;
/*
@@ -231,7 +231,8 @@ MODULE_SCOPE OSStatus TkMacOSHIShapeUnionWithRect(HIMutableShapeRef inShape,
const CGRect *inRect);
MODULE_SCOPE OSStatus TkMacOSHIShapeUnion(HIShapeRef inShape1,
HIShapeRef inShape2, HIMutableShapeRef outResult);
-
+MODULE_SCOPE int TkMacOSXCountRectsInRegion(HIShapeRef shape);
+MODULE_SCOPE void TkMacOSXPrintRectsInRegion(HIShapeRef shape);
/*
* Prototypes of TkAqua internal procs.
*/
diff --git a/macosx/tkMacOSXRegion.c b/macosx/tkMacOSXRegion.c
index 6c70a63..fbb41cb 100644
--- a/macosx/tkMacOSXRegion.c
+++ b/macosx/tkMacOSXRegion.c
@@ -555,6 +555,55 @@ TkMacOSHIShapeUnion(
return result;
}
+static OSStatus
+rectCounter(
+ int msg,
+ TCL_UNUSED(HIShapeRef),
+ const CGRect *rect,
+ void *ref)
+{
+ int *count = (int *)ref;
+ (*count)++;
+ return noErr;
+}
+
+static OSStatus
+rectPrinter(
+ int msg,
+ TCL_UNUSED(HIShapeRef),
+ const CGRect *rect,
+ void *ref)
+{
+ if (rect) {
+ fprintf(stderr, " %s\n", NSStringFromRect(*rect).UTF8String);
+ }
+ return noErr;
+}
+
+int
+TkMacOSXCountRectsInRegion(
+ HIShapeRef shape)
+{
+ int rect_count = 0;
+ if (!HIShapeIsEmpty(shape)) {
+ ChkErr(HIShapeEnumerate, shape,
+ kHIShapeParseFromBottom|kHIShapeParseFromLeft,
+ rectCounter, &rect_count);
+ }
+ return rect_count;
+}
+
+void
+TkMacOSXPrintRectsInRegion(
+ HIShapeRef shape)
+{
+ if (!HIShapeIsEmpty(shape)) {
+ ChkErr(HIShapeEnumerate, shape,
+ kHIShapeParseFromBottom|kHIShapeParseFromLeft,
+ rectPrinter, NULL);
+ }
+}
+
/*
* Local Variables:
* mode: objc
diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c
index 2a6a04c..1093816 100644
--- a/macosx/tkMacOSXSubwindows.c
+++ b/macosx/tkMacOSXSubwindows.c
@@ -303,12 +303,41 @@ XUnmapWindow(
display->request++;
if (Tk_IsTopLevel(winPtr)) {
if (!Tk_IsEmbedded(winPtr) &&
- winPtr->wmInfoPtr->hints.initial_state!=IconicState) {
- [win orderOut:nil];
+ winPtr->wmInfoPtr->hints.initial_state!=IconicState) {
[win setExcludedFromWindowsMenu:YES];
+ [win orderOut:NSApp];
+ if ([win isKeyWindow]) {
+
+ /*
+ * If we are unmapping the key window then we need to make sure
+ * that a new key window is assigned, if possible. This is
+ * supposed to happen when a key window is ordered out, but as
+ * noted in tkMacOSXWm.c this does not happen, in spite of
+ * Apple's claims to the contrary.
+ */
+
+ for (NSWindow *w in [NSApp orderedWindows]) {
+ TkWindow *winPtr2 = TkMacOSXGetTkWindow(w);
+ WmInfo *wmInfoPtr;
+
+ BOOL isOnScreen;
+
+ if (!winPtr2 || !winPtr2->wmInfoPtr) {
+ continue;
+ }
+ wmInfoPtr = winPtr2->wmInfoPtr;
+ isOnScreen = (wmInfoPtr->hints.initial_state != IconicState &&
+ wmInfoPtr->hints.initial_state != WithdrawnState);
+ if (w != win && isOnScreen && [w canBecomeKeyWindow]) {
+ [w makeKeyAndOrderFront:NSApp];
+ break;
+ }
+ }
+ }
}
TkMacOSXInvalClipRgns((Tk_Window)winPtr);
} else {
+
/*
* Rebuild the visRgn clip region for the parent so it will be allowed
* to draw in the space from which this subwindow was removed and then
diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c
index 0075fb8..5d90716 100644
--- a/macosx/tkMacOSXWindowEvent.c
+++ b/macosx/tkMacOSXWindowEvent.c
@@ -162,6 +162,9 @@ extern NSString *NSWindowDidOrderOffScreenNotification;
#ifdef TK_MAC_DEBUG_NOTIFICATIONS
TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification);
#endif
+ if (![[notification object] respondsToSelector: @selector (tkLayoutChanged)]) {
+ return;
+ }
[(TKWindow *)[notification object] tkLayoutChanged];
}
@@ -170,6 +173,9 @@ extern NSString *NSWindowDidOrderOffScreenNotification;
#ifdef TK_MAC_DEBUG_NOTIFICATIONS
TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification);
#endif
+ if (![[notification object] respondsToSelector: @selector (tkLayoutChanged)]) {
+ return;
+ }
[(TKWindow *)[notification object] tkLayoutChanged];
}
@@ -182,6 +188,7 @@ extern NSString *NSWindowDidOrderOffScreenNotification;
TkWindow *winPtr = TkMacOSXGetTkWindow(w);
if (winPtr) {
+ winPtr->wmInfoPtr->hints.initial_state = IconicState;
Tk_UnmapWindow((Tk_Window)winPtr);
}
}
@@ -291,6 +298,16 @@ extern NSString *NSWindowDidOrderOffScreenNotification;
}
@end
+
+/*
+ * Idle task which forces focus to a particular window.
+ */
+
+static void RefocusGrabWindow(void *data) {
+ TkWindow *winPtr = (TkWindow *) data;
+ TkpChangeFocus(winPtr, 1);
+}
+
#pragma mark TKApplication(TKApplicationEvent)
@implementation TKApplication(TKApplicationEvent)
@@ -308,6 +325,10 @@ extern NSString *NSWindowDidOrderOffScreenNotification;
* When the application is activated with Command-Tab it will create a
* zombie window for every Tk window which has been withdrawn. So iterate
* through the list of windows and order out any withdrawn window.
+ * If one of the windows is the grab window for its display we focus
+ * it. This is done as at idle, in case the app was reactivated by
+ * clicking a different window. In that case we need to wait until the
+ * mouse event has been processed before focusing the grab window.
*/
for (NSWindow *win in [NSApp windows]) {
@@ -316,7 +337,12 @@ extern NSString *NSWindowDidOrderOffScreenNotification;
continue;
}
if (winPtr->wmInfoPtr->hints.initial_state == WithdrawnState) {
- [win orderOut:nil];
+ [win orderOut:NSApp];
+ }
+ if (winPtr->dispPtr->grabWinPtr == winPtr) {
+ Tcl_DoWhenIdle(RefocusGrabWindow, winPtr);
+ } else {
+ [[self keyWindow] orderFront: self];
}
}
}
@@ -915,11 +941,59 @@ ConfigureRestrictProc(
@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;
+ }
+ return self;
+}
+
+/*
+ * We will just use drawRect.
+ */
+
+- (BOOL) wantsUpdateLayer
+{
+ return NO;
+}
+
+- (void) viewDidChangeBackingProperties
+{
+
+ /*
+ * Make sure that the layer uses a contentScale that matches the
+ * backing scale factor of the screen. This avoids blurry text whe
+ * the view is on a Retina display, as well as incorrect size when
+ * the view is on a normal display.
+ */
+
+ self.layer.contentsScale = self.window.screen.backingScaleFactor;
+}
+
- (void) addTkDirtyRect: (NSRect) rect
{
_tkNeedsDisplay = YES;
_tkDirtyRect = NSUnionRect(_tkDirtyRect, rect);
[NSApp setNeedsToDraw:YES];
+ [self setNeedsDisplay:YES];
+ [[self layer] setNeedsDisplay];
}
- (void) clearTkDirtyRect
diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c
index 3c23308..5a39948 100644
--- a/macosx/tkMacOSXWm.c
+++ b/macosx/tkMacOSXWm.c
@@ -1942,6 +1942,8 @@ WmDeiconifyCmd(
}
}
+ [[win contentView] setNeedsDisplay:YES];
+ Tcl_DoWhenIdle(TkMacOSXDrawAllViews, NULL);
return TCL_OK;
}
@@ -2825,7 +2827,7 @@ WmIconwindowCmd(
*/
TkpWmSetState(oldIcon, WithdrawnState);
- [win orderOut:nil];
+ [win orderOut:NSApp];
[win setExcludedFromWindowsMenu:YES];
wmPtr3->iconFor = NULL;
}
@@ -3734,6 +3736,7 @@ WmTransientCmd(
if (TkGetWindowFromObj(interp, tkwin, objv[3], &container) != TCL_OK) {
return TCL_ERROR;
}
+ RemoveTransient(winPtr);
containerPtr = (TkWindow*) container;
while (!Tk_TopWinHierarchy(containerPtr)) {
/*
@@ -5535,12 +5538,15 @@ Tk_MacOSXGetTkWindow(
void *w)
{
Window window = None;
- TkDisplay *dispPtr = TkGetDisplayList();
if ([(NSWindow *)w respondsToSelector: @selector (tkWindow)]) {
window = [(TKWindow *)w tkWindow];
}
- return (window != None ?
- Tk_IdToWindow(dispPtr->display, window) : NULL);
+ if (window) {
+ TkDisplay *dispPtr = TkGetDisplayList();
+ return Tk_IdToWindow(dispPtr->display, window);
+ } else {
+ return NULL;
+ }
}
/*
@@ -6271,6 +6277,7 @@ TkMacOSXMakeRealWindowExist(
Tk_ChangeWindowAttributes((Tk_Window)winPtr, CWOverrideRedirect, &atts);
ApplyContainerOverrideChanges(winPtr, NULL);
}
+ [window display];
}
/*
@@ -6309,8 +6316,7 @@ TkpRedrawWidget(Tk_Window tkwin) {
[view bounds].size.height - tkBounds.bottom,
tkBounds.right - tkBounds.left,
tkBounds.bottom - tkBounds.top);
- [view setTkNeedsDisplay:YES];
- [view setTkDirtyRect:bounds];
+ [view addTkDirtyRect:bounds];
}
}
@@ -6443,6 +6449,19 @@ TkpWmSetState(
macWin = TkMacOSXGetNSWindowForDrawable(winPtr->window);
+ /*
+ * Make sure windows are updated before the state change. As an exception,
+ * do not process idle tasks before withdrawing a window. The purpose of
+ * this is to support the common paradigm of immediately withdrawing the
+ * root window. Processing idle tasks before changing the state causes the
+ * root to briefly flash on the screen, which users of this paradigm find
+ * annoying. Not processing the events does not guarantee that the window
+ * will not appear but makes it more likely.
+ */
+
+ if (state != WithdrawnState) {
+ while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {};
+ }
if (state == WithdrawnState) {
Tk_UnmapWindow((Tk_Window)winPtr);
} else if (state == IconicState) {
@@ -6463,8 +6482,9 @@ TkpWmSetState(
[macWin orderFront:NSApp];
TkMacOSXZoomToplevel(macWin, state == NormalState ? inZoomIn : inZoomOut);
}
+
/*
- * Make sure windows are updated after the state change.
+ * Make sure windows are updated after the state change too.
*/
while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)){}
diff --git a/macosx/ttkMacOSXTheme.c b/macosx/ttkMacOSXTheme.c
index 92c5156..52880ef 100644
--- a/macosx/ttkMacOSXTheme.c
+++ b/macosx/ttkMacOSXTheme.c
@@ -36,10 +36,13 @@
* Macros for handling drawing contexts.
*/
-#define BEGIN_DRAWING(d) { \
- TkMacOSXDrawingContext dc; \
- if (!TkMacOSXSetupDrawingContext((d), NULL, &dc)) {return;}
-#define END_DRAWING \
+#define BEGIN_DRAWING(d) { \
+ TkMacOSXDrawingContext dc; \
+ if (!TkMacOSXSetupDrawingContext((d), NULL, &dc)) { \
+ return; \
+ } \
+
+#define END_DRAWING \
TkMacOSXRestoreDrawingContext(&dc);}
#define HIOrientation kHIThemeOrientationNormal
@@ -60,7 +63,10 @@
*/
static CGFloat darkButtonFace[4] = {
- 112.0 / 255, 113.0 / 255, 115.0 / 255, 1.0
+ 90.0 / 255, 86.0 / 255, 95.0 / 255, 1.0
+};
+static CGFloat darkPressedButtonFace[4] = {
+ 114.0 / 255, 110.0 / 255, 118.0 / 255, 1.0
};
static CGFloat darkPressedBevelFace[4] = {
135.0 / 255, 136.0 / 255, 138.0 / 255, 1.0
@@ -74,6 +80,12 @@ static CGFloat darkDisabledButtonFace[4] = {
static CGFloat darkInactiveSelectedTab[4] = {
159.0 / 255, 160.0 / 255, 161.0 / 255, 1.0
};
+static CGFloat darkSelectedTab[4] = {
+ 97.0 / 255, 94.0 / 255, 102.0 / 255, 1.0
+};
+static CGFloat darkTab[4] = {
+ 44.0 / 255, 41.0 / 255, 50.0 / 255, 1.0
+};
static CGFloat darkFocusRing[4] = {
38.0 / 255, 113.0 / 255, 159.0 / 255, 1.0
};
@@ -717,8 +729,15 @@ static void DrawDarkButton(
bounds = CGRectInset(bounds, 1, 1);
if (kind == kThemePushButton && (state & TTK_STATE_PRESSED)) {
- GradientFillRoundedRectangle(context, bounds, 4,
+ if ([NSApp macOSVersion] < 120000) {
+ GradientFillRoundedRectangle(context, bounds, 4,
pressedPushButtonGradient, 2);
+ } else {
+ faceColor = [NSColor colorWithColorSpace: deviceRGB
+ components: darkPressedButtonFace
+ count: 4];
+ SolidFillRoundedRectangle(context, bounds, 4, faceColor);
+ }
} else if (kind == kThemePushButton &&
(state & TTK_STATE_ALTERNATE) &&
!(state & TTK_STATE_BACKGROUND)) {
@@ -993,6 +1012,7 @@ static void DrawDarkTab(
NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
NSColor *faceColor, *stroke;
CGRect originalBounds = bounds;
+ int OSVersion = [NSApp macOSVersion];
CGContextSetLineWidth(context, 1.0);
CGContextClipToRect(context, bounds);
@@ -1002,13 +1022,14 @@ static void DrawDarkTab(
* clipped off.
*/
- if (!(state & TTK_STATE_FIRST_TAB)) {
- bounds.origin.x -= 10;
- bounds.size.width += 10;
- }
-
- if (!(state & TTK_STATE_LAST_TAB)) {
- bounds.size.width += 10;
+ if (OSVersion < 110000 || !(state & TTK_STATE_SELECTED)) {
+ if (!(state & TTK_STATE_FIRST_TAB)) {
+ bounds.origin.x -= 10;
+ bounds.size.width += 10;
+ }
+ if (!(state & TTK_STATE_LAST_TAB)) {
+ bounds.size.width += 10;
+ }
}
/*
@@ -1020,13 +1041,25 @@ static void DrawDarkTab(
bounds = CGRectInset(bounds, 1, 1);
if (!(state & TTK_STATE_SELECTED)) {
if (state & TTK_STATE_DISABLED) {
- faceColor = [NSColor colorWithColorSpace: deviceRGB
- components: darkDisabledButtonFace
- count: 4];
+ if (OSVersion < 110000) {
+ faceColor = [NSColor colorWithColorSpace: deviceRGB
+ components: darkDisabledButtonFace
+ count: 4];
+ } else {
+ faceColor = [NSColor colorWithColorSpace: deviceRGB
+ components: darkTab
+ count: 4];
+ }
} else {
- faceColor = [NSColor colorWithColorSpace: deviceRGB
- components: darkButtonFace
- count: 4];
+ if (OSVersion < 110000) {
+ faceColor = [NSColor colorWithColorSpace: deviceRGB
+ components: darkButtonFace
+ count: 4];
+ } else {
+ faceColor = [NSColor colorWithColorSpace: deviceRGB
+ components: darkTab
+ count: 4];
+ }
}
SolidFillRoundedRectangle(context, bounds, 4, faceColor);
@@ -1053,21 +1086,34 @@ static void DrawDarkTab(
} else {
/*
- * This is the selected tab; paint it blue. If it is first, cover up
- * the separator line drawn by the second one. (The selected tab is
- * always drawn last.)
+ * This is the selected tab. If it is first, cover up the separator
+ * line drawn by the second one. (The selected tab is always drawn
+ * last.)
*/
if ((state & TTK_STATE_FIRST_TAB) && !(state & TTK_STATE_LAST_TAB)) {
bounds.size.width += 1;
}
if (!(state & TTK_STATE_BACKGROUND)) {
- GradientFillRoundedRectangle(context, bounds, 4,
- darkSelectedGradient, 2);
+ if (OSVersion < 110000) {
+ GradientFillRoundedRectangle(context, bounds, 4,
+ darkSelectedGradient, 2);
+ } else {
+ faceColor = [NSColor colorWithColorSpace: deviceRGB
+ components: darkSelectedTab
+ count: 4];
+ SolidFillRoundedRectangle(context, bounds, 4, faceColor);
+ }
} else {
- faceColor = [NSColor colorWithColorSpace: deviceRGB
- components: darkInactiveSelectedTab
- count: 4];
+ if (OSVersion < 110000) {
+ faceColor = [NSColor colorWithColorSpace: deviceRGB
+ components: darkInactiveSelectedTab
+ count: 4];
+ } else {
+ faceColor = [NSColor colorWithColorSpace: deviceRGB
+ components: darkSelectedTab
+ count: 4];
+ }
SolidFillRoundedRectangle(context, bounds, 4, faceColor);
}
HighlightButtonBorder(context, bounds);