From ad261482e9c9a0b05913b3ce6e5fd5ae98cc7b7e Mon Sep 17 00:00:00 2001 From: Tobias Melcher Date: Tue, 12 May 2026 19:35:40 +0200 Subject: [PATCH 1/2] Add native method to query Mach-O SDK version at runtime The macOS runtime can report incorrect OS versions (10.16 instead of actual version) when the executable's linked SDK is too old. Add OS.getMachOSDKVersion() to extract the SDK version from the LC_BUILD_VERSION load command in the main executable's Mach-O header. This allows SWT to detect the SDK version the running JVM was built against, enabling proper diagnostics for version reporting issues. --- .../Eclipse SWT PI/cocoa/library/os_custom.c | 25 +++++++++++++++++++ .../Eclipse SWT PI/cocoa/library/os_stats.h | 1 + .../org/eclipse/swt/internal/cocoa/OS.java | 20 +++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/library/os_custom.c b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/library/os_custom.c index 99f8bae271d..27bc21a5d39 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/library/os_custom.c +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/library/os_custom.c @@ -15,6 +15,8 @@ #include "swt.h" #include "os_structs.h" #include "os_stats.h" +#include +#include #define OS_NATIVE(func) Java_org_eclipse_swt_internal_cocoa_OS_##func @@ -150,3 +152,26 @@ JNIEXPORT jlong JNICALL OS_NATIVE(beginSheetModalForWindow) } #endif +#ifndef NO_getMachOSDKVersion +JNIEXPORT jint JNICALL OS_NATIVE(getMachOSDKVersion) + (JNIEnv *env, jclass that) +{ + jint rc = 0; + OS_NATIVE_ENTER(env, that, getMachOSDKVersion_FUNC); + const struct mach_header_64 *header = (const struct mach_header_64 *)_NSGetMachExecuteHeader(); + const uint8_t *ptr = (const uint8_t *)header + sizeof(struct mach_header_64); + for (uint32_t i = 0; i < header->ncmds; i++) { + const struct load_command *lc = (const struct load_command *)ptr; + if (lc->cmd == LC_BUILD_VERSION) { + const struct build_version_command *bv = (const struct build_version_command *)ptr; + uint32_t sdk = bv->sdk; + rc = (jint)(((sdk >> 16) & 0xff) << 16) | (((sdk >> 8) & 0xff) << 8) | (sdk & 0xff); + break; + } + ptr += lc->cmdsize; + } + OS_NATIVE_EXIT(env, that, getMachOSDKVersion_FUNC); + return rc; +} +#endif + diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/library/os_stats.h b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/library/os_stats.h index be2c5250070..c5d0e1e8583 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/library/os_stats.h +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/library/os_stats.h @@ -369,6 +369,7 @@ typedef enum { class_1getMethodImplementation_FUNC, class_1getName_FUNC, class_1getSuperclass_FUNC, + getMachOSDKVersion_FUNC, getpid_FUNC, instrumentObjcMessageSends_FUNC, isFlipped_1CALLBACK_FUNC, diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/OS.java b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/OS.java index cd24777ad62..eba95a9672b 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/OS.java +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/OS.java @@ -38,6 +38,18 @@ public static int VERSION (int major, int minor, int bugfix) { return (major << 16) + (minor << 8) + bugfix; } + public static int VERSION_MAJOR (int version) { + return (version >>> 16) & 0xFF; + } + + public static int VERSION_MINOR (int version) { + return (version >>> 8) & 0xFF; + } + + public static int VERSION_BUGFIX (int version) { + return version & 0xFF; + } + public static final boolean IS_X86_64 = System.getProperty("os.arch").equals("x86_64"); //$NON-NLS-1$ /* @@ -336,6 +348,14 @@ public static boolean isSystemDarkAppearance() { public static final native int getpid(); +/** + * Returns the SDK version from LC_BUILD_VERSION in the main executable's + * Mach-O header, encoded as {@code (major << 16) + (minor << 8) + bugfix}. + * Returns 0 if LC_BUILD_VERSION is not found. + * @method flags=no_gen + */ +public static final native int getMachOSDKVersion(); + public static final native void call(long proc, long id, long sel); /** QuickDraw calls */ From 1e4df2dc26a0db82c29b4797a5cff9f2ccb7230e Mon Sep 17 00:00:00 2001 From: Tobias Melcher Date: Wed, 13 May 2026 19:42:39 +0200 Subject: [PATCH 2/2] [macOS 26] Add layer-based border rendering for Text and Combo widgets On macOS 26 (Tahoe) Liquid Glass, all text fields and combo boxes require a visible border regardless of SWT.BORDER style. This adds the necessary Cocoa bindings (NSView.setWantsLayer, CALayer.setBorderColor/ setBorderWidth/setCornerRadius, NSColor.separatorColor/CGColor) and draws borders via the layer pipeline. The shared logic is extracted into Control.configureLayerBorder() and the SDK version is cached in OS.MACH_O_SDK_VERSION. --- .../org/eclipse/swt/internal/cocoa/CALayer.java | 12 ++++++++++++ .../org/eclipse/swt/internal/cocoa/NSColor.java | 9 +++++++++ .../org/eclipse/swt/internal/cocoa/NSView.java | 4 ++++ .../cocoa/org/eclipse/swt/internal/cocoa/OS.java | 6 ++++++ .../org/eclipse/swt/internal/cocoa/Selector.java | 5 +++++ .../cocoa/org/eclipse/swt/widgets/Combo.java | 6 ++++++ .../cocoa/org/eclipse/swt/widgets/Control.java | 13 +++++++++++++ .../cocoa/org/eclipse/swt/widgets/Text.java | 13 ++++++++++--- 8 files changed, 65 insertions(+), 3 deletions(-) diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/CALayer.java b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/CALayer.java index d2767f63a7e..5344aad3678 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/CALayer.java +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/CALayer.java @@ -27,6 +27,18 @@ public CALayer(id id) { super(id); } +public void setBorderColor(long /*CGColorRef*/ color) { + OS.objc_msgSend(this.id, OS.sel_setBorderColor_, color); +} + +public void setBorderWidth(double width) { + OS.objc_msgSend(this.id, OS.sel_setBorderWidth_, width); +} + +public void setCornerRadius(double cornerRadius) { + OS.objc_msgSend(this.id, OS.sel_setCornerRadius_, cornerRadius); +} + public void setHidden(boolean hidden) { OS.objc_msgSend(this.id, OS.sel_setHidden_, hidden); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/NSColor.java b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/NSColor.java index b74b633bad5..e9df7a77353 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/NSColor.java +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/NSColor.java @@ -27,6 +27,10 @@ public NSColor(id id) { super(id); } +public long /*CGColorRef*/ CGColor() { + return OS.objc_msgSend(this.id, OS.sel_CGColor); +} + public double alphaComponent() { return OS.objc_msgSend_fpret(this.id, OS.sel_alphaComponent); } @@ -151,6 +155,11 @@ public static NSColor selectedTextColor() { return result != 0 ? new NSColor(result) : null; } +public static NSColor separatorColor() { + long result = OS.objc_msgSend(OS.class_NSColor, OS.sel_separatorColor); + return result != 0 ? new NSColor(result) : null; +} + public void set() { OS.objc_msgSend(this.id, OS.sel_set); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/NSView.java b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/NSView.java index 02a5319e7f3..b9f6c8c0110 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/NSView.java +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/NSView.java @@ -276,6 +276,10 @@ public void setToolTip(NSString toolTip) { OS.objc_msgSend(this.id, OS.sel_setToolTip_, toolTip != null ? toolTip.id : 0); } +public void setWantsLayer(boolean wantsLayer) { + OS.objc_msgSend(this.id, OS.sel_setWantsLayer_, wantsLayer); +} + public void setWantsRestingTouches(boolean wantsRestingTouches) { OS.objc_msgSend(this.id, OS.sel_setWantsRestingTouches_, wantsRestingTouches); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/OS.java b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/OS.java index eba95a9672b..0bb3610e376 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/OS.java +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/OS.java @@ -355,6 +355,7 @@ public static boolean isSystemDarkAppearance() { * @method flags=no_gen */ public static final native int getMachOSDKVersion(); +public static final int MACH_O_SDK_VERSION = getMachOSDKVersion(); public static final native void call(long proc, long id, long sel); @@ -843,6 +844,7 @@ public static void registerSelector (Long value, Selector selector) { public static Selector getSelector (long value) { return SELECTORS.get(value); } +public static final long sel_CGColor = Selector.sel_CGColor.value; public static final long sel_CGEvent = Selector.sel_CGEvent.value; public static final long sel_DOMDocument = Selector.sel_DOMDocument.value; public static final long sel_IBeamCursor = Selector.sel_IBeamCursor.value; @@ -1673,6 +1675,7 @@ public static Selector getSelector (long value) { public static final long sel_sendAction_to_from_ = Selector.sel_sendAction_to_from_.value; public static final long sel_sendEvent_ = Selector.sel_sendEvent_.value; public static final long sel_sender = Selector.sel_sender.value; +public static final long sel_separatorColor = Selector.sel_separatorColor.value; public static final long sel_separatorItem = Selector.sel_separatorItem.value; public static final long sel_set = Selector.sel_set.value; public static final long sel_setAcceptsMouseMovedEvents_ = Selector.sel_setAcceptsMouseMovedEvents_.value; @@ -1715,6 +1718,7 @@ public static Selector getSelector (long value) { public static final long sel_setBecomesKeyOnlyIfNeeded_ = Selector.sel_setBecomesKeyOnlyIfNeeded_.value; public static final long sel_setBezelStyle_ = Selector.sel_setBezelStyle_.value; public static final long sel_setBezeled_ = Selector.sel_setBezeled_.value; +public static final long sel_setBorderColor_ = Selector.sel_setBorderColor_.value; public static final long sel_setBorderType_ = Selector.sel_setBorderType_.value; public static final long sel_setBorderWidth_ = Selector.sel_setBorderWidth_.value; public static final long sel_setBordered_ = Selector.sel_setBordered_.value; @@ -1739,6 +1743,7 @@ public static Selector getSelector (long value) { public static final long sel_setContentView_ = Selector.sel_setContentView_.value; public static final long sel_setContentViewMargins_ = Selector.sel_setContentViewMargins_.value; public static final long sel_setControlSize_ = Selector.sel_setControlSize_.value; +public static final long sel_setCornerRadius_ = Selector.sel_setCornerRadius_.value; public static final long sel_setCookie_ = Selector.sel_setCookie_.value; public static final long sel_setCopiesOnScroll_ = Selector.sel_setCopiesOnScroll_.value; public static final long sel_setCurrentContext_ = Selector.sel_setCurrentContext_.value; @@ -1935,6 +1940,7 @@ public static Selector getSelector (long value) { public static final long sel_setVerticalScroller_ = Selector.sel_setVerticalScroller_.value; public static final long sel_setView_ = Selector.sel_setView_.value; public static final long sel_setVisible_ = Selector.sel_setVisible_.value; +public static final long sel_setWantsLayer_ = Selector.sel_setWantsLayer_.value; public static final long sel_setWantsRestingTouches_ = Selector.sel_setWantsRestingTouches_.value; public static final long sel_setWidth_ = Selector.sel_setWidth_.value; public static final long sel_setWidthTracksTextView_ = Selector.sel_setWidthTracksTextView_.value; diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/Selector.java b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/Selector.java index 589d736607e..5d43256c9b9 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/Selector.java +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/Selector.java @@ -89,6 +89,7 @@ public enum Selector { /** This section is auto generated */ + , sel_CGColor("CGColor") , sel_CGEvent("CGEvent") , sel_DOMDocument("DOMDocument") , sel_IBeamCursor("IBeamCursor") @@ -919,6 +920,7 @@ public enum Selector { , sel_sendAction_to_from_("sendAction:to:from:") , sel_sendEvent_("sendEvent:") , sel_sender("sender") + , sel_separatorColor("separatorColor") , sel_separatorItem("separatorItem") , sel_set("set") , sel_setAcceptsMouseMovedEvents_("setAcceptsMouseMovedEvents:") @@ -961,6 +963,7 @@ public enum Selector { , sel_setBecomesKeyOnlyIfNeeded_("setBecomesKeyOnlyIfNeeded:") , sel_setBezelStyle_("setBezelStyle:") , sel_setBezeled_("setBezeled:") + , sel_setBorderColor_("setBorderColor:") , sel_setBorderType_("setBorderType:") , sel_setBorderWidth_("setBorderWidth:") , sel_setBordered_("setBordered:") @@ -985,6 +988,7 @@ public enum Selector { , sel_setContentView_("setContentView:") , sel_setContentViewMargins_("setContentViewMargins:") , sel_setControlSize_("setControlSize:") + , sel_setCornerRadius_("setCornerRadius:") , sel_setCookie_("setCookie:") , sel_setCopiesOnScroll_("setCopiesOnScroll:") , sel_setCurrentContext_("setCurrentContext:") @@ -1181,6 +1185,7 @@ public enum Selector { , sel_setVerticalScroller_("setVerticalScroller:") , sel_setView_("setView:") , sel_setVisible_("setVisible:") + , sel_setWantsLayer_("setWantsLayer:") , sel_setWantsRestingTouches_("setWantsRestingTouches:") , sel_setWidth_("setWidth:") , sel_setWidthTracksTextView_("setWidthTracksTextView:") diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Combo.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Combo.java index 0597becb231..630870b8f7c 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Combo.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Combo.java @@ -510,6 +510,12 @@ void createHandle () { if (cell != null) { cell.setUsesSingleLineMode(true); } + if (OS.VERSION_MAJOR(OS.MACH_O_SDK_VERSION) == 26) { + // macOS 26 (Tahoe) Liquid Glass requires a visible border on all combo boxes, + // so we draw one via the layer. + widget.setBordered(false); + configureLayerBorder(widget); + } view = widget; } } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Control.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Control.java index 1e423a6a52e..a7ab8653a14 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Control.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Control.java @@ -3631,6 +3631,19 @@ void setBackgroundImage (NSImage image) { void setBackgroundColor (NSColor nsColor) { } +void configureLayerBorder(NSView nsView) { + nsView.setWantsLayer(true); + CALayer layer = nsView.layer(); + if (layer != null) { + NSColor separatorColor = NSColor.separatorColor(); + if (separatorColor != null) { + layer.setBorderColor(separatorColor.CGColor()); + } + layer.setBorderWidth(1.0); + layer.setCornerRadius(4.0); + } +} + /** * Sets the receiver's size and location in points to the rectangular * area specified by the arguments. The x and diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Text.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Text.java index ee965adcccd..f48a3e0d161 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Text.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Text.java @@ -515,9 +515,16 @@ void createHandle () { widget.init (); widget.setSelectable (true); widget.setEditable((style & SWT.READ_ONLY) == 0); - if ((style & SWT.BORDER) == 0) { - widget.setFocusRingType (OS.NSFocusRingTypeNone); - widget.setBordered (false); + if (OS.VERSION_MAJOR(OS.MACH_O_SDK_VERSION) == 26) { + // macOS 26 (Tahoe) Liquid Glass requires a visible border on all text fields + // regardless of SWT.BORDER style, so we draw one via the layer. + widget.setBordered(false); + configureLayerBorder(widget); + } else { + if ((style & SWT.BORDER) == 0) { + widget.setFocusRingType (OS.NSFocusRingTypeNone); + widget.setBordered (false); + } } /* * Bug in Cocoa: On OSX 10.10, setting the alignment on the search field