Skip to content

Commit

Permalink
Removal of XdoTool Requirement
Browse files Browse the repository at this point in the history
  • Loading branch information
Vinyzu committed Apr 8, 2024
1 parent 0a6ee30 commit cd49390
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 27 deletions.
50 changes: 31 additions & 19 deletions cdp_patches/input/os_base/linux.py
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
numpy==1.26.4
pywinauto==0.6.8
python-libxdo==0.1.2a1
python-xlib==0.33
11 changes: 5 additions & 6 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ packages = find:
install_requires =
numpy
pywinauto; platform_system=='Windows'
python-libxdo; platform_system=='Linux'
python-xlib; platform_system=='Linux'


Expand All @@ -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
Expand All @@ -47,8 +51,3 @@ testing =
botright
selenium
selenium_driverless
automation_linting =
playwright
botright
selenium
selenium_driverless
2 changes: 1 addition & 1 deletion tests/test_async_selenium.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down

0 comments on commit cd49390

Please sign in to comment.