Skip to content

Commit

Permalink
Merge remote-tracking branch 'litwak913/v4.x-cross' into v4.x-cross
Browse files Browse the repository at this point in the history
  • Loading branch information
isHarryh committed Jan 23, 2025
2 parents c2e2150 + 404f7c8 commit 2131b9c
Show file tree
Hide file tree
Showing 5 changed files with 335 additions and 10 deletions.
Binary file modified assets/icons/icon.icns
Binary file not shown.
295 changes: 295 additions & 0 deletions core/src/cn/harryh/arkpets/platform/QuartzHWndCtrl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
package cn.harryh.arkpets.platform;

import cn.harryh.arkpets.utils.Logger;
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 java.util.ArrayList;
import java.util.List;
import java.util.Objects;


public class QuartzHWndCtrl extends HWndCtrl {
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 long windowID;
private Pointer nsWin;
private long layer;
// 0:Uncheck 1:Checked,Available -1:Checked,Unavailable
private byte nsWinUnavailable;

public QuartzHWndCtrl(CFDictionaryRef dict) {
super(getWindowName(dict.getValue(kCGWindowOwnerName), dict.getValue(kCGWindowName)), getWindowRect(dict.getValue(kCGWindowBounds)));
windowID = new CFNumberRef(dict.getValue(kCGWindowNumber)).longValue();
layer = new CFNumberRef(dict.getValue(kCGWindowLayer)).longValue();
}

@Override
public boolean isForeground() {
//todo
return true;
}

@Override
public boolean isVisible() {
return false;
}

@Override
public boolean close(int timeout) {
//todo
return true;
}

@Override
public HWndCtrl updated() {
//todo
/*QuartzHWndCtrl hwnd = null;
CFIndex index = new CFIndex(1);
LongByReference wid = new LongByReference(windowID);
CFArrayRef arr = CFExt.INSTANCE.CFArrayCreate(null,new Pointer[] {wid.getPointer()},index,null);
if (arr != null) {
CFArrayRef win = CoreGraphics.INSTANCE.CGWindowListCreateDescriptionFromArray(arr);
arr.release();
CFDictionaryRef dict = new CFDictionaryRef(win.getValueAtIndex(0));
hwnd=new QuartzHWndCtrl(dict);
win.release();
}*/
return new NullHWndCtrl();
}

@Override
public void setForeground() {
getNSWindow(windowID);
GoldenGlow.INSTANCE.APActive(nsWin);
}

@Override
public void setWindowPosition(HWndCtrl insertAfter, int x, int y, int w, int h) {
getNSWindow(windowID);
GoldenGlow.INSTANCE.APResizeOnMain(nsWin, x, y, w, h);
}

@Override
public void setTaskbar(boolean enable) {
checkNSApp();
GoldenGlow.INSTANCE.APSetDockOnMain(nsapp, enable);
}

@Override
public void setLayered(boolean enable) {
// not necessary in macOS.
}

@Override
public void setTopmost(boolean enable) {
getNSWindow(windowID);
GoldenGlow.INSTANCE.APSetTopmostOnMain(nsWin, enable);
}

@Override
public void setTransparent(boolean enable) {
// not necessary in macOS.
}

@Override
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.");
} else {
CoreFoundation.INSTANCE.CFRelease(server);
}
kCGWindowNumber = CFStringRef.createCFString("kCGWindowNumber");
kCGWindowBounds = CFStringRef.createCFString("kCGWindowBounds");
kCGWindowLayer = CFStringRef.createCFString("kCGWindowLayer");
kCGWindowName = CFStringRef.createCFString("kCGWindowName");
kCGWindowOwnerName = CFStringRef.createCFString("kCGWindowOwnerName");
}

protected static void free() {
kCGWindowNumber.release();
kCGWindowLayer.release();
kCGWindowName.release();
kCGWindowBounds.release();
kCGWindowOwnerName.release();
}

protected static List<QuartzHWndCtrl> getWindowList(boolean onlyVisible) {
ArrayList<QuartzHWndCtrl> list = new ArrayList<>();
//todo
int opt;
if (onlyVisible) {
opt = kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements;
} else {
opt = kCGWindowListExcludeDesktopElements;
}
CFArrayRef windows = CoreGraphics.INSTANCE.CGWindowListCopyWindowInfo(opt, 0);
int numWindows = windows.getCount();
for (int i = 0; i < numWindows; i++) {
Pointer result = windows.getValueAtIndex(i);
CFDictionaryRef windowRef = new CFDictionaryRef(result);
QuartzHWndCtrl win = new QuartzHWndCtrl(windowRef);
if (!onlyVisible || (win.layer >= 0 && win.layer != 20)) {
list.add(win);
}
}
windows.release();
return list;
}

protected static QuartzHWndCtrl find(String className, String windowText) {
CFArrayRef windows = CoreGraphics.INSTANCE.CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, 0);
int numWindows = windows.getCount();
QuartzHWndCtrl win = null;
for (int i = 0; i < numWindows; i++) {
Pointer result = windows.getValueAtIndex(i);
CFDictionaryRef windowRef = new CFDictionaryRef(result);
String cname = getWindowName(windowRef.getValue(kCGWindowOwnerName));
String wname = getWindowName(windowRef.getValue(kCGWindowName));
if (className == null) {
if (wname.equals(windowText) || cname.equals(windowText)) {
win = new QuartzHWndCtrl(windowRef);
break;
}
} else {
if (cname.equals(className) && wname.equals(windowText)) {
win = new QuartzHWndCtrl(windowRef);
break;
}
}
}
windows.release();
return win;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

QuartzHWndCtrl hWndCtrl = (QuartzHWndCtrl) o;
return windowID == hWndCtrl.windowID;
}

@Override
public int hashCode() {
return Objects.hash(windowID);
}

private void checkNSApp() {
if (nsapp == null) {
nsapp = GoldenGlow.INSTANCE.APGetApp();
}
}

private void getNSWindow(long CGWindowId) {
checkNSApp();
if (nsWinUnavailable == 0) {
Pointer nswin = GoldenGlow.INSTANCE.APGetNSWindow(nsapp, CGWindowId);
if (nswin == null) {
nsWinUnavailable = -1;
}
this.nsWin = nswin;
nsWinUnavailable = 1;
}
}

private static String getWindowName(Pointer value) {
return value == null ? "" : new CFStringRef(value).stringValue();
}

private static String getWindowName(Pointer own, Pointer title) {
String ownName;
String titleName;
ownName = own == null ? "" : new CFStringRef(own).stringValue();
titleName = title == null ? "" : new CFStringRef(title).stringValue();
if (titleName.isEmpty()) return ownName;
return titleName;
}

private static WindowRect getWindowRect(Pointer value) {
if (value != null) {
CGRect.ByReference rect = new CGRect.ByReference();
boolean success = CoreGraphics.INSTANCE.CGRectMakeWithDictionaryRepresentation(new CFDictionaryRef(value), rect);
if (success) {
return new WindowRect(
(int) Math.round(rect.origin.y),
(int) Math.round(rect.origin.y + rect.size.height),
(int) Math.round(rect.origin.x),
(int) Math.round(rect.origin.x + rect.size.width)
);
}
}
return new WindowRect(0, 0, 0, 0);
}

// JNA Definition

private interface CoreGraphics extends Library {
CoreGraphics INSTANCE = Native.load("CoreGraphics", CoreGraphics.class);

CFArrayRef CGWindowListCopyWindowInfo(int option, int relativeToWindow);

CFArrayRef CGWindowListCreateDescriptionFromArray(CFArrayRef windowArray);

boolean CGRectMakeWithDictionaryRepresentation(CFDictionaryRef dict, CGRect.ByReference rect);

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 {
}
}

@Structure.FieldOrder({"x", "y"})
public static class CGPoint extends Structure {
public double x;
public double y;
}

@Structure.FieldOrder({"width", "height"})
public static class CGSize extends Structure {
public double width;
public double height;
}
}
26 changes: 21 additions & 5 deletions core/src/cn/harryh/arkpets/platform/WindowSystem.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,15 @@ public static WindowSystem detectWindowSystem() {
} else if (Platform.isLinux()) {
String desktop = System.getenv("XDG_CURRENT_DESKTOP");
String type = System.getenv("XDG_SESSION_TYPE");
if (desktop.equals("GNOME")) {
return WindowSystem.MUTTER;
} else if (desktop.equals("KDE")) {
return WindowSystem.KWIN;
} else if (type.equals("x11")) {
if (desktop != null && type != null) {
if (desktop.equals("GNOME")) {
return WindowSystem.MUTTER;
} else if (desktop.equals("KDE") && type.equals("wayland")) {
return WindowSystem.KWIN;
} else if (type.equals("x11")) {
return WindowSystem.X11;
}
} else {
return WindowSystem.X11;
}
}
Expand All @@ -59,6 +63,9 @@ public static void init(WindowSystem platform) {
case X11 -> {
X11HWndCtrl.init();
}
case QUARTZ -> {
QuartzHWndCtrl.init();
}
}
}

Expand Down Expand Up @@ -88,6 +95,9 @@ public static HWndCtrl findWindow(String className, String windowText) {
case X11 -> {
return X11HWndCtrl.find(className, windowText);
}
case QUARTZ -> {
return QuartzHWndCtrl.find(className, windowText);
}
default -> {
return NullHWndCtrl.find(className, windowText);
}
Expand All @@ -112,6 +122,9 @@ public static List<? extends HWndCtrl> getWindowList(boolean onlyVisible) {
case X11 -> {
return X11HWndCtrl.getWindowList(onlyVisible);
}
case QUARTZ -> {
return QuartzHWndCtrl.getWindowList(onlyVisible);
}
default -> {
return new ArrayList<>();
}
Expand Down Expand Up @@ -154,6 +167,9 @@ public static void free() {
case X11 -> {
X11HWndCtrl.free();
}
case QUARTZ -> {
QuartzHWndCtrl.free();
}
}
}

Expand Down
Loading

0 comments on commit 2131b9c

Please sign in to comment.