From ce3fe4e32f9cb65f798f744be8348619e14da7a1 Mon Sep 17 00:00:00 2001 From: litwak913 Date: Tue, 13 Aug 2024 22:00:04 +0800 Subject: [PATCH 1/2] abstract HWndCtrl --- core/src/cn/harryh/arkpets/ArkPets.java | 70 ++-- .../src/cn/harryh/arkpets/utils/HWndCtrl.java | 265 ++++----------- .../harryh/arkpets/utils/HWndCtrlFactory.java | 145 ++++++++ .../cn/harryh/arkpets/utils/NullHWndCtrl.java | 73 ++++ .../harryh/arkpets/utils/User32HWndCtrl.java | 311 ++++++++++++++++++ .../cn/harryh/arkpets/EmbeddedLauncher.java | 2 + 6 files changed, 641 insertions(+), 225 deletions(-) create mode 100644 core/src/cn/harryh/arkpets/utils/HWndCtrlFactory.java create mode 100644 core/src/cn/harryh/arkpets/utils/NullHWndCtrl.java create mode 100644 core/src/cn/harryh/arkpets/utils/User32HWndCtrl.java diff --git a/core/src/cn/harryh/arkpets/ArkPets.java b/core/src/cn/harryh/arkpets/ArkPets.java index 30d68586..266375b8 100644 --- a/core/src/cn/harryh/arkpets/ArkPets.java +++ b/core/src/cn/harryh/arkpets/ArkPets.java @@ -11,6 +11,7 @@ import cn.harryh.arkpets.transitions.TransitionVector2; import cn.harryh.arkpets.tray.MemberTrayImpl; import cn.harryh.arkpets.utils.HWndCtrl; +import cn.harryh.arkpets.utils.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 hWndList; + private List> 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 line = new HashMap<>(); + private HWndCtrl refreshWindowIndex() { + hWndList = HWndCtrlFactory.getWindowList(true); + HWndCtrl minWindow = null; + HashMap> 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/utils/HWndCtrl.java b/core/src/cn/harryh/arkpets/utils/HWndCtrl.java index 2211027e..722b0d3e 100644 --- a/core/src/cn/harryh/arkpets/utils/HWndCtrl.java +++ b/core/src/cn/harryh/arkpets/utils/HWndCtrl.java @@ -3,58 +3,33 @@ */ package cn.harryh.arkpets.utils; -import com.sun.jna.Native; -import com.sun.jna.Pointer; -import com.sun.jna.platform.win32.User32; -import com.sun.jna.platform.win32.WinDef; -import com.sun.jna.platform.win32.WinDef.HWND; -import com.sun.jna.platform.win32.WinDef.RECT; -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 abstract class HWndCtrl { + public final T hWnd; //Platform HWND public final String windowText; - 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; - public static final int WS_EX_TOOLWINDOW = 0x00000080; - public static final int WS_EX_LAYERED = 0x00080000; - - public static final int WM_MOUSEMOVE = 0x0200; - public static final int WM_LBUTTONDOWN = 0x0201; - public static final int WM_LBUTTONUP = 0x0202; - public static final int WM_RBUTTONDOWN = 0x0204; - public static final int WM_RBUTTONUP = 0x0205; - public static final int WM_MBUTTONDOWN = 0x0207; - public static final int WM_MBUTTONUP = 0x0208; - - private static final int MK_LBUTTON = 0x0001; - private static final int MK_RBUTTON = 0x0002; - private static final int MK_MBUTTON = 0x0010; - - - /** HWnd Controller instance. - * @param hWnd The handle of the window. - */ - public HWndCtrl(HWND hWnd) { + 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); - windowPointer = getWindowIdx(hWnd); - RECT rect = getWindowRect(hWnd); + WindowRect rect = getWindowRect(hWnd); posTop = rect.top; posBottom = rect.bottom; posLeft = rect.left; @@ -63,53 +38,25 @@ public HWndCtrl(HWND hWnd) { windowHeight = posBottom-posTop; } - /** HWnd Controller instance. - * @param className The class name of the window. - * @param windowName The title of the window. + /** Returns window rect. */ - public HWndCtrl(String className, String windowName) { - this(User32.INSTANCE.FindWindow(className, windowName)); - } + public abstract WindowRect getWindowRect(T hWnd); - /** HWnd Controller instance. - * @param pointer The pointer. + /** Returns window title. */ - public HWndCtrl(int pointer) { - this(new HWND(Pointer.createConstant(pointer))); - } - - /** Empty HWnd Controller instance. - */ - public HWndCtrl() { - hWnd = null; - windowText = ""; - windowPointer = null; - posTop = 0; - posBottom = 0; - posLeft = 0; - posRight = 0; - windowWidth = 0; - windowHeight = 0; - } + public abstract String getWindowText(T hWnd); /** Returns true if the handle is empty. */ - public boolean isEmpty() { - return hWnd == null; - } + public abstract boolean isEmpty(); /** Returns true if the window is a foreground window now. */ - public boolean isForeground() { - if (isEmpty()) return false; - return hWnd.equals(User32.INSTANCE.GetForegroundWindow()); - } + public abstract boolean isForeground(); /** Returns true if the window is visible now. */ - public boolean isVisible() { - return isVisible(hWnd); - } + public abstract boolean isVisible(); /** Gets the center X position. * @return X. @@ -129,43 +76,16 @@ public float getCenterY() { * @param timeout Timeout for waiting response (ms). * @return true=success, false=failure. */ - public boolean close(int timeout) { - return User32.INSTANCE.SendMessageTimeout(hWnd, 0x10, null, null, timeout, WinUser.SMTO_NORMAL, null).intValue() == 0; - } - - /** Gets the value of the window's extended styles. - * @return EX_STYLE value. - * @see WinUser - */ - public int getWindowExStyle() { - if (isEmpty()) return 0; - return User32.INSTANCE.GetWindowLong(hWnd, WinUser.GWL_EXSTYLE); - } + public abstract boolean close(int timeout); /** Sets the window as the foreground window. */ - public void setForeground() { - User32.INSTANCE.SetForegroundWindow(hWnd); - } + public abstract void setForeground(); /** Sets the window's transparency. * @param alpha Alpha value, from 0 to 1. */ - public void setWindowAlpha(float alpha) { - if (isEmpty()) return; - alpha = Math.max(0, Math.min(1, alpha)); - byte byteAlpha = (byte)((int)(alpha * 255) & 0xFF); - User32.INSTANCE.SetLayeredWindowAttributes(hWnd, 0, byteAlpha, User32.LWA_ALPHA); - } - - /** Sets the window's extended styles. - * @param newLong New EX_STYLE value. - * @see WinUser - */ - public void setWindowExStyle(int newLong) { - if (isEmpty()) return; - User32.INSTANCE.SetWindowLong(hWnd, WinUser.GWL_EXSTYLE, newLong); - } + 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. @@ -174,103 +94,37 @@ 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) { - if (isEmpty()) return; - User32.INSTANCE.SetWindowPos(hWnd, insertAfter.hWnd, x, y, w, h, WinUser.SWP_NOACTIVATE); - } + public abstract void setWindowPosition(HWndCtrl 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 void setWindowTransparent(boolean transparent) { - if (isEmpty()) return; - if (transparent) - setWindowExStyle(getWindowExStyle() | HWndCtrl.WS_EX_TRANSPARENT); - else - setWindowExStyle(getWindowExStyle() & ~HWndCtrl.WS_EX_TRANSPARENT); - } + public abstract void setWindowTransparent(boolean transparent); - /** 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. + /** + * Sets the window is a tool window. + * @param enable Whether the window is a tool window. */ - public void sendMouseEvent(int msg, int x, int y) { - int wParam = switch (msg) { - case WM_LBUTTONDOWN -> MK_LBUTTON; - case WM_RBUTTONDOWN -> MK_RBUTTON; - case WM_MBUTTONDOWN -> MK_MBUTTON; - default -> 0; - }; - int lParam = (y << 16) | x; - User32.INSTANCE.SendMessage(hWnd, msg, new WinDef.WPARAM(wParam), new WinDef.LPARAM(lParam)); - } + public abstract void setToolWindow(boolean enable); - /** Gets a new HWndCtrl which contains the updated information of this window. - * @return The up-to-dated HWndCtrl. + /** + * Sets the window is layered. + * @param enable Whether the window is layered. */ - public HWndCtrl updated() { - return new HWndCtrl(hWnd); - } + public abstract void setLayered(boolean enable); - /** Gets the current list of windows. - * @param only_visible Whether exclude the invisible window. - * @return An ArrayList consists of HWndCtrls. + /** + * Sets the window is topmost. + * @param enable Whether the window is topmost. */ - public static ArrayList getWindowList(boolean only_visible) { - ArrayList windowList = new ArrayList<>(); - User32.INSTANCE.EnumWindows((hWnd, arg1) -> { - if (User32.INSTANCE.IsWindow(hWnd) && (!only_visible || isVisible(hWnd))) - windowList.add(new HWndCtrl(hWnd)); - return true; - }, null); - return windowList; - } + public abstract void setTopMost(boolean enable); - /** Gets the current list of windows. (Advanced) - * @param only_visible Whether exclude the invisible window. - * @param exclude_ws_ex Exclude the specific window-style-extra. - * @return An ArrayList consists of HWndCtrls. + /** 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 static ArrayList getWindowList(boolean only_visible, long exclude_ws_ex) { - ArrayList windowList = new ArrayList<>(); - User32.INSTANCE.EnumWindows((hWnd, arg1) -> { - if (User32.INSTANCE.IsWindow(hWnd) && (!only_visible || isVisible(hWnd)) - && (User32.INSTANCE.GetWindowLong(hWnd, WinUser.GWL_EXSTYLE) & exclude_ws_ex) != exclude_ws_ex) - windowList.add(new HWndCtrl(hWnd)); - return true; - }, null); - return windowList; - } - - private static boolean isVisible(HWND hWnd) { - try { - if (!User32.INSTANCE.IsWindowVisible(hWnd) || !User32.INSTANCE.IsWindowEnabled(hWnd)) - return false; - RECT rect = getWindowRect(hWnd); - if (rect.top == rect.bottom || rect.left == rect.right) - return false; - } catch(Exception e) { - return false; - } - return true; - } - - private static Pointer getWindowIdx(HWND hWnd) { - return hWnd.getPointer(); - } - - private static 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) { - RECT rect = new RECT(); - User32.INSTANCE.GetWindowRect(hWnd, rect); - return rect; - } + public abstract void sendMouseEvent(MouseEvent msg, int x, int y); @Override public boolean equals(Object o) { @@ -288,7 +142,7 @@ public int hashCode() { @Override public String toString() { - return "‘" + windowText + "’ " + windowWidth + "*" + windowHeight + " style=" + getWindowExStyle(); + return "‘" + windowText + "’ " + windowWidth + "*" + windowHeight; } @@ -305,7 +159,7 @@ public NumberedTitleManager(String coreName) { numberedNamePattern = Pattern.compile("^" + coreName + " \\(([0-9]+)\\)"); } - public int getNumber(HWndCtrl hWndCtrl) { + public int getNumber(HWndCtrl hWndCtrl) { if (hWndCtrl.isEmpty()) return -1; return getNumber(hWndCtrl.windowText); } @@ -323,16 +177,41 @@ public int getNumber(String windowText) { public String getIdleTitle() { String title = String.format(zeroNameFormat); - if (User32.INSTANCE.FindWindow(null, title) == null) { + if (HWndCtrlFactory.find(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) + 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/utils/HWndCtrlFactory.java b/core/src/cn/harryh/arkpets/utils/HWndCtrlFactory.java new file mode 100644 index 00000000..a052963d --- /dev/null +++ b/core/src/cn/harryh/arkpets/utils/HWndCtrlFactory.java @@ -0,0 +1,145 @@ +package cn.harryh.arkpets.utils; + +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, + WLROOTS, + 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("wayland")) { + return WindowSystem.WLROOTS; + } 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> 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/utils/NullHWndCtrl.java b/core/src/cn/harryh/arkpets/utils/NullHWndCtrl.java new file mode 100644 index 00000000..a791e21f --- /dev/null +++ b/core/src/cn/harryh/arkpets/utils/NullHWndCtrl.java @@ -0,0 +1,73 @@ +package cn.harryh.arkpets.utils; + +public class NullHWndCtrl extends HWndCtrl{ + @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 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/User32HWndCtrl.java b/core/src/cn/harryh/arkpets/utils/User32HWndCtrl.java new file mode 100644 index 00000000..6d0c500b --- /dev/null +++ b/core/src/cn/harryh/arkpets/utils/User32HWndCtrl.java @@ -0,0 +1,311 @@ +/** Copyright (c) 2022-2024, Harry Huang + * At GPL-3.0 License + */ +package cn.harryh.arkpets.utils; + +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.User32; +import com.sun.jna.platform.win32.WinDef; +import com.sun.jna.platform.win32.WinDef.HWND; +import com.sun.jna.platform.win32.WinDef.RECT; +import com.sun.jna.platform.win32.WinUser; + +import java.util.ArrayList; + +public class User32HWndCtrl extends HWndCtrl{ + public final Pointer windowPointer; + + public static final int WS_EX_TOPMOST = 0x00000008; + public static final int WS_EX_TRANSPARENT = 0x00000020; + public static final int WS_EX_TOOLWINDOW = 0x00000080; + public static final int WS_EX_LAYERED = 0x00080000; + + public static final int WM_MOUSEMOVE = 0x0200; + public static final int WM_LBUTTONDOWN = 0x0201; + public static final int WM_LBUTTONUP = 0x0202; + public static final int WM_RBUTTONDOWN = 0x0204; + public static final int WM_RBUTTONUP = 0x0205; + public static final int WM_MBUTTONDOWN = 0x0207; + public static final int WM_MBUTTONUP = 0x0208; + + private static final int MK_LBUTTON = 0x0001; + private static final int MK_RBUTTON = 0x0002; + private static final int MK_MBUTTON = 0x0010; + + + /** HWnd Controller instance. + * @param hWnd The handle of the window. + */ + public User32HWndCtrl(HWND hWnd) { + super(hWnd); + windowPointer = getWindowIdx(hWnd); + } + + /** Find a window. + * @param className The class name of the window. + * @param windowName The title of the window. + */ + public static HWndCtrl 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 User32HWndCtrl(int pointer) { + this(new HWND(Pointer.createConstant(pointer))); + } + + /** Empty HWnd Controller instance. + */ + public User32HWndCtrl() { + super(); + windowPointer = null; + } + + /** Returns true if the handle is empty. + */ + public boolean isEmpty() { + return hWnd == null; + } + + /** Returns true if the window is a foreground window now. + */ + public boolean isForeground() { + if (isEmpty()) return false; + return hWnd.equals(User32.INSTANCE.GetForegroundWindow()); + } + + /** Returns true if the window is visible now. + */ + public boolean isVisible() { + return visible(hWnd); + } + + /** Requests to close the window. + * @param timeout Timeout for waiting response (ms). + * @return true=success, false=failure. + */ + public boolean close(int timeout) { + return User32.INSTANCE.SendMessageTimeout(hWnd, 0x10, null, null, timeout, WinUser.SMTO_NORMAL, null).intValue() == 0; + } + + /** Gets the value of the window's extended styles. + * @return EX_STYLE value. + * @see WinUser + */ + public int getWindowExStyle() { + if (isEmpty()) return 0; + return User32.INSTANCE.GetWindowLong(hWnd, WinUser.GWL_EXSTYLE); + } + + /** Sets the window as the foreground window. + */ + public void setForeground() { + User32.INSTANCE.SetForegroundWindow(hWnd); + } + + /** Sets the window's transparency. + * @param alpha Alpha value, from 0 to 1. + */ + public void setWindowAlpha(float alpha) { + if (isEmpty()) return; + alpha = Math.max(0, Math.min(1, alpha)); + byte byteAlpha = (byte)((int)(alpha * 255) & 0xFF); + User32.INSTANCE.SetLayeredWindowAttributes(hWnd, 0, byteAlpha, User32.LWA_ALPHA); + } + + /** Sets the window's extended styles. + * @param newLong New EX_STYLE value. + * @see WinUser + */ + public void setWindowExStyle(int newLong) { + if (isEmpty()) return; + User32.INSTANCE.SetWindowLong(hWnd, WinUser.GWL_EXSTYLE, newLong); + } + + /** 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 void setWindowPosition(HWndCtrl 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); + } + + /** Sets the window's ability to be passed through. + * @param transparent Whether the window can be passed through. + */ + public void setWindowTransparent(boolean transparent) { + if (isEmpty()) return; + if (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() & ~User32HWndCtrl.WS_EX_TOPMOST); + } + + /** 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 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 LBUTTONDOWN -> MK_LBUTTON; + case RBUTTONDOWN -> MK_RBUTTON; + case MBUTTONDOWN -> MK_MBUTTON; + default -> 0; + }; + int lParam = (y << 16) | x; + 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 getWindowList(boolean only_visible) { + ArrayList windowList = new ArrayList<>(); + User32.INSTANCE.EnumWindows((hWnd, arg1) -> { + if (User32.INSTANCE.IsWindow(hWnd) && (!only_visible || visible(hWnd))) + windowList.add(new User32HWndCtrl(hWnd)); + return true; + }, null); + return windowList; + } + + /** Gets the current list of windows. (Advanced) + * @param only_visible Whether exclude the invisible window. + * @param exclude_ws_ex Exclude the specific window-style-extra. + * @return An ArrayList consists of HWndCtrls. + */ + public static ArrayList getWindowList(boolean only_visible, long exclude_ws_ex) { + ArrayList windowList = new ArrayList<>(); + User32.INSTANCE.EnumWindows((hWnd, arg1) -> { + 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 User32HWndCtrl(hWnd)); + return true; + }, null); + return windowList; + } + + /** + * 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; + WindowRect rect = getRect(hWnd); + if (rect.top == rect.bottom || rect.left == rect.right) + return false; + } catch(Exception e) { + return false; + } + return true; + } + + private static Pointer getWindowIdx(HWND hWnd) { + return hWnd.getPointer(); + } + + public String getWindowText(HWND hWnd) { + char[] text = new char[1024]; + User32.INSTANCE.GetWindowText(hWnd, text, 1024); + return Native.toString(text); + } + + private static WindowRect getRect(HWND hWnd) { + RECT rect = new RECT(); + User32.INSTANCE.GetWindowRect(hWnd, 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 + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + User32HWndCtrl hWndCtrl = (User32HWndCtrl)o; + return hWnd.equals(hWndCtrl.hWnd); + } + + @Override + public int hashCode() { + return hWnd.hashCode(); + } + + @Override + public String toString() { + return "‘" + windowText + "’ " + windowWidth + "*" + windowHeight + " style=" + getWindowExStyle(); + } +} diff --git a/desktop/src/cn/harryh/arkpets/EmbeddedLauncher.java b/desktop/src/cn/harryh/arkpets/EmbeddedLauncher.java index dc187ee7..e834f8aa 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.utils.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); From 7f467375e27434d8f46c20f32c5d7f07b86ecf36 Mon Sep 17 00:00:00 2001 From: litwak913 Date: Tue, 20 Aug 2024 21:20:31 +0800 Subject: [PATCH 2/2] remove wlroots and move into hwnd package --- core/src/cn/harryh/arkpets/ArkPets.java | 4 ++-- core/src/cn/harryh/arkpets/Const.java | 2 +- core/src/cn/harryh/arkpets/{utils => hwnd}/HWndCtrl.java | 2 +- .../cn/harryh/arkpets/{utils => hwnd}/HWndCtrlFactory.java | 6 ++---- .../src/cn/harryh/arkpets/{utils => hwnd}/NullHWndCtrl.java | 2 +- .../cn/harryh/arkpets/{utils => hwnd}/User32HWndCtrl.java | 2 +- desktop/src/cn/harryh/arkpets/EmbeddedLauncher.java | 2 +- 7 files changed, 9 insertions(+), 11 deletions(-) rename core/src/cn/harryh/arkpets/{utils => hwnd}/HWndCtrl.java (99%) rename core/src/cn/harryh/arkpets/{utils => hwnd}/HWndCtrlFactory.java (96%) rename core/src/cn/harryh/arkpets/{utils => hwnd}/NullHWndCtrl.java (97%) rename core/src/cn/harryh/arkpets/{utils => hwnd}/User32HWndCtrl.java (99%) diff --git a/core/src/cn/harryh/arkpets/ArkPets.java b/core/src/cn/harryh/arkpets/ArkPets.java index 266375b8..aca44f4d 100644 --- a/core/src/cn/harryh/arkpets/ArkPets.java +++ b/core/src/cn/harryh/arkpets/ArkPets.java @@ -10,8 +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.utils.HWndCtrlFactory; +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; 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/utils/HWndCtrl.java b/core/src/cn/harryh/arkpets/hwnd/HWndCtrl.java similarity index 99% rename from core/src/cn/harryh/arkpets/utils/HWndCtrl.java rename to core/src/cn/harryh/arkpets/hwnd/HWndCtrl.java index 722b0d3e..3efdf728 100644 --- a/core/src/cn/harryh/arkpets/utils/HWndCtrl.java +++ b/core/src/cn/harryh/arkpets/hwnd/HWndCtrl.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 java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/core/src/cn/harryh/arkpets/utils/HWndCtrlFactory.java b/core/src/cn/harryh/arkpets/hwnd/HWndCtrlFactory.java similarity index 96% rename from core/src/cn/harryh/arkpets/utils/HWndCtrlFactory.java rename to core/src/cn/harryh/arkpets/hwnd/HWndCtrlFactory.java index a052963d..01393316 100644 --- a/core/src/cn/harryh/arkpets/utils/HWndCtrlFactory.java +++ b/core/src/cn/harryh/arkpets/hwnd/HWndCtrlFactory.java @@ -1,5 +1,6 @@ -package cn.harryh.arkpets.utils; +package cn.harryh.arkpets.hwnd; +import cn.harryh.arkpets.utils.Logger; import com.sun.jna.Platform; import com.sun.jna.platform.win32.WinDef; @@ -16,7 +17,6 @@ public enum WindowSystem { X11, MUTTER, KWIN, - WLROOTS, QUARTZ, NULL } @@ -32,8 +32,6 @@ public static WindowSystem detectWindowSystem(){ return WindowSystem.MUTTER; } else if (desktop.equals("KDE")) { return WindowSystem.KWIN; - } else if (type.equals("wayland")) { - return WindowSystem.WLROOTS; } else if (type.equals("x11")) { return WindowSystem.X11; } diff --git a/core/src/cn/harryh/arkpets/utils/NullHWndCtrl.java b/core/src/cn/harryh/arkpets/hwnd/NullHWndCtrl.java similarity index 97% rename from core/src/cn/harryh/arkpets/utils/NullHWndCtrl.java rename to core/src/cn/harryh/arkpets/hwnd/NullHWndCtrl.java index a791e21f..66c07d9c 100644 --- a/core/src/cn/harryh/arkpets/utils/NullHWndCtrl.java +++ b/core/src/cn/harryh/arkpets/hwnd/NullHWndCtrl.java @@ -1,4 +1,4 @@ -package cn.harryh.arkpets.utils; +package cn.harryh.arkpets.hwnd; public class NullHWndCtrl extends HWndCtrl{ @Override diff --git a/core/src/cn/harryh/arkpets/utils/User32HWndCtrl.java b/core/src/cn/harryh/arkpets/hwnd/User32HWndCtrl.java similarity index 99% rename from core/src/cn/harryh/arkpets/utils/User32HWndCtrl.java rename to core/src/cn/harryh/arkpets/hwnd/User32HWndCtrl.java index 6d0c500b..4de58f0a 100644 --- a/core/src/cn/harryh/arkpets/utils/User32HWndCtrl.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; diff --git a/desktop/src/cn/harryh/arkpets/EmbeddedLauncher.java b/desktop/src/cn/harryh/arkpets/EmbeddedLauncher.java index e834f8aa..a1ff0178 100644 --- a/desktop/src/cn/harryh/arkpets/EmbeddedLauncher.java +++ b/desktop/src/cn/harryh/arkpets/EmbeddedLauncher.java @@ -4,7 +4,7 @@ package cn.harryh.arkpets; import cn.harryh.arkpets.utils.ArgPending; -import cn.harryh.arkpets.utils.HWndCtrlFactory; +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;