diff --git a/core/src/cn/harryh/arkpets/ArkPets.java b/core/src/cn/harryh/arkpets/ArkPets.java index 30d68586..aca44f4d 100644 --- a/core/src/cn/harryh/arkpets/ArkPets.java +++ b/core/src/cn/harryh/arkpets/ArkPets.java @@ -10,7 +10,8 @@ import cn.harryh.arkpets.transitions.TransitionFloat; import cn.harryh.arkpets.transitions.TransitionVector2; import cn.harryh.arkpets.tray.MemberTrayImpl; -import cn.harryh.arkpets.utils.HWndCtrl; +import cn.harryh.arkpets.hwnd.HWndCtrl; +import cn.harryh.arkpets.hwnd.HWndCtrlFactory; import cn.harryh.arkpets.utils.Logger; import cn.harryh.arkpets.utils.Plane; import com.badlogic.gdx.ApplicationAdapter; @@ -36,10 +37,10 @@ public class ArkPets extends ApplicationAdapter implements InputProcessor { public TransitionFloat windowAlpha; // Window Opacity Easing public TransitionVector2 windowPosition; // Window Position Easing - private HWndCtrl hWndMine; - private HWndCtrl hWndTopmost; + private HWndCtrl<?> hWndMine; + private HWndCtrl<?> hWndTopmost; private LoopCtrl getHWndLoopCtrl; - private List<HWndCtrl> hWndList; + private List<? extends HWndCtrl<?>> hWndList; private final String APP_TITLE; private final MouseStatus mouseStatus = new MouseStatus(); @@ -95,8 +96,12 @@ public void create() { // 5.Window style setup windowAlpha = new TransitionFloat(TernaryFunction.EASE_OUT_CUBIC, easingDuration); windowAlpha.reset(1f); - hWndMine = new HWndCtrl(null, APP_TITLE); - hWndMine.setWindowExStyle(HWndCtrl.WS_EX_LAYERED | (config.window_style_topmost ? HWndCtrl.WS_EX_TOPMOST : 0)); + hWndMine = HWndCtrlFactory.find(null, APP_TITLE); + hWndMine.setLayered(true); + if(config.window_style_topmost){ + hWndMine.setTopMost(true); + } + //hWndMine.setWindowExStyle(HWndCtrl.WS_EX_LAYERED | (config.window_style_topmost ? HWndCtrl.WS_EX_TOPMOST : 0)); promiseToolwindowStyle(1000); // 6.Tray icon setup @@ -155,6 +160,7 @@ public void resize(int x, int y) { @Override public void dispose() { Logger.info("App", "Dispose"); + HWndCtrlFactory.free(); } /* INTERFACES */ @@ -194,10 +200,10 @@ public boolean touchDown(int screenX, int screenY, int pointer, int button) { RelativeWindowPosition rwp = getRelativeWindowPositionAt(screenX, screenY); if (rwp != null) rwp.sendMouseEvent(switch (button) { - case Input.Buttons.LEFT -> HWndCtrl.WM_LBUTTONDOWN; - case Input.Buttons.RIGHT -> HWndCtrl.WM_RBUTTONDOWN; - case Input.Buttons.MIDDLE -> HWndCtrl.WM_MBUTTONDOWN; - default -> 0; + case Input.Buttons.LEFT -> HWndCtrl.MouseEvent.LBUTTONDOWN; + case Input.Buttons.RIGHT -> HWndCtrl.MouseEvent.RBUTTONDOWN; + case Input.Buttons.MIDDLE -> HWndCtrl.MouseEvent.MBUTTONDOWN; + default -> HWndCtrl.MouseEvent.EMPTY; }); } else { if (button == Input.Buttons.LEFT) { @@ -253,10 +259,10 @@ public boolean touchUp(int screenX, int screenY, int pointer, int button) { RelativeWindowPosition rwp = getRelativeWindowPositionAt(screenX, screenY); if (rwp != null) rwp.sendMouseEvent(switch (button) { - case Input.Buttons.LEFT -> HWndCtrl.WM_LBUTTONUP; - case Input.Buttons.RIGHT -> HWndCtrl.WM_RBUTTONUP; - case Input.Buttons.MIDDLE -> HWndCtrl.WM_MBUTTONUP; - default -> 0; + case Input.Buttons.LEFT -> HWndCtrl.MouseEvent.LBUTTONUP; + case Input.Buttons.RIGHT -> HWndCtrl.MouseEvent.RBUTTONUP; + case Input.Buttons.MIDDLE -> HWndCtrl.MouseEvent.MBUTTONUP; + default -> HWndCtrl.MouseEvent.EMPTY; }); } else if (button == Input.Buttons.LEFT) { // Left Click: Play the specified animation @@ -292,7 +298,7 @@ public boolean mouseMoved(int screenX, int screenY) { // Transfer mouse event RelativeWindowPosition rwp = getRelativeWindowPositionAt(screenX, screenY); if (rwp != null) - rwp.sendMouseEvent(HWndCtrl.WM_MOUSEMOVE); + rwp.sendMouseEvent(HWndCtrl.MouseEvent.MOUSEMOVE); } return false; } @@ -312,11 +318,11 @@ private void setWindowPos() { if (hWndMine == null) return; if (getHWndLoopCtrl.isExecutable(Gdx.graphics.getDeltaTime())) { refreshMonitorInfo(); - HWndCtrl new_hwnd_topmost = refreshWindowIndex(); + HWndCtrl<?> new_hwnd_topmost = refreshWindowIndex(); hWndTopmost = new_hwnd_topmost != hWndTopmost ? new_hwnd_topmost : hWndTopmost; hWndMine.setWindowTransparent(isAlwaysTransparent); } - hWndMine.setWindowPosition(hWndTopmost, (int)windowPosition.now().x, (int)windowPosition.now().y, width, height); + hWndMine.setWindowPosition((HWndCtrl)hWndTopmost, (int)windowPosition.now().x, (int)windowPosition.now().y, width, height); } private RelativeWindowPosition getRelativeWindowPositionAt(int x, int y) { @@ -324,7 +330,7 @@ private RelativeWindowPosition getRelativeWindowPositionAt(int x, int y) { return null; int absX = x + (int)(windowPosition.now().x); int absY = y + (int)(windowPosition.now().y); - for (HWndCtrl hWndCtrl : hWndList) { + for (HWndCtrl<?> hWndCtrl : hWndList) { if (coreTitleManager.getNumber(hWndCtrl) < 0) if (hWndCtrl.posLeft <= absX && hWndCtrl.posRight > absX) if (hWndCtrl.posTop <= absY && hWndCtrl.posBottom > absY) { @@ -336,10 +342,10 @@ private RelativeWindowPosition getRelativeWindowPositionAt(int x, int y) { return null; } - private HWndCtrl refreshWindowIndex() { - hWndList = HWndCtrl.getWindowList(true); - HWndCtrl minWindow = null; - HashMap<Integer, HWndCtrl> line = new HashMap<>(); + private HWndCtrl<?> refreshWindowIndex() { + hWndList = HWndCtrlFactory.getWindowList(true); + HWndCtrl<?> minWindow = null; + HashMap<Integer, HWndCtrl<?>> line = new HashMap<>(); int myPos = (int)(windowPosition.now().x + width / 2); int minNum = 2048; int myNum = coreTitleManager.getNumber(APP_TITLE); @@ -349,7 +355,7 @@ private HWndCtrl refreshWindowIndex() { plane.barriers.clear(); plane.pointCharges.clear(); } - for (HWndCtrl hWndCtrl : hWndList) { + for (HWndCtrl<?> hWndCtrl : hWndList) { int wndNum = coreTitleManager.getNumber(hWndCtrl); // Distinguish non-peer windows from peers. if (wndNum == -1) { @@ -360,7 +366,7 @@ private HWndCtrl refreshWindowIndex() { for (int h = -hWndCtrl.posTop; h > -hWndCtrl.posBottom; h--) { // Mark the window's y-position in the vertical line. if (!line.containsKey(h)) - line.put(h, (h == -hWndCtrl.posTop) ? hWndCtrl : HWndCtrl.EMPTY); // Record this window. + line.put(h, (h == -hWndCtrl.posTop) ? hWndCtrl : HWndCtrlFactory.EMPTY); // Record this window. } } } @@ -379,19 +385,19 @@ private HWndCtrl refreshWindowIndex() { } if (minWindow == null || minWindow.isEmpty()) { // Set as the top window if there is no peer. - minWindow = new HWndCtrl(-1); + minWindow = HWndCtrlFactory.getTopMost(); } if (plane != null) { // Set barriers according to the vertical line. for (int h = (int)plane.borderTop(); h > plane.borderBottom(); h--) { if (line.containsKey(h)) { - HWndCtrl temp = line.get(h); + HWndCtrl<?> temp = line.get(h); if (temp != null && temp.hWnd != null) plane.setBarrier(-temp.posTop, temp.posLeft, temp.windowWidth, false); } } } - return config.window_style_topmost ? minWindow : HWndCtrl.EMPTY; // Return the last peer window. + return config.window_style_topmost ? minWindow : HWndCtrlFactory.EMPTY; // Return the last peer window. } private ArkConfig.Monitor refreshMonitorInfo() { @@ -419,7 +425,8 @@ private void promiseToolwindowStyle(int maxRetries) { // Make sure ArkPets has been set as foreground window once for (int i = 0; ; i++) { if (hWndMine.isForeground()) { - hWndMine.setWindowExStyle(hWndMine.getWindowExStyle() | HWndCtrl.WS_EX_TOOLWINDOW); + hWndMine.setToolWindow(true); + //hWndMine.setWindowExStyle(hWndMine.getWindowExStyle() | HWndCtrl.WS_EX_TOOLWINDOW); Logger.info("Window", "SetForegroundWindow succeeded"); isToolwindowStyle = true; break; @@ -507,12 +514,11 @@ public void updatePosition(int newX, int newY, int button) { } - private record RelativeWindowPosition(HWndCtrl hWndCtrl, int relX, int relY) { - public void sendMouseEvent(int msg) { - if (msg == 0) - return; + private record RelativeWindowPosition(HWndCtrl<?> hWndCtrl, int relX, int relY) { + public void sendMouseEvent(HWndCtrl.MouseEvent msg) { + if(msg == HWndCtrl.MouseEvent.EMPTY) return; //Logger.debug("Input", "Transfer mouse event " + msg + " to `" + hWndCtrl.windowText + "` @ " + relX + ", " + relY); - hWndCtrl.updated().sendMouseEvent(msg, relX, relY); + HWndCtrlFactory.update(hWndCtrl).sendMouseEvent(msg, relX, relY); } } } diff --git a/core/src/cn/harryh/arkpets/Const.java b/core/src/cn/harryh/arkpets/Const.java index 1b352b0e..3583cbf3 100644 --- a/core/src/cn/harryh/arkpets/Const.java +++ b/core/src/cn/harryh/arkpets/Const.java @@ -3,7 +3,7 @@ */ package cn.harryh.arkpets; -import cn.harryh.arkpets.utils.HWndCtrl.NumberedTitleManager; +import cn.harryh.arkpets.hwnd.HWndCtrl.NumberedTitleManager; import cn.harryh.arkpets.utils.Logger; import cn.harryh.arkpets.utils.Version; import javafx.util.Duration; diff --git a/core/src/cn/harryh/arkpets/hwnd/HWndCtrl.java b/core/src/cn/harryh/arkpets/hwnd/HWndCtrl.java new file mode 100644 index 00000000..3efdf728 --- /dev/null +++ b/core/src/cn/harryh/arkpets/hwnd/HWndCtrl.java @@ -0,0 +1,217 @@ +/** Copyright (c) 2022-2024, Harry Huang + * At GPL-3.0 License + */ +package cn.harryh.arkpets.hwnd; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +public abstract class HWndCtrl<T> { + public final T hWnd; //Platform HWND + public final String windowText; + public final int posTop; + public final int posBottom; + public final int posLeft; + public final int posRight; + public final int windowWidth; + public final int windowHeight; + public HWndCtrl(){ + hWnd = null; + windowText = ""; + posTop = 0; + posBottom = 0; + posLeft = 0; + posRight = 0; + windowWidth = 0; + windowHeight = 0; + } + public HWndCtrl(T hWnd) { + this.hWnd = hWnd; + windowText = getWindowText(hWnd); + WindowRect rect = getWindowRect(hWnd); + posTop = rect.top; + posBottom = rect.bottom; + posLeft = rect.left; + posRight = rect.right; + windowWidth = posRight-posLeft; + windowHeight = posBottom-posTop; + } + + /** Returns window rect. + */ + public abstract WindowRect getWindowRect(T hWnd); + + /** Returns window title. + */ + public abstract String getWindowText(T hWnd); + + /** Returns true if the handle is empty. + */ + public abstract boolean isEmpty(); + + /** Returns true if the window is a foreground window now. + */ + public abstract boolean isForeground(); + + /** Returns true if the window is visible now. + */ + public abstract boolean isVisible(); + + /** Gets the center X position. + * @return X. + */ + public float getCenterX() { + return posLeft + windowWidth / 2f; + } + + /** Gets the center Y position. + * @return Y. + */ + public float getCenterY() { + return posTop + windowHeight / 2f; + } + + /** Requests to close the window. + * @param timeout Timeout for waiting response (ms). + * @return true=success, false=failure. + */ + public abstract boolean close(int timeout); + + /** Sets the window as the foreground window. + */ + public abstract void setForeground(); + + /** Sets the window's transparency. + * @param alpha Alpha value, from 0 to 1. + */ + public abstract void setWindowAlpha(float alpha); + + /** Sets the window's position without activating the window. + * @param insertAfter The window to precede the positioned window in the Z order. + * @param x The new position of the left side of the window, in client coordinates. + * @param y The new position of the top of the window, in client coordinates. + * @param w The new width of the window, in pixels. + * @param h The new height of the window, in pixels. + */ + public abstract void setWindowPosition(HWndCtrl<T> insertAfter, int x, int y, int w, int h); + + /** Sets the window's ability to be passed through. + * @param transparent Whether the window can be passed through. + */ + public abstract void setWindowTransparent(boolean transparent); + + /** + * Sets the window is a tool window. + * @param enable Whether the window is a tool window. + */ + public abstract void setToolWindow(boolean enable); + + /** + * Sets the window is layered. + * @param enable Whether the window is layered. + */ + public abstract void setLayered(boolean enable); + + /** + * Sets the window is topmost. + * @param enable Whether the window is topmost. + */ + public abstract void setTopMost(boolean enable); + + /** Sends a mouse event message to the window. + * @param msg The window message value. + * @param x The X-axis coordinate, related to the left border of the window. + * @param y The Y-axis coordinate, related to the top border of the window. + */ + public abstract void sendMouseEvent(MouseEvent msg, int x, int y); + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + HWndCtrl hWndCtrl = (HWndCtrl)o; + return hWnd.equals(hWndCtrl.hWnd); + } + + @Override + public int hashCode() { + return hWnd.hashCode(); + } + + @Override + public String toString() { + return "‘" + windowText + "’ " + windowWidth + "*" + windowHeight; + } + + + public static class NumberedTitleManager { + private final String zeroNameFormat; + private final String numberedNameFormat; + private final Pattern zeroNamePattern; + private final Pattern numberedNamePattern; + + public NumberedTitleManager(String coreName) { + zeroNameFormat = coreName; + numberedNameFormat = coreName + " (%d)"; + zeroNamePattern = Pattern.compile("^" + coreName + "$"); + numberedNamePattern = Pattern.compile("^" + coreName + " \\(([0-9]+)\\)"); + } + + public int getNumber(HWndCtrl<?> hWndCtrl) { + if (hWndCtrl.isEmpty()) return -1; + return getNumber(hWndCtrl.windowText); + } + + public int getNumber(String windowText) { + if (windowText.isEmpty()) return -1; + if (zeroNamePattern.matcher(windowText).find()) return 0; + try { + Matcher matcher = numberedNamePattern.matcher(windowText); + return matcher.find() ? Integer.parseInt(matcher.group(1)) : -1; + } catch (NumberFormatException ignored) { + return -1; + } + } + + public String getIdleTitle() { + String title = String.format(zeroNameFormat); + if (HWndCtrlFactory.find(null, title) == null) { + return title; + } else { + for (int cur = 2; cur <= 1024 ; cur ++) { + title = String.format(numberedNameFormat, cur); + if (HWndCtrlFactory.find(null, title) == null) + return title; + } + throw new IllegalStateException("Failed to get idle title."); + } + } + } + + public static class WindowRect{ + public int top; + public int bottom; + public int right; + public int left; + public WindowRect(){} + public WindowRect(int x,int y,int h,int w){ + this.top=y; + this.left=x; + this.bottom=y+h; + this.right=x+w; + } + } + + public enum MouseEvent{ + EMPTY, + MOUSEMOVE, + LBUTTONDOWN, + LBUTTONUP, + RBUTTONDOWN, + RBUTTONUP, + MBUTTONDOWN, + MBUTTONUP, + } +} diff --git a/core/src/cn/harryh/arkpets/hwnd/HWndCtrlFactory.java b/core/src/cn/harryh/arkpets/hwnd/HWndCtrlFactory.java new file mode 100644 index 00000000..01393316 --- /dev/null +++ b/core/src/cn/harryh/arkpets/hwnd/HWndCtrlFactory.java @@ -0,0 +1,143 @@ +package cn.harryh.arkpets.hwnd; + +import cn.harryh.arkpets.utils.Logger; +import com.sun.jna.Platform; +import com.sun.jna.platform.win32.WinDef; + +import java.util.ArrayList; +import java.util.List; + + +public class HWndCtrlFactory { + private static WindowSystem platform; + public static HWndCtrl<?> EMPTY; + public enum WindowSystem { + AUTO, + USER32, + X11, + MUTTER, + KWIN, + QUARTZ, + NULL + } + public static WindowSystem detectWindowSystem(){ + if(Platform.isWindows()){ + return WindowSystem.USER32; + } else if (Platform.isMac()) { + return WindowSystem.QUARTZ; + } 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")) { + return WindowSystem.X11; + } + } + return WindowSystem.NULL; + } + + /** + * Init window system. + */ + public static void init(){ + platform=detectWindowSystem(); + Logger.info("System","Using "+platform.toString()+" Window System"); + //establish connection + switch (platform){ + case MUTTER -> { + //todo + } + } + EMPTY=create(); + } + /** + * Find a window. + * @param className window's class name. + * @param windowName window's title. + * @return HWndCtrl + */ + public static HWndCtrl<?> find(String className, String windowName){ + switch (platform){ + case USER32 -> { + return User32HWndCtrl.find(className,windowName); + } + default -> { + return new NullHWndCtrl(); + } + } + } + + /** + * Create empty HWndCtrl. + * @return empty HWndCtrl + */ + public static HWndCtrl<?> create(){ + switch (platform){ + case USER32 -> { + return new User32HWndCtrl(); + } + default -> { + return new NullHWndCtrl(); + } + } + } + /** Gets the current list of windows. + * @param only_visible Whether exclude the invisible window. + * @return An ArrayList consists of HWndCtrls. + */ + public static List<? extends HWndCtrl<?>> getWindowList(boolean only_visible){ + switch (platform){ + case USER32 -> { + return User32HWndCtrl.getWindowList(only_visible); + } + default-> { + return new ArrayList<>(); + } + } + } + + /** + * Gets the topmost window. + * @return The topmost window's HWndCtrl. + */ + public static HWndCtrl<?> getTopMost(){ + switch (platform){ + case USER32 -> { + return User32HWndCtrl.getTopMost(); + } + default-> { + return new NullHWndCtrl(); + } + } + } + + /** Gets a new HWndCtrl which contains the updated information of this window. + * @return The up-to-dated HWndCtrl. + */ + public static HWndCtrl<?> update(HWndCtrl<?> old){ + switch (platform){ + case USER32 -> { + return new User32HWndCtrl((WinDef.HWND) old.hWnd); + } + default-> { + return new NullHWndCtrl(); + } + } + } + + /** Free all resources. + */ + public static void free(){ + switch (platform){ + case X11 -> { + //todo + } + default-> { + return; + } + } + } +} diff --git a/core/src/cn/harryh/arkpets/hwnd/NullHWndCtrl.java b/core/src/cn/harryh/arkpets/hwnd/NullHWndCtrl.java new file mode 100644 index 00000000..66c07d9c --- /dev/null +++ b/core/src/cn/harryh/arkpets/hwnd/NullHWndCtrl.java @@ -0,0 +1,73 @@ +package cn.harryh.arkpets.hwnd; + +public class NullHWndCtrl extends HWndCtrl<Object>{ + @Override + public WindowRect getWindowRect(Object hWnd) { + return new WindowRect(0,0,0,0); + } + + @Override + public String getWindowText(Object hWnd) { + return ""; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean isForeground() { + return false; + } + + @Override + public boolean isVisible() { + return true; + } + + @Override + public boolean close(int timeout) { + return true; + } + + @Override + public void setForeground() { + + } + + @Override + public void setWindowAlpha(float alpha) { + + } + + @Override + public void setWindowPosition(HWndCtrl<Object> insertAfter, int x, int y, int w, int h) { + + } + + @Override + public void setWindowTransparent(boolean transparent) { + + } + + @Override + public void setToolWindow(boolean enable) { + + } + + @Override + public void setLayered(boolean enable) { + + } + + @Override + public void setTopMost(boolean enable) { + + } + + @Override + public void sendMouseEvent(MouseEvent msg, int x, int y) { + + } +} diff --git a/core/src/cn/harryh/arkpets/utils/HWndCtrl.java b/core/src/cn/harryh/arkpets/hwnd/User32HWndCtrl.java similarity index 59% rename from core/src/cn/harryh/arkpets/utils/HWndCtrl.java rename to core/src/cn/harryh/arkpets/hwnd/User32HWndCtrl.java index 2211027e..4de58f0a 100644 --- a/core/src/cn/harryh/arkpets/utils/HWndCtrl.java +++ b/core/src/cn/harryh/arkpets/hwnd/User32HWndCtrl.java @@ -1,7 +1,7 @@ /** Copyright (c) 2022-2024, Harry Huang * At GPL-3.0 License */ -package cn.harryh.arkpets.utils; +package cn.harryh.arkpets.hwnd; import com.sun.jna.Native; import com.sun.jna.Pointer; @@ -12,22 +12,9 @@ import com.sun.jna.platform.win32.WinUser; import java.util.ArrayList; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class HWndCtrl { - public final HWND hWnd; - public final String windowText; +public class User32HWndCtrl extends HWndCtrl<HWND>{ public final Pointer windowPointer; - public final int posTop; - public final int posBottom; - public final int posLeft; - public final int posRight; - public final int windowWidth; - public final int windowHeight; - - public static final HWndCtrl EMPTY = new HWndCtrl(); public static final int WS_EX_TOPMOST = 0x00000008; public static final int WS_EX_TRANSPARENT = 0x00000020; @@ -50,46 +37,35 @@ public class HWndCtrl { /** HWnd Controller instance. * @param hWnd The handle of the window. */ - public HWndCtrl(HWND hWnd) { - this.hWnd = hWnd; - windowText = getWindowText(hWnd); + public User32HWndCtrl(HWND hWnd) { + super(hWnd); windowPointer = getWindowIdx(hWnd); - RECT rect = getWindowRect(hWnd); - posTop = rect.top; - posBottom = rect.bottom; - posLeft = rect.left; - posRight = rect.right; - windowWidth = posRight-posLeft; - windowHeight = posBottom-posTop; } - /** HWnd Controller instance. + /** Find a window. * @param className The class name of the window. * @param windowName The title of the window. */ - public HWndCtrl(String className, String windowName) { - this(User32.INSTANCE.FindWindow(className, windowName)); + public static HWndCtrl<HWND> find(String className, String windowName) { + HWND hwnd=User32.INSTANCE.FindWindow(className, windowName); + if(hwnd!=null){ + return new User32HWndCtrl(hwnd); + } + return null; } /** HWnd Controller instance. * @param pointer The pointer. */ - public HWndCtrl(int pointer) { + public User32HWndCtrl(int pointer) { this(new HWND(Pointer.createConstant(pointer))); } /** Empty HWnd Controller instance. */ - public HWndCtrl() { - hWnd = null; - windowText = ""; + public User32HWndCtrl() { + super(); windowPointer = null; - posTop = 0; - posBottom = 0; - posLeft = 0; - posRight = 0; - windowWidth = 0; - windowHeight = 0; } /** Returns true if the handle is empty. @@ -108,21 +84,7 @@ public boolean isForeground() { /** Returns true if the window is visible now. */ public boolean isVisible() { - return isVisible(hWnd); - } - - /** Gets the center X position. - * @return X. - */ - public float getCenterX() { - return posLeft + windowWidth / 2f; - } - - /** Gets the center Y position. - * @return Y. - */ - public float getCenterY() { - return posTop + windowHeight / 2f; + return visible(hWnd); } /** Requests to close the window. @@ -174,7 +136,7 @@ public void setWindowExStyle(int newLong) { * @param w The new width of the window, in pixels. * @param h The new height of the window, in pixels. */ - public void setWindowPosition(HWndCtrl insertAfter, int x, int y, int w, int h) { + public void setWindowPosition(HWndCtrl<HWND> insertAfter, int x, int y, int w, int h) { if (isEmpty()) return; User32.INSTANCE.SetWindowPos(hWnd, insertAfter.hWnd, x, y, w, h, WinUser.SWP_NOACTIVATE); } @@ -185,9 +147,45 @@ public void setWindowPosition(HWndCtrl insertAfter, int x, int y, int w, int h) public void setWindowTransparent(boolean transparent) { if (isEmpty()) return; if (transparent) - setWindowExStyle(getWindowExStyle() | HWndCtrl.WS_EX_TRANSPARENT); + setWindowExStyle(getWindowExStyle() | User32HWndCtrl.WS_EX_TRANSPARENT); + else + setWindowExStyle(getWindowExStyle() & ~User32HWndCtrl.WS_EX_TRANSPARENT); + } + + /** + * Sets the window is a tool window. + * @param enable Whether the window is a tool window. + */ + public void setToolWindow(boolean enable) { + if (isEmpty()) return; + if (enable) + setWindowExStyle(getWindowExStyle() | User32HWndCtrl.WS_EX_TOOLWINDOW); + else + setWindowExStyle(getWindowExStyle() & ~User32HWndCtrl.WS_EX_TOOLWINDOW); + } + + /** + * Sets the window is layered. + * @param enable Whether the window is layered. + */ + public void setLayered(boolean enable){ + if (isEmpty()) return; + if (enable) + setWindowExStyle(getWindowExStyle() | User32HWndCtrl.WS_EX_LAYERED); + else + setWindowExStyle(getWindowExStyle() & ~User32HWndCtrl.WS_EX_LAYERED); + } + + /** + * Sets the window is topmost. + * @param enable Whether the window is topmost. + */ + public void setTopMost(boolean enable){ + if (isEmpty()) return; + if (enable) + setWindowExStyle(getWindowExStyle() | User32HWndCtrl.WS_EX_TOPMOST); else - setWindowExStyle(getWindowExStyle() & ~HWndCtrl.WS_EX_TRANSPARENT); + setWindowExStyle(getWindowExStyle() & ~User32HWndCtrl.WS_EX_TOPMOST); } /** Sends a mouse event message to the window. @@ -195,33 +193,36 @@ public void setWindowTransparent(boolean transparent) { * @param x The X-axis coordinate, related to the left border of the window. * @param y The Y-axis coordinate, related to the top border of the window. */ - public void sendMouseEvent(int msg, int x, int y) { + public void sendMouseEvent(MouseEvent msg, int x, int y) { + int wmsg=switch (msg){ + case MOUSEMOVE -> WM_MOUSEMOVE; + case LBUTTONDOWN -> WM_LBUTTONDOWN; + case LBUTTONUP -> WM_LBUTTONUP; + case RBUTTONDOWN -> WM_RBUTTONDOWN; + case RBUTTONUP -> WM_RBUTTONUP; + case MBUTTONDOWN -> WM_MBUTTONDOWN; + case MBUTTONUP -> WM_MBUTTONUP; + default -> 0; + }; int wParam = switch (msg) { - case WM_LBUTTONDOWN -> MK_LBUTTON; - case WM_RBUTTONDOWN -> MK_RBUTTON; - case WM_MBUTTONDOWN -> MK_MBUTTON; + case LBUTTONDOWN -> MK_LBUTTON; + case RBUTTONDOWN -> MK_RBUTTON; + case MBUTTONDOWN -> MK_MBUTTON; default -> 0; }; int lParam = (y << 16) | x; - User32.INSTANCE.SendMessage(hWnd, msg, new WinDef.WPARAM(wParam), new WinDef.LPARAM(lParam)); - } - - /** Gets a new HWndCtrl which contains the updated information of this window. - * @return The up-to-dated HWndCtrl. - */ - public HWndCtrl updated() { - return new HWndCtrl(hWnd); + User32.INSTANCE.SendMessage(hWnd, wmsg, new WinDef.WPARAM(wParam), new WinDef.LPARAM(lParam)); } /** Gets the current list of windows. * @param only_visible Whether exclude the invisible window. * @return An ArrayList consists of HWndCtrls. */ - public static ArrayList<HWndCtrl> getWindowList(boolean only_visible) { - ArrayList<HWndCtrl> windowList = new ArrayList<>(); + public static ArrayList<User32HWndCtrl> getWindowList(boolean only_visible) { + ArrayList<User32HWndCtrl> windowList = new ArrayList<>(); User32.INSTANCE.EnumWindows((hWnd, arg1) -> { - if (User32.INSTANCE.IsWindow(hWnd) && (!only_visible || isVisible(hWnd))) - windowList.add(new HWndCtrl(hWnd)); + if (User32.INSTANCE.IsWindow(hWnd) && (!only_visible || visible(hWnd))) + windowList.add(new User32HWndCtrl(hWnd)); return true; }, null); return windowList; @@ -232,22 +233,30 @@ public static ArrayList<HWndCtrl> getWindowList(boolean only_visible) { * @param exclude_ws_ex Exclude the specific window-style-extra. * @return An ArrayList consists of HWndCtrls. */ - public static ArrayList<HWndCtrl> getWindowList(boolean only_visible, long exclude_ws_ex) { - ArrayList<HWndCtrl> windowList = new ArrayList<>(); + public static ArrayList<User32HWndCtrl> getWindowList(boolean only_visible, long exclude_ws_ex) { + ArrayList<User32HWndCtrl> windowList = new ArrayList<>(); User32.INSTANCE.EnumWindows((hWnd, arg1) -> { - if (User32.INSTANCE.IsWindow(hWnd) && (!only_visible || isVisible(hWnd)) + if (User32.INSTANCE.IsWindow(hWnd) && (!only_visible || visible(hWnd)) && (User32.INSTANCE.GetWindowLong(hWnd, WinUser.GWL_EXSTYLE) & exclude_ws_ex) != exclude_ws_ex) - windowList.add(new HWndCtrl(hWnd)); + windowList.add(new User32HWndCtrl(hWnd)); return true; }, null); return windowList; } - private static boolean isVisible(HWND hWnd) { + /** + * Gets the topmost window. + * @return The topmost window's HWndCtrl. + */ + public static User32HWndCtrl getTopMost(){ + return new User32HWndCtrl(-1); + } + + private static boolean visible(HWND hWnd) { try { if (!User32.INSTANCE.IsWindowVisible(hWnd) || !User32.INSTANCE.IsWindowEnabled(hWnd)) return false; - RECT rect = getWindowRect(hWnd); + WindowRect rect = getRect(hWnd); if (rect.top == rect.bottom || rect.left == rect.right) return false; } catch(Exception e) { @@ -260,16 +269,25 @@ private static Pointer getWindowIdx(HWND hWnd) { return hWnd.getPointer(); } - private static String getWindowText(HWND hWnd) { + public String getWindowText(HWND hWnd) { char[] text = new char[1024]; User32.INSTANCE.GetWindowText(hWnd, text, 1024); return Native.toString(text); } - private static RECT getWindowRect(HWND hWnd) { + private static WindowRect getRect(HWND hWnd) { RECT rect = new RECT(); User32.INSTANCE.GetWindowRect(hWnd, rect); - return rect; + WindowRect newRect=new WindowRect(); + newRect.top= rect.top; + newRect.bottom= rect.bottom; + newRect.left= rect.left; + newRect.right= rect.right; + return newRect; + } + + public WindowRect getWindowRect(HWND hWnd) { + return getRect(hWnd); } @Override @@ -277,7 +295,7 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - HWndCtrl hWndCtrl = (HWndCtrl)o; + User32HWndCtrl hWndCtrl = (User32HWndCtrl)o; return hWnd.equals(hWndCtrl.hWnd); } @@ -290,49 +308,4 @@ public int hashCode() { public String toString() { return "‘" + windowText + "’ " + windowWidth + "*" + windowHeight + " style=" + getWindowExStyle(); } - - - public static class NumberedTitleManager { - private final String zeroNameFormat; - private final String numberedNameFormat; - private final Pattern zeroNamePattern; - private final Pattern numberedNamePattern; - - public NumberedTitleManager(String coreName) { - zeroNameFormat = coreName; - numberedNameFormat = coreName + " (%d)"; - zeroNamePattern = Pattern.compile("^" + coreName + "$"); - numberedNamePattern = Pattern.compile("^" + coreName + " \\(([0-9]+)\\)"); - } - - public int getNumber(HWndCtrl hWndCtrl) { - if (hWndCtrl.isEmpty()) return -1; - return getNumber(hWndCtrl.windowText); - } - - public int getNumber(String windowText) { - if (windowText.isEmpty()) return -1; - if (zeroNamePattern.matcher(windowText).find()) return 0; - try { - Matcher matcher = numberedNamePattern.matcher(windowText); - return matcher.find() ? Integer.parseInt(matcher.group(1)) : -1; - } catch (NumberFormatException ignored) { - return -1; - } - } - - public String getIdleTitle() { - String title = String.format(zeroNameFormat); - if (User32.INSTANCE.FindWindow(null, title) == null) { - return title; - } else { - for (int cur = 2; cur <= 1024 ; cur ++) { - title = String.format(numberedNameFormat, cur); - if (User32.INSTANCE.FindWindow(null, title) == null) - return title; - } - throw new IllegalStateException("Failed to get idle title."); - } - } - } } diff --git a/desktop/src/cn/harryh/arkpets/EmbeddedLauncher.java b/desktop/src/cn/harryh/arkpets/EmbeddedLauncher.java index dc187ee7..a1ff0178 100644 --- a/desktop/src/cn/harryh/arkpets/EmbeddedLauncher.java +++ b/desktop/src/cn/harryh/arkpets/EmbeddedLauncher.java @@ -4,6 +4,7 @@ package cn.harryh.arkpets; import cn.harryh.arkpets.utils.ArgPending; +import cn.harryh.arkpets.hwnd.HWndCtrlFactory; import cn.harryh.arkpets.utils.Logger; import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration; @@ -57,6 +58,7 @@ protected void process(String command, String addition) { Logger.debug("System", "Default charset is " + Charset.defaultCharset()); try { + HWndCtrlFactory.init(); Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration(); // Configure FPS config.setForegroundFPS(fpsDefault);