From 46de81c1c9c79eac0c7cc9655ad9c757ad333ef8 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Thu, 14 Dec 2023 14:39:26 +0100 Subject: [PATCH] macOS native: - removed `FlatNativeMacLibrary.getWindowPtr()` because it is too dangerous to use `windowPtr` (which is `NSWindow*`) in Java (using invalid window pointer would crash app) - made `getNSWindow()` 20x faster - catch exceptions in `getNSWindow()` - digitally signed dylibs --- .../flatlaf/ui/FlatNativeMacLibrary.java | 33 +++++----- .../formdev/flatlaf/ui/FlatPopupFactory.java | 12 ++-- .../natives/libflatlaf-macos-arm64.dylib | Bin 53851 -> 72640 bytes .../natives/libflatlaf-macos-x86_64.dylib | Bin 35760 -> 55408 bytes .../flatlaf-natives-macos/build.gradle.kts | 11 ++-- .../src/main/headers/JNIUtils.h | 7 +- ..._formdev_flatlaf_ui_FlatNativeMacLibrary.h | 14 +--- .../src/main/objcpp/JNIUtils.mm | 44 +++++++++++++ .../src/main/objcpp/MacWindow.mm | 61 +++++++++--------- 9 files changed, 108 insertions(+), 74 deletions(-) mode change 100644 => 100755 flatlaf-core/src/main/resources/com/formdev/flatlaf/natives/libflatlaf-macos-arm64.dylib mode change 100644 => 100755 flatlaf-core/src/main/resources/com/formdev/flatlaf/natives/libflatlaf-macos-x86_64.dylib create mode 100644 flatlaf-natives/flatlaf-natives-macos/src/main/objcpp/JNIUtils.mm diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeMacLibrary.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeMacLibrary.java index 2a1b24c3..3799bfd9 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeMacLibrary.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeMacLibrary.java @@ -23,6 +23,20 @@ import java.awt.Window; *

* Note: This is private API. Do not use! * + *

Methods that use windows as parameter

+ * + * For all methods that accept a {@link java.awt.Window} as parameter, + * the underlying macOS window must be already created, + * otherwise the method fails. You can use following to ensure this: + *
{@code
+ * if( !window.isDisplayable() )
+ *     window.addNotify();
+ * }
+ * or invoke the method after packing the window. E.g. + *
{@code
+ * window.pack();
+ * }
+ * * @author Karl Tauber * @since 3.3 */ @@ -38,22 +52,5 @@ public class FlatNativeMacLibrary return FlatNativeLibrary.isLoaded(); } - /** - * Gets the macOS window pointer (NSWindow) for the given Swing window. - *

- * Note that the underlying macOS window must be already created, - * otherwise this method returns zero. Use following to ensure this: - *

{@code
-	 * if( !window.isDisplayable() )
-	 *     window.addNotify();
-	 * }
- * or invoke this method after packing the window. E.g. - *
{@code
-	 * window.pack();
-	 * long windowPtr = getWindowPtr( window );
-	 * }
- */ - public native static long getWindowPtr( Window window ); - - public native static void setWindowRoundedBorder( long windowPtr, float radius, float borderWidth, int borderColor ); + public native static boolean setWindowRoundedBorder( Window window, float radius, float borderWidth, int borderColor ); } diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupFactory.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupFactory.java index 8aa636f0..9039c28d 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupFactory.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupFactory.java @@ -324,13 +324,6 @@ public class FlatPopupFactory if( !popupWindow.isDisplayable() ) popupWindow.addNotify(); - // get native window handle/pointer - long hwnd = SystemInfo.isWindows - ? FlatNativeWindowsLibrary.getHWND( popupWindow ) - : FlatNativeMacLibrary.getWindowPtr( popupWindow ); - if( hwnd == 0 ) - return; - int borderCornerRadius = getBorderCornerRadius( owner, contents ); float borderWidth = getRoundedBorderWidth( owner, contents ); @@ -353,6 +346,9 @@ public class FlatPopupFactory } if( SystemInfo.isWindows ) { + // get native window handle + long hwnd = FlatNativeWindowsLibrary.getHWND( popupWindow ); + // set corner preference int cornerPreference = (borderCornerRadius <= 4) ? FlatNativeWindowsLibrary.DWMWCP_ROUNDSMALL // 4px @@ -366,7 +362,7 @@ public class FlatPopupFactory borderWidth = 0; // set corner radius, border width and color - FlatNativeMacLibrary.setWindowRoundedBorder( hwnd, borderCornerRadius, + FlatNativeMacLibrary.setWindowRoundedBorder( popupWindow, borderCornerRadius, borderWidth, (borderColor != null) ? borderColor.getRGB() : 0 ); } } diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/natives/libflatlaf-macos-arm64.dylib b/flatlaf-core/src/main/resources/com/formdev/flatlaf/natives/libflatlaf-macos-arm64.dylib old mode 100644 new mode 100755 index 6094c83157f7f6a08b77455b42c1dbf53bacf74f..e29df3b1766ed568b08d1bd2d7a94d97effcf663 GIT binary patch literal 72640 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 literal 53851 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 G1pXhaxk|eL literal 35760 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 UsO)fGtl>_xQ-oKe@N*{nKR0sx+W-In diff --git a/flatlaf-natives/flatlaf-natives-macos/build.gradle.kts b/flatlaf-natives/flatlaf-natives-macos/build.gradle.kts index b6b24745..322b1998 100644 --- a/flatlaf-natives/flatlaf-natives-macos/build.gradle.kts +++ b/flatlaf-natives/flatlaf-natives-macos/build.gradle.kts @@ -81,7 +81,7 @@ tasks { val nativesDir = project( ":flatlaf-core" ).projectDir.resolve( "src/main/resources/com/formdev/flatlaf/natives" ) val isARM64 = name.contains( "Arm64" ) val minOs = if( isARM64 ) minOsARM64 else minOsX86_64 - val libraryName = if( isARM64 ) "flatlaf-macos-arm64.dylib" else "flatlaf-macos-x86_64.dylib" + val libraryName = if( isARM64 ) "libflatlaf-macos-arm64.dylib" else "libflatlaf-macos-x86_64.dylib" linkerArgs.addAll( toolChain.map { when( it ) { @@ -91,14 +91,17 @@ tasks { } ) doLast { + // sign shared library +// exec { commandLine( "codesign", "-s", "FormDev Software GmbH", "--timestamp", "${linkedFile.asFile.get()}" ) } + // copy shared library to flatlaf-core resources copy { from( linkedFile ) into( nativesDir ) - rename( "flatlaf-natives-macos.dylib", libraryName ) + rename( "libflatlaf-natives-macos.dylib", libraryName ) } -///*dump +/*dump val dylib = linkedFile.asFile.get() val dylibDir = dylib.parent exec { commandLine( "size", dylib ) } @@ -123,7 +126,7 @@ tasks { } exec { commandLine( "objdump", "--disassemble-all", dylib ); standardOutput = FileOutputStream( "$dylibDir/disassemble.txt" ) } exec { commandLine( "objdump", "--full-contents", dylib ); standardOutput = FileOutputStream( "$dylibDir/full-contents.txt" ) } -//dump*/ +dump*/ } } } diff --git a/flatlaf-natives/flatlaf-natives-macos/src/main/headers/JNIUtils.h b/flatlaf-natives/flatlaf-natives-macos/src/main/headers/JNIUtils.h index 8e5be892..2de80956 100644 --- a/flatlaf-natives/flatlaf-natives-macos/src/main/headers/JNIUtils.h +++ b/flatlaf-natives/flatlaf-natives-macos/src/main/headers/JNIUtils.h @@ -23,9 +23,9 @@ */ -// from JNFJNI.h +// from jlong_md.h #ifndef jlong_to_ptr -#define jlong_to_ptr(a) ((void *)(uintptr_t)(a)) + #define jlong_to_ptr(a) ((void*)(a)) #endif @@ -39,3 +39,6 @@ [ex name], [ex reason], [ex userInfo], [ex callStackSymbols] ); \ } \ } + + +jfieldID getFieldID( JNIEnv *env, const char* className, const char* fieldName, const char* fieldSignature ); diff --git a/flatlaf-natives/flatlaf-natives-macos/src/main/headers/com_formdev_flatlaf_ui_FlatNativeMacLibrary.h b/flatlaf-natives/flatlaf-natives-macos/src/main/headers/com_formdev_flatlaf_ui_FlatNativeMacLibrary.h index f4d55eed..f5bcd35b 100644 --- a/flatlaf-natives/flatlaf-natives-macos/src/main/headers/com_formdev_flatlaf_ui_FlatNativeMacLibrary.h +++ b/flatlaf-natives/flatlaf-natives-macos/src/main/headers/com_formdev_flatlaf_ui_FlatNativeMacLibrary.h @@ -7,21 +7,13 @@ #ifdef __cplusplus extern "C" { #endif -/* - * Class: com_formdev_flatlaf_ui_FlatNativeMacLibrary - * Method: getWindowPtr - * Signature: (Ljava/awt/Window;)J - */ -JNIEXPORT jlong JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_getWindowPtr - (JNIEnv *, jclass, jobject); - /* * Class: com_formdev_flatlaf_ui_FlatNativeMacLibrary * Method: setWindowRoundedBorder - * Signature: (JFFI)V + * Signature: (Ljava/awt/Window;FFI)Z */ -JNIEXPORT void JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowRoundedBorder - (JNIEnv *, jclass, jlong, jfloat, jfloat, jint); +JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowRoundedBorder + (JNIEnv *, jclass, jobject, jfloat, jfloat, jint); #ifdef __cplusplus } diff --git a/flatlaf-natives/flatlaf-natives-macos/src/main/objcpp/JNIUtils.mm b/flatlaf-natives/flatlaf-natives-macos/src/main/objcpp/JNIUtils.mm new file mode 100644 index 00000000..7c4e798d --- /dev/null +++ b/flatlaf-natives/flatlaf-natives-macos/src/main/objcpp/JNIUtils.mm @@ -0,0 +1,44 @@ +/* + * Copyright 2023 FormDev Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import +#import "JNIUtils.h" + +/** + * @author Karl Tauber + */ + +jfieldID getFieldID( JNIEnv *env, const char* className, const char* fieldName, const char* fieldSignature ) { +// NSLog( @"getFieldID %s %s %s", className, fieldName, fieldSignature ); + + jclass cls = env->FindClass( className ); + if( cls == NULL ) { + NSLog( @"FlatLaf: failed to lookup class '%s'", className ); + env->ExceptionDescribe(); // print stack trace + env->ExceptionClear(); + return NULL; + } + + jfieldID fieldID = env->GetFieldID( cls, fieldName, fieldSignature ); + if( fieldID == NULL ) { + NSLog( @"FlatLaf: failed to lookup field '%s' of type '%s' in class '%s'", fieldName, fieldSignature, className ); + env->ExceptionDescribe(); // print stack trace + env->ExceptionClear(); + return NULL; + } + + return fieldID; +} diff --git a/flatlaf-natives/flatlaf-natives-macos/src/main/objcpp/MacWindow.mm b/flatlaf-natives/flatlaf-natives-macos/src/main/objcpp/MacWindow.mm index de0e8876..5d81a51c 100644 --- a/flatlaf-natives/flatlaf-natives-macos/src/main/objcpp/MacWindow.mm +++ b/flatlaf-natives/flatlaf-natives-macos/src/main/objcpp/MacWindow.mm @@ -24,67 +24,66 @@ * @author Karl Tauber */ -extern "C" -JNIEXPORT jlong JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_getWindowPtr - ( JNIEnv* env, jclass cls, jobject window ) -{ +NSWindow* getNSWindow( JNIEnv* env, jclass cls, jobject window ) { if( window == NULL ) return NULL; - - JNI_COCOA_ENTER() + + // initialize field IDs (done only once because fields are static) + static jfieldID peerID = getFieldID( env, "java/awt/Component", "peer", "Ljava/awt/peer/ComponentPeer;" ); + static jfieldID platformWindowID = getFieldID( env, "sun/lwawt/LWWindowPeer", "platformWindow", "Lsun/lwawt/PlatformWindow;" ); + static jfieldID ptrID = getFieldID( env, "sun/lwawt/macosx/CFRetainedResource", "ptr", "J" ); + if( peerID == NULL || platformWindowID == NULL || ptrID == NULL ) + return NULL; // get field java.awt.Component.peer - jfieldID peerID = env->GetFieldID( env->GetObjectClass( window ), "peer", "Ljava/awt/peer/ComponentPeer;" ); - jobject peer = (peerID != NULL) ? env->GetObjectField( window, peerID ) : NULL; + jobject peer = env->GetObjectField( window, peerID ); if( peer == NULL ) return NULL; // get field sun.lwawt.LWWindowPeer.platformWindow - jfieldID platformWindowID = env->GetFieldID( env->GetObjectClass( peer ), "platformWindow", "Lsun/lwawt/PlatformWindow;" ); - jobject platformWindow = (platformWindowID != NULL) ? env->GetObjectField( peer, platformWindowID ) : NULL; + jobject platformWindow = env->GetObjectField( peer, platformWindowID ); if( platformWindow == NULL ) return NULL; // get field sun.lwawt.macosx.CFRetainedResource.ptr - jfieldID ptrID = env->GetFieldID( env->GetObjectClass( platformWindow ), "ptr", "J" ); - return (ptrID != NULL) ? env->GetLongField( platformWindow, ptrID ) : NULL; - - JNI_COCOA_EXIT() + return (NSWindow *) jlong_to_ptr( env->GetLongField( platformWindow, ptrID ) ); } extern "C" -JNIEXPORT void JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowRoundedBorder - ( JNIEnv* env, jclass cls, jlong windowPtr, jfloat radius, jfloat borderWidth, jint borderColor ) +JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowRoundedBorder + ( JNIEnv* env, jclass cls, jobject window, jfloat radius, jfloat borderWidth, jint borderColor ) { - if( windowPtr == 0 ) - return; - JNI_COCOA_ENTER() + NSWindow* nsWindow = getNSWindow( env, cls, window ); + if( nsWindow == NULL ) + return FALSE; + [FlatJNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ - NSWindow* window = (NSWindow *) jlong_to_ptr( windowPtr ); + nsWindow.hasShadow = YES; + nsWindow.contentView.wantsLayer = YES; + nsWindow.contentView.layer.cornerRadius = radius; + nsWindow.contentView.layer.masksToBounds = YES; - window.hasShadow = YES; - window.contentView.wantsLayer = YES; - window.contentView.layer.cornerRadius = radius; - window.contentView.layer.masksToBounds = YES; - - window.contentView.layer.borderWidth = borderWidth; + nsWindow.contentView.layer.borderWidth = borderWidth; if( borderWidth > 0 ) { CGFloat red = ((borderColor >> 16) & 0xff) / 255.; CGFloat green = ((borderColor >> 8) & 0xff) / 255.; CGFloat blue = (borderColor & 0xff) / 255.; CGFloat alpha = ((borderColor >> 24) & 0xff) / 255.; - window.contentView.layer.borderColor = [[NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha] CGColor]; + nsWindow.contentView.layer.borderColor = [[NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha] CGColor]; } - window.backgroundColor = NSColor.clearColor; - window.opaque = NO; + nsWindow.backgroundColor = NSColor.clearColor; + nsWindow.opaque = NO; - [window.contentView.layer removeAllAnimations]; - [window invalidateShadow]; + [nsWindow.contentView.layer removeAllAnimations]; + [nsWindow invalidateShadow]; }]; + return TRUE; + JNI_COCOA_EXIT() + return FALSE; }