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