Files
gnome-jarvis/computer-use/py/atspi_action.py
T
snxraven 599bfe440d
Rolling release / release (push) Failing after 1m46s
Computer Use Updates
2026-09-13 19:30:17 -04:00

75 lines
2.5 KiB
Python

#!/usr/bin/env python3
import json
import sys
import gi
gi.require_version('Atspi', '2.0')
from gi.repository import Atspi
target, action = json.loads(sys.argv[1]), sys.argv[2]
Atspi.init()
desktop = Atspi.get_desktop(0)
found = None
wanted_name = target.get('name') or ''
wanted_role = target.get('role') or ''
wanted_rect = target.get('raw_rect') or target.get('rect') or None
def rect_close(node):
if not wanted_rect or len(wanted_rect) < 4:
return True
try:
component = node.get_component()
if not component:
return True
extents = component.get_extents(Atspi.CoordType.SCREEN)
return abs(extents.x - wanted_rect[0]) < 8 and abs(extents.y - wanted_rect[1]) < 8
except Exception:
return True
def walk(node, depth=0):
global found
if found or node is None or depth > 30:
return
try:
if (node.get_name() or '') == wanted_name and (node.get_role_name() or '') == wanted_role and rect_close(node):
found = node
return
for i in range(node.get_child_count()):
walk(node.get_child_at_index(i), depth + 1)
except Exception:
return
if target.get('pid') is not None and target.get('atspi_path') is not None:
for i in range(desktop.get_child_count()):
app = desktop.get_child_at_index(i)
if app.get_process_id() != target['pid']:
continue
candidate = app
for index in target['atspi_path']:
candidate = candidate.get_child_at_index(index)
if candidate is None:
break
if candidate and (candidate.get_name() or '') == wanted_name and (candidate.get_role_name() or '') == wanted_role and rect_close(candidate):
found = candidate
break
else:
walk(desktop)
if not found:
raise SystemExit('AT-SPI target not found')
actions = found.get_action()
if actions is None:
raise SystemExit(f'AT-SPI action unavailable: {action}')
aliases = [action.lower()]
if action.lower() in ('click', 'press', 'activate'):
aliases.extend(['press', 'click', 'activate'])
seen = set()
for name in aliases:
if name in seen:
continue
seen.add(name)
for i in range(actions.get_n_actions()):
if actions.get_action_name(i).lower() == name:
if actions.do_action(i):
print(json.dumps({'ok': True, 'action': name}))
raise SystemExit(0)
raise SystemExit(f'AT-SPI action unavailable: {action}')