diff --git a/core/src/cn/harryh/arkpets/platform/QuartzHWndCtrl.java b/core/src/cn/harryh/arkpets/platform/QuartzHWndCtrl.java index f78c2fa..94261b8 100644 --- a/core/src/cn/harryh/arkpets/platform/QuartzHWndCtrl.java +++ b/core/src/cn/harryh/arkpets/platform/QuartzHWndCtrl.java @@ -1,12 +1,12 @@ package cn.harryh.arkpets.platform; -import cn.harryh.arkpets.utils.Logger; +import cn.harryh.arkpets.utils.ObjCWrapper; import com.sun.jna.*; import com.sun.jna.platform.mac.CoreFoundation; -import com.sun.jna.platform.mac.CoreFoundation.CFNumberRef; -import com.sun.jna.platform.mac.CoreFoundation.CFStringRef; import com.sun.jna.platform.mac.CoreFoundation.CFArrayRef; import com.sun.jna.platform.mac.CoreFoundation.CFDictionaryRef; +import com.sun.jna.platform.mac.CoreFoundation.CFNumberRef; +import com.sun.jna.platform.mac.CoreFoundation.CFStringRef; import java.util.ArrayList; import java.util.List; @@ -14,7 +14,7 @@ public class QuartzHWndCtrl extends HWndCtrl { - private static Pointer nsapp; + private static Pointer nsApp; private static CFStringRef kCGWindowNumber; private static CFStringRef kCGWindowLayer; private static CFStringRef kCGWindowBounds; @@ -22,9 +22,15 @@ public class QuartzHWndCtrl extends HWndCtrl { private static CFStringRef kCGWindowOwnerName; private static final int kCGWindowListExcludeDesktopElements = (1 << 4); private static final int kCGWindowListOptionOnScreenOnly = 1; + private static final int NSStatusWindowLevel = 25; + private static final int NSNormalWindowLevel = 0; + + private boolean trans = true; + private static final boolean isArm = Platform.isARM(); private long windowID; private Pointer nsWin; + private Pointer nsScreen; private long layer; // 0:Uncheck 1:Checked,Available -1:Checked,Unavailable private byte nsWinUnavailable; @@ -72,19 +78,39 @@ public HWndCtrl updated() { @Override public void setForeground() { getNSWindow(windowID); - GoldenGlow.INSTANCE.APActive(nsWin); + ObjCWrapper.msgSend.invokeVoid(new Object[]{ + nsWin, + ObjCWrapper.sel("orderFrontRegardless:") + }); } @Override public void setWindowPosition(HWndCtrl insertAfter, int x, int y, int w, int h) { getNSWindow(windowID); - GoldenGlow.INSTANCE.APResizeOnMain(nsWin, x, y, w, h); + CGRect rect = getScreenSize(); + CGRect.ByValue newRect = new CGRect.ByValue(); + newRect.origin.x = x; + newRect.origin.y = rect.size.height - y - h; + newRect.size.width = w; + newRect.size.height = h; + ObjCWrapper.runOnAppKit(() -> { + ObjCWrapper.msgSend.invokeVoid(new Object[]{ + nsWin, + ObjCWrapper.sel("setFrame:display:animate:"), + newRect, + 1, 0 + }); + }); } @Override public void setTaskbar(boolean enable) { checkNSApp(); - GoldenGlow.INSTANCE.APSetDockOnMain(nsapp, enable); + ObjCWrapper.msgSend.invokeVoid(new Object[]{ + nsApp, + ObjCWrapper.sel("setActivationPolicy:"), + enable ? 0 : 1 + }); } @Override @@ -95,12 +121,25 @@ public void setLayered(boolean enable) { @Override public void setTopmost(boolean enable) { getNSWindow(windowID); - GoldenGlow.INSTANCE.APSetTopmostOnMain(nsWin, enable); + ObjCWrapper.msgSend.invokeVoid(new Object[]{ + nsWin, + ObjCWrapper.sel("setLevel:"), + enable ? NSStatusWindowLevel : NSNormalWindowLevel + }); } @Override public void setTransparent(boolean enable) { - // not necessary in macOS. + if (trans == enable) return; + getNSWindow(windowID); + ObjCWrapper.runOnAppKit(() -> { + ObjCWrapper.msgSend.invokeVoid(new Object[]{ + nsWin, + ObjCWrapper.sel("setIgnoresMouseEvents:"), + enable ? 1 : 0 + }); + }); + trans = enable; } @Override @@ -109,7 +148,6 @@ public void sendMouseEvent(MouseEvent msg, int x, int y) { } protected static void init() { - Logger.info("System", "Objective-C bridge library version " + GoldenGlow.INSTANCE.APVersion()); CFDictionaryRef server = CoreGraphics.INSTANCE.CGSessionCopyCurrentDictionary(); if (server == null) { throw new RuntimeException("No window server connection."); @@ -121,6 +159,7 @@ protected static void init() { kCGWindowLayer = CFStringRef.createCFString("kCGWindowLayer"); kCGWindowName = CFStringRef.createCFString("kCGWindowName"); kCGWindowOwnerName = CFStringRef.createCFString("kCGWindowOwnerName"); + ObjCWrapper.init(); } protected static void free() { @@ -194,15 +233,22 @@ public int hashCode() { } private void checkNSApp() { - if (nsapp == null) { - nsapp = GoldenGlow.INSTANCE.APGetApp(); + if (nsApp == null) { + nsApp = ObjCWrapper.msgSend.invokePointer(new Object[]{ + ObjCWrapper.cls("NSApplication"), + ObjCWrapper.sel("sharedApplication") + }); } } private void getNSWindow(long CGWindowId) { checkNSApp(); if (nsWinUnavailable == 0) { - Pointer nswin = GoldenGlow.INSTANCE.APGetNSWindow(nsapp, CGWindowId); + Pointer nswin = ObjCWrapper.msgSend.invokePointer(new Object[]{ + nsApp, + ObjCWrapper.sel("windowWithWindowNumber:"), + CGWindowId + }); if (nswin == null) { nsWinUnavailable = -1; } @@ -211,6 +257,14 @@ private void getNSWindow(long CGWindowId) { } } + private void getNSScreen() { + if (this.nsWinUnavailable != 1 || this.nsScreen != null) return; + this.nsScreen = ObjCWrapper.msgSend.invokePointer(new Object[]{ + nsWin, + ObjCWrapper.sel("screen") + }); + } + private static String getWindowName(Pointer value) { return value == null ? "" : new CFStringRef(value).stringValue(); } @@ -240,6 +294,24 @@ private static WindowRect getWindowRect(Pointer value) { return new WindowRect(0, 0, 0, 0); } + private CGRect getScreenSize() { + getNSScreen(); + if (isArm) { + return (CGRect.ByValue) ObjCWrapper.msgSend.invoke(CGRect.ByValue.class, new Object[]{ + nsScreen, + ObjCWrapper.sel("frame") + }); + } else { + CGRect.ByReference rect = new CGRect.ByReference(); + ObjCWrapper.msgSend_stret.invokeVoid(new Object[]{ + rect, + nsScreen, + ObjCWrapper.sel("frame") + }); + return rect; + } + } + // JNA Definition private interface CoreGraphics extends Library { @@ -254,24 +326,6 @@ private interface CoreGraphics extends Library { CFDictionaryRef CGSessionCopyCurrentDictionary(); } - private interface GoldenGlow extends Library { - GoldenGlow INSTANCE = Native.load(System.getProperty("user.dir") + "/libgoldenglow.dylib", GoldenGlow.class); - - void APResizeOnMain(Pointer p, int x, int y, int w, int h); - - Pointer APGetApp(); - - void APSetDockOnMain(Pointer app, boolean enable); - - void APSetTopmostOnMain(Pointer win, boolean enable); - - Pointer APGetNSWindow(Pointer app, long cgid); - - void APActive(Pointer win); - - int APVersion(); - } - @Structure.FieldOrder({"origin", "size"}) public static class CGRect extends Structure { public CGPoint origin; @@ -279,6 +333,9 @@ public static class CGRect extends Structure { public static class ByReference extends CGRect implements Structure.ByReference { } + + public static class ByValue extends CGRect implements Structure.ByValue { + } } @Structure.FieldOrder({"x", "y"}) diff --git a/core/src/cn/harryh/arkpets/utils/ObjCWrapper.java b/core/src/cn/harryh/arkpets/utils/ObjCWrapper.java new file mode 100644 index 0000000..e99debf --- /dev/null +++ b/core/src/cn/harryh/arkpets/utils/ObjCWrapper.java @@ -0,0 +1,66 @@ +package cn.harryh.arkpets.utils; + +import com.sun.jna.*; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + + +public class ObjCWrapper { + private static Map selMap = new HashMap<>(); + private static Map clsMap = new HashMap<>(); + public static Function msgSend; + public static Function msgSend_stret; + private static Method lwtOnMain; + + public static void init() { + msgSend = Function.getFunction("objc.A", "objc_msgSend"); + if (Platform.isIntel()) { + msgSend_stret = Function.getFunction("objc.A", "objc_msgSend_stret"); + } + try { + Class lwckit = Class.forName("sun.lwawt.macosx.LWCToolkit"); + lwtOnMain = lwckit.getDeclaredMethod("performOnMainThreadAfterDelay", Runnable.class, long.class); + lwtOnMain.setAccessible(true); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static Pointer cls(String cls) { + Pointer ptr; + ptr = clsMap.get(cls); + if (ptr == null) { + ptr = Runtime.INSTANCE.objc_lookUpClass(cls); + if (ptr != null) clsMap.put(cls, ptr); + } + return ptr; + } + + public static Pointer sel(String sel) { + Pointer ptr; + ptr = selMap.get(sel); + if (ptr == null) { + ptr = Runtime.INSTANCE.sel_getUid(sel); + if (ptr != null) selMap.put(sel, ptr); + } + return ptr; + } + + public static void runOnAppKit(Runnable r) { + try { + lwtOnMain.invoke(null, r, 0); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private interface Runtime extends Library { + Runtime INSTANCE = Native.load("objc.A", Runtime.class); + + Pointer sel_getUid(String msg); + + Pointer objc_lookUpClass(String name); + } +}