From cd49390826be51ed791bbb5a993dd2996fbf8a37 Mon Sep 17 00:00:00 2001 From: Vinyzu <50874994+Vinyzu@users.noreply.github.com> Date: Mon, 8 Apr 2024 18:31:36 +0200 Subject: [PATCH] Removal of XdoTool Requirement --- cdp_patches/input/os_base/linux.py | 50 ++++++++++++++++++------------ requirements.txt | 1 - setup.cfg | 11 +++---- tests/test_async_selenium.py | 2 +- 4 files changed, 37 insertions(+), 27 deletions(-) diff --git a/cdp_patches/input/os_base/linux.py b/cdp_patches/input/os_base/linux.py index 454a10e..e144bf5 100644 --- a/cdp_patches/input/os_base/linux.py +++ b/cdp_patches/input/os_base/linux.py @@ -1,16 +1,13 @@ import re import subprocess import time -from typing import Any, Literal, Tuple +from typing import Any, List, Literal, Tuple -from xdo import Xdo from Xlib import X, display from Xlib.ext.xtest import fake_input from Xlib.XK import string_to_keysym from Xlib.xobject.drawable import Window -xdo = Xdo() - symbol_dict = { # Every Common Symbol on a QWERTY Keyboard, Source: https://github.com/python-xlib/python-xlib/blob/4e8bbf8fc4941e5da301a8b3db8d27e98de68666/Xlib/keysymdef/latin1.py "+": "plus", "-": "minus", @@ -93,30 +90,45 @@ def __init__(self, pid: int, scale_factor: float) -> None: self.browser_window = self.display.create_resource_object("window", self.tab_pid) def get_window(self) -> Any: - res_windows = xdo.search_windows(pid=self.pid) + name_atom = self.display.get_atom("WM_NAME", only_if_exists=True) + pid_atom = self.display.get_atom("_NET_WM_PID", only_if_exists=True) + res_windows: List[Window] = [] + + # Getting all WindowIds by PID by recursively searching through all windows under the root window query tree + def search_windows_by_pid(query_tree, pid: int): + for window in query_tree.children: + window_pid = window.get_property(pid_atom, 0, 0, pow(2, 32) - 1) + if window_pid and window_pid.value[0] == pid: + res_windows.append(window) + if window.query_tree().children: + search_windows_by_pid(window.query_tree(), pid) + + search_windows_by_pid(self.display.screen().root.query_tree(), self.pid) for window in res_windows: - window_size = xdo.get_window_size(window) - title = xdo.get_window_name(window) + # Getting necessary window properties + title = window.get_property(name_atom, 0, 0, pow(2, 32) - 1) + parent_offset_coords = window.translate_coords(window.query_tree().parent, 0, 0) + window_x, window_y = parent_offset_coords.x, parent_offset_coords.y - # Filtering out helper windows (like the taskbar) - if (title == b"google-chrome") or (window_size.width == window_size.height): + # Filter out non-browser windows, for example the Taskbar or Info Bars + if (title == b"google-chrome") or (title == b"chrome") or (window_x == window_y) or not all((window_x, window_y)): continue - return window + self.browser_window = window + return self.browser_window raise ValueError("No browser window found") def _offset_toolbar_height(self) -> Tuple[int, int]: - window_location = xdo.get_window_location(self.tab_pid) + # Get Window Location + root_offset_coords = self.browser_window.translate_coords(self.browser_window.query_tree().root, 0, 0) + parent_offset_coords = self.browser_window.translate_coords(self.browser_window.query_tree().parent, 0, 0) + window_x = abs(root_offset_coords.x) + abs(parent_offset_coords.x) + window_y = abs(root_offset_coords.y) + abs(parent_offset_coords.y) # Get Chrome Toolbar Height (Note: Fetching Chromes "Program Specified Minimum Size" - 1 for Chrome Toolbar Height + 1 for the Minimum Tab Size) - normal_hints_atom = self.display.get_atom("WM_NORMAL_HINTS", only_if_exists=True) - if normal_hints_atom == X.NONE: - raise ValueError('No Atom interned with the Name "WM_NORMAL_HINTS".') - - normal_hints = self.browser_window.get_property(normal_hints_atom, 0, 0, pow(2, 32) - 1) - chrome_toolbar_height = normal_hints.value[6] - 1 + chrome_toolbar_height = self.browser_window.get_wm_normal_hints().min_height - 1 # Get Linux (Outer) Window Toolbar Height frame_extends_atom = self.display.get_atom("_NET_FRAME_EXTENTS", only_if_exists=True) @@ -127,8 +139,8 @@ def _offset_toolbar_height(self) -> Tuple[int, int]: window_toolbar_height = net_frame_extends.value[2] window_toolbar_width = net_frame_extends.value[3] - offset_width: int = window_location.x - window_toolbar_width - offset_height: int = window_location.y - window_toolbar_height + chrome_toolbar_height + offset_width: int = window_x - window_toolbar_width + offset_height: int = window_y - window_toolbar_height + chrome_toolbar_height return offset_width, offset_height @staticmethod diff --git a/requirements.txt b/requirements.txt index 9a14642..b5381cf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ numpy==1.26.4 pywinauto==0.6.8 -python-libxdo==0.1.2a1 python-xlib==0.33 \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 669c568..c53b6a6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,7 +26,6 @@ packages = find: install_requires = numpy pywinauto; platform_system=='Windows' - python-libxdo; platform_system=='Linux' python-xlib; platform_system=='Linux' @@ -38,6 +37,11 @@ include = cdp_patches, cdp_patches.*, LICENSE exclude = tests, .github [options.extras_require] +automation_linting = + playwright + botright + selenium + selenium_driverless testing = pytest mypy @@ -47,8 +51,3 @@ testing = botright selenium selenium_driverless -automation_linting = - playwright - botright - selenium - selenium_driverless diff --git a/tests/test_async_selenium.py b/tests/test_async_selenium.py index d170365..805427b 100644 --- a/tests/test_async_selenium.py +++ b/tests/test_async_selenium.py @@ -42,7 +42,7 @@ async def test_input_leak(async_driver: Chrome, server: Server) -> None: x, y = await get_locator_pos(sync_locator) await async_driver.async_input.click("left", x, y) # type: ignore[attr-defined] - is_leaking = await async_driver.eval_async("return await window.is_leaking", timeout=10) + is_leaking = await async_driver.eval_async("return await window.is_leaking", timeout=30) assert not is_leaking