Skip to content

Commit

Permalink
refactor(quartz): deprecate bridge library
Browse files Browse the repository at this point in the history
  • Loading branch information
litwak913 committed Feb 16, 2025
1 parent 4c129b7 commit ec2cc80
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 31 deletions.
119 changes: 88 additions & 31 deletions core/src/cn/harryh/arkpets/platform/QuartzHWndCtrl.java
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
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;
import java.util.Objects;


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;
private static CFStringRef kCGWindowName;
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;
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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.");
Expand All @@ -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() {
Expand Down Expand Up @@ -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;
}
Expand All @@ -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();
}
Expand Down Expand Up @@ -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 {
Expand All @@ -254,31 +326,16 @@ 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;
public CGSize size;

public static class ByReference extends CGRect implements Structure.ByReference {
}

public static class ByValue extends CGRect implements Structure.ByValue {
}
}

@Structure.FieldOrder({"x", "y"})
Expand Down
66 changes: 66 additions & 0 deletions core/src/cn/harryh/arkpets/utils/ObjCWrapper.java
Original file line number Diff line number Diff line change
@@ -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<String, Pointer> selMap = new HashMap<>();
private static Map<String, Pointer> 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);
}
}

0 comments on commit ec2cc80

Please sign in to comment.