265 lines
10 KiB
Java
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;
|
|
}
|
|
}
|
|
}
|
|
}
|