More updates to itb (wip)
This commit is contained in:
144
tools/itb/bin/itb_cursor
Normal file
144
tools/itb/bin/itb_cursor
Normal file
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import argparse
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.common.action_chains import ActionChains
|
||||
from selenium.webdriver.common.actions.action_builder import ActionBuilder
|
||||
from selenium.webdriver.common.actions.mouse_button import MouseButton
|
||||
|
||||
class CursorController:
|
||||
def __init__(self, debug_port: int, verbose: bool = False):
|
||||
"""Initialize connection to Chrome instance"""
|
||||
self.verbose = verbose
|
||||
options = webdriver.ChromeOptions()
|
||||
options.add_experimental_option("debuggerAddress", f"127.0.0.1:{debug_port}")
|
||||
self.driver = webdriver.Chrome(options=options)
|
||||
|
||||
# Load JavaScript files
|
||||
js_dir = os.path.join(os.path.dirname(__file__), "..", "js")
|
||||
self.js_code = ""
|
||||
for filename in ["viewport.js", "mouse.js"]:
|
||||
with open(os.path.join(js_dir, filename)) as f:
|
||||
self.js_code += f.read() + "\n"
|
||||
|
||||
def log(self, msg: str):
|
||||
"""Log message if verbose mode is enabled"""
|
||||
if self.verbose:
|
||||
print(f"DEBUG: {msg}", file=sys.stderr)
|
||||
|
||||
def get_viewport_info(self):
|
||||
"""Get viewport dimensions and scroll position"""
|
||||
return self.driver.execute_script(self.js_code + "; return getViewportInfo();")
|
||||
|
||||
def get_mouse_position(self):
|
||||
"""Get current mouse coordinates"""
|
||||
return self.driver.execute_script(self.js_code + "; return getMousePosition();")
|
||||
|
||||
def scroll_to_target(self, x: int, y: int):
|
||||
"""Scroll viewport to make target coordinates visible"""
|
||||
viewport = self.get_viewport_info()
|
||||
self.driver.execute_script(
|
||||
"window.scrollTo(arguments[0], arguments[1]);",
|
||||
max(0, x - viewport['width'] // 2),
|
||||
max(0, y - viewport['height'] // 2)
|
||||
)
|
||||
time.sleep(0.5) # Wait for scroll to complete
|
||||
|
||||
def get_element_at_position(self, x: int, y: int):
|
||||
"""Get the element at specified coordinates"""
|
||||
return self.driver.execute_script(
|
||||
self.js_code + "; return document.elementFromPoint(arguments[0], arguments[1]);",
|
||||
x, y
|
||||
)
|
||||
|
||||
def move_cursor(self, x: int, y: int, perform_click: bool = False,
|
||||
double_click: bool = False, right_click: bool = False,
|
||||
element_id: str = None) -> bool:
|
||||
"""Move cursor to specified coordinates and optionally perform click actions"""
|
||||
try:
|
||||
self.log(f"Moving cursor to ({x}, {y})")
|
||||
|
||||
if element_id:
|
||||
# Try to find element by ID first
|
||||
try:
|
||||
element = self.driver.find_element(By.ID, element_id)
|
||||
self.log(f"Found element by ID: {element_id}")
|
||||
except:
|
||||
self.log(f"Failed to find element by ID: {element_id}")
|
||||
return False
|
||||
else:
|
||||
# Scroll to make target visible
|
||||
self.scroll_to_target(x, y)
|
||||
|
||||
# Get element at position
|
||||
element = self.get_element_at_position(x, y)
|
||||
if not element:
|
||||
self.log(f"No element found at coordinates ({x}, {y})")
|
||||
return False
|
||||
|
||||
# Create action chain
|
||||
actions = ActionChains(self.driver)
|
||||
|
||||
if element_id:
|
||||
actions.move_to_element(element)
|
||||
else:
|
||||
actions.move_by_offset(x, y)
|
||||
|
||||
if perform_click:
|
||||
actions.click()
|
||||
elif double_click:
|
||||
actions.double_click()
|
||||
elif right_click:
|
||||
actions.context_click()
|
||||
|
||||
# Execute the action chain
|
||||
actions.perform()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.log(f"Error moving cursor: {str(e)}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Control mouse cursor in Chrome')
|
||||
parser.add_argument('debug_port', type=int, help='Chrome debug port')
|
||||
parser.add_argument('-x', type=int, help='Target X coordinate')
|
||||
parser.add_argument('-y', type=int, help='Target Y coordinate')
|
||||
parser.add_argument('--click', action='store_true', help='Perform click')
|
||||
parser.add_argument('--double', action='store_true', help='Perform double click')
|
||||
parser.add_argument('--right', action='store_true', help='Perform right click')
|
||||
parser.add_argument('--id', help='Element ID to target')
|
||||
parser.add_argument('-v', '--verbose', action='store_true', help='Enable debug output')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.double and args.right:
|
||||
print("Cannot specify both --double and --right", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if not args.id and (args.x is None or args.y is None):
|
||||
print("Must specify either element ID or both x and y coordinates", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
controller = CursorController(args.debug_port, args.verbose)
|
||||
success = controller.move_cursor(
|
||||
x=args.x if args.x is not None else 0,
|
||||
y=args.y if args.y is not None else 0,
|
||||
perform_click=args.click,
|
||||
double_click=args.double,
|
||||
right_click=args.right,
|
||||
element_id=args.id
|
||||
)
|
||||
sys.exit(0 if success else 1)
|
||||
|
||||
except Exception as e:
|
||||
if args.verbose:
|
||||
print(f"Fatal error: {str(e)}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user