diff --git a/skills/ui-query/.gitignore b/skills/ui-query/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/skills/ui-query/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/skills/ui-query/scripts/common.py b/skills/ui-query/scripts/common.py new file mode 100644 index 0000000..433e137 --- /dev/null +++ b/skills/ui-query/scripts/common.py @@ -0,0 +1,69 @@ +"""Shared utilities for ui-query scripts.""" + +import pyatspi + + +def find_windows(pattern=None, app_name=None, all_windows=False): + """Find windows matching criteria. + + Args: + pattern: Substring to match in window name (case-insensitive) + app_name: Substring to match in application name (case-insensitive) + all_windows: If True, return all windows (ignore pattern/app_name) + + Returns: + List of dicts with 'app', 'window', and 'accessible' keys + """ + desktop = pyatspi.Registry.getDesktop(0) + matches = [] + + for i in range(desktop.childCount): + app = desktop.getChildAtIndex(i) + if not app: + continue + + # Filter by app name + if app_name and app_name.lower() not in (app.name or "").lower(): + continue + + for j in range(app.childCount): + window = app.getChildAtIndex(j) + if not window: + continue + + window_name = window.name or "" + + # Filter by window pattern + if pattern and pattern.lower() not in window_name.lower(): + continue + + # Include if: all_windows requested, or any filter was specified + if all_windows or pattern or app_name: + matches.append({ + "app": app.name or "(unnamed)", + "window": window_name, + "accessible": window, + }) + + return matches + + +def get_all_windows(): + """Get all windows without filtering. + + Returns: + List of dicts with 'accessible' key (minimal info for searching) + """ + desktop = pyatspi.Registry.getDesktop(0) + windows = [] + + for i in range(desktop.childCount): + app = desktop.getChildAtIndex(i) + if not app: + continue + for j in range(app.childCount): + window = app.getChildAtIndex(j) + if window: + windows.append({"accessible": window}) + + return windows diff --git a/skills/ui-query/scripts/find-element.py b/skills/ui-query/scripts/find-element.py index 7819b8c..52de658 100755 --- a/skills/ui-query/scripts/find-element.py +++ b/skills/ui-query/scripts/find-element.py @@ -22,6 +22,8 @@ import sys import pyatspi +from common import find_windows, get_all_windows + def get_element_info(element): """Extract useful info from an accessible element.""" @@ -126,37 +128,6 @@ def find_elements(accessible, role=None, name=None, results=None, max_depth=15, return results -def find_windows(pattern=None, app_name=None): - """Find windows matching criteria.""" - desktop = pyatspi.Registry.getDesktop(0) - matches = [] - - for i in range(desktop.childCount): - app = desktop.getChildAtIndex(i) - if not app: - continue - - if app_name and app_name.lower() not in (app.name or "").lower(): - continue - - for j in range(app.childCount): - window = app.getChildAtIndex(j) - if not window: - continue - - window_name = window.name or "" - if pattern and pattern.lower() not in window_name.lower(): - continue - - matches.append({ - "app": app.name or "(unnamed)", - "window": window_name, - "accessible": window, - }) - - return matches - - def print_results(results, as_json=False): """Print search results.""" if as_json: @@ -206,7 +177,7 @@ def main(): sys.exit(1) try: - windows = find_windows(args.window, args.app) + windows = find_windows(pattern=args.window, app_name=args.app) except Exception as e: print(f"Error accessing AT-SPI: {e}", file=sys.stderr) sys.exit(1) @@ -216,15 +187,7 @@ def main(): print("No matching windows found.", file=sys.stderr) else: # Search all windows - desktop = pyatspi.Registry.getDesktop(0) - windows = [] - for i in range(desktop.childCount): - app = desktop.getChildAtIndex(i) - if app: - for j in range(app.childCount): - win = app.getChildAtIndex(j) - if win: - windows.append({"accessible": win}) + windows = get_all_windows() all_results = [] for win in windows: diff --git a/skills/ui-query/scripts/get-text.py b/skills/ui-query/scripts/get-text.py index 02db446..458c570 100755 --- a/skills/ui-query/scripts/get-text.py +++ b/skills/ui-query/scripts/get-text.py @@ -21,6 +21,8 @@ from dataclasses import dataclass, field import pyatspi +from common import find_windows + @dataclass class TextNode: @@ -118,41 +120,6 @@ def flatten_text(node, indent=0): return lines -def find_windows(pattern=None, app_name=None, all_windows=False): - """Find windows matching criteria.""" - desktop = pyatspi.Registry.getDesktop(0) - matches = [] - - for i in range(desktop.childCount): - app = desktop.getChildAtIndex(i) - if not app: - continue - - # Filter by app name - if app_name and app_name.lower() not in (app.name or "").lower(): - continue - - for j in range(app.childCount): - window = app.getChildAtIndex(j) - if not window: - continue - - window_name = window.name or "" - - # Filter by window pattern - if pattern and pattern.lower() not in window_name.lower(): - continue - - if all_windows or pattern or app_name: - matches.append({ - "app": app.name or "(unnamed)", - "window": window_name, - "accessible": window, - }) - - return matches - - def main(): parser = argparse.ArgumentParser(description="Extract text from AT-SPI windows") parser.add_argument("pattern", nargs="?", help="Window name pattern to match")