pgo-automation/src/main/java/com/pokemongo/WindowFinder.java
2025-12-19 22:58:50 +01:00

265 lines
10 KiB
Java

package com.pokemongo;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;
import java.awt.Rectangle;
import java.util.Arrays;
import java.util.List;
/**
* Pomocná třída pro hledání oken v Linux/X11 prostředí.
* Používá X11 API přes JNA bindings.
*/
public class WindowFinder {
private static Pointer cachedDisplay = null;
/**
* X11 native library interface
*/
public interface X11 extends Library {
X11 INSTANCE = Native.load("X11", X11.class);
/**
* X11 Window Attributes struktura - přesná kopie z X11/Xlib.h
*/
class XWindowAttributes extends Structure {
public int x;
public int y;
public int width;
public int height;
public int border_width;
public int depth;
public Pointer visual; // Visual* - opaque pointer
public long root; // Window (XID)
public int class_; // int, not pointer
public int bit_gravity;
public int win_gravity;
public int backing_store;
public long backing_planes;
public long backing_pixel;
public boolean save_under; // Bool - should be boolean
public long colormap; // Colormap (XID) - should be long
public boolean map_installed; // Bool - should be boolean
public int map_state;
public long all_event_masks;
public long your_event_mask;
public long do_not_propagate_mask;
public boolean override_redirect; // Bool - missing field
public Pointer screen; // Screen* - opaque pointer, missing field
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("x", "y", "width", "height", "border_width", "depth",
"visual", "root", "class_", "bit_gravity", "win_gravity",
"backing_store", "backing_planes", "backing_pixel",
"save_under", "colormap", "map_installed", "map_state",
"all_event_masks", "your_event_mask", "do_not_propagate_mask",
"override_redirect", "screen");
}
}
// X11 funkce
// Display je Pointer (nije Structure)
Pointer XOpenDisplay(String displayName);
int XCloseDisplay(Pointer display);
long XDefaultRootWindow(Pointer display);
int XGetWindowAttributes(Pointer display, long window, XWindowAttributes attrs);
long XInternAtom(Pointer display, String atomName, boolean onlyIfExists);
int XQueryTree(Pointer display, long window,
PointerByReference root_return, PointerByReference parent_return,
PointerByReference children_return, IntByReference nchildren_return);
int XFetchName(Pointer display, long window, PointerByReference window_name_return);
Pointer XFree(Pointer ptr);
}
/**
* Najde okno podle jeho názvu a vrátí jeho bounds
* @param searchPattern Pattern pro hledání (substring názvu okna)
* @return Rectangle s pozicí a velikostí okna, nebo null pokud nenalezeno
*/
public static synchronized Rectangle findWindowByName(String searchPattern) {
Pointer display = null;
try {
// Použít cachedDisplay pokud existuje, nebo otevřít nový
if (cachedDisplay != null) {
display = cachedDisplay;
System.out.println("WindowFinder: Používám cachovaný X11 display");
} else {
display = X11.INSTANCE.XOpenDisplay(null);
if (display == null) {
System.out.println("WindowFinder: Nelze se připojit k X11 displei");
return null;
}
cachedDisplay = display;
System.out.println("WindowFinder: Připojen k X11 displayu a uložen v cache");
}
long rootWindow = X11.INSTANCE.XDefaultRootWindow(display);
long[] foundWindow = new long[1];
foundWindow[0] = 0;
searchWindowRecursive(display, rootWindow, searchPattern, foundWindow);
if (foundWindow[0] != 0) {
Rectangle bounds = getWindowBounds(display, foundWindow[0]);
if (bounds != null) {
System.out.println("WindowFinder: Okno nalezeno: " + bounds);
return bounds;
}
}
System.out.println("WindowFinder: Okno s názvem '" + searchPattern + "' nenalezeno");
return null;
} catch (Exception e) {
System.err.println("WindowFinder - Chyba: " + e.getMessage());
e.printStackTrace();
// Pokud dojde k chybě, vynulovat cache a zavřít display
if (display != null && display == cachedDisplay) {
try {
X11.INSTANCE.XCloseDisplay(display);
cachedDisplay = null;
} catch (Exception ex) {
System.err.println("WindowFinder - Chyba při zavírání displeje: " + ex.getMessage());
}
}
return null;
}
}
/**
* Rekurzivně hledá okno v stromě oken
*/
private static void searchWindowRecursive(Pointer display, long window,
String searchPattern, long[] result) {
if (result[0] != 0) {
return; // Okno již nalezeno
}
try {
String windowName = getWindowNameString(display, window);
if (windowName != null && windowName.contains(searchPattern)) {
result[0] = window;
System.out.println("WindowFinder: Nalezeno okno: '" + windowName + "' (XID: " + window + ")");
return;
}
} catch (Exception e) {
// Ignorovat chyby při čtení jména okna
}
// Hledat v potomcích
PointerByReference children_return = new PointerByReference();
IntByReference nchildren_return = new IntByReference();
try {
PointerByReference root_return = new PointerByReference();
PointerByReference parent_return = new PointerByReference();
int status = X11.INSTANCE.XQueryTree(display, window, root_return, parent_return,
children_return, nchildren_return);
if (status != 0 && nchildren_return.getValue() > 0) {
Pointer childrenPtr = children_return.getValue();
int numChildren = nchildren_return.getValue();
if (childrenPtr != null) {
// Čtení window IDs z pole
for (int i = 0; i < numChildren && result[0] == 0; i++) {
long childWindow = childrenPtr.getLong((long) i * 8); // 64-bit window IDs
if (childWindow != 0) {
searchWindowRecursive(display, childWindow, searchPattern, result);
}
}
// Don't call XFree - it can cause heap corruption
// X11.INSTANCE.XFree(childrenPtr);
}
}
} catch (Exception e) {
// Ignorovat chyby při hledání v potomcích
}
}
/**
* Získá jméno okna jako String
*/
private static String getWindowNameString(Pointer display, long window) {
PointerByReference namePtr = new PointerByReference();
try {
int status = X11.INSTANCE.XFetchName(display, window, namePtr);
if (status != 0) {
Pointer actualNamePtr = namePtr.getValue();
if (actualNamePtr != null) {
String name = actualNamePtr.getString(0);
// Don't call XFree - it can cause heap corruption
// X11.INSTANCE.XFree(actualNamePtr);
return (name != null && !name.isEmpty()) ? name : null;
}
}
} catch (Exception e) {
// Ignorovat chyby
}
return null;
}
/**
* Získá bounds okna (pozici a velikost)
*/
private static Rectangle getWindowBounds(Pointer display, long window) {
try {
X11.XWindowAttributes attrs = new X11.XWindowAttributes();
int status = X11.INSTANCE.XGetWindowAttributes(display, window, attrs);
if (status == 0) {
System.err.println("WindowFinder - Chyba při získávání window attributes");
return null;
}
System.out.println("WindowFinder: Window bounds - x:" + attrs.x + " y:" + attrs.y +
" w:" + attrs.width + " h:" + attrs.height);
return new Rectangle(attrs.x, attrs.y, attrs.width, attrs.height);
} catch (Exception e) {
System.err.println("WindowFinder - Chyba při získávání bounds: " + e.getMessage());
e.printStackTrace();
}
return null;
}
/**
* Ukončí X11 display connection a vyčistí cache
* Volat při shutdown aplikace
*/
public static synchronized void cleanup() {
if (cachedDisplay != null) {
try {
X11.INSTANCE.XCloseDisplay(cachedDisplay);
System.out.println("WindowFinder: X11 display uzavřen a cache vyčištěn");
} catch (Exception e) {
System.err.println("WindowFinder - Chyba při zavírání displeje: " + e.getMessage());
} finally {
cachedDisplay = null;
}
}
}
}