diff --git a/.github/workflows/lint-and-build.yml b/.github/workflows/lint-and-build.yml
index 5f166411..7f5cccd3 100644
--- a/.github/workflows/lint-and-build.yml
+++ b/.github/workflows/lint-and-build.yml
@@ -80,7 +80,8 @@ jobs:
# Per-package no-binary overrides no-build, allowing
# known pure-Python source-only dependencies to still build.
# The Build job intentionally builds some binary packages from source.
- UV_NO_BINARY_PACKAGE: keyboard PyAutoGUI beslogic-ruff-config
+ # python-xlib: https://github.com/python-xlib/python-xlib/pull/298
+ UV_NO_BINARY_PACKAGE: keyboard PyAutoGUI beslogic-ruff-config python-xlib
strategy:
fail-fast: false
# Pyright is version and platform sensible
diff --git a/README.md b/README.md
index 36bff735..7ee7eea2 100644
--- a/README.md
+++ b/README.md
@@ -120,6 +120,8 @@ Not a developer? You can still help through the following methods:
- Sharing AutoSplit with other speedrunners
- Starring the repository
- Upvoting 👍 the following upstream issues in libraries and tools we use:
+ -
+ -
-
-
-
diff --git a/docs/tutorial.md b/docs/tutorial.md
index db71feb3..6da52037 100644
--- a/docs/tutorial.md
+++ b/docs/tutorial.md
@@ -75,9 +75,9 @@
##### Linux
-- **X11 XCB** (slower, requires XCB)\
- Uses the XCB library to take screenshots of the X11 server.
-- **Scrot** (fast, may leave files in `/tmp`)\
+- **X11** (fastest)\
+ Request screenshots directly from the X11 server.
+- **Scrot** (slow, may leave files in `/tmp`)\
Uses Scrot (SCReenshOT) to take screenshots.\
Leaves behind a screenshot file in `/tmp` if interrupted.
diff --git a/pyproject.toml b/pyproject.toml
index 753ca720..3e6cc86d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -42,7 +42,6 @@ dependencies = [
"winrt-Windows.Graphics.Imaging >=3.2.1; sys_platform == 'win32'",
#
# Linux-only dependencies
- "pillow >=13.0.0; sys_platform == 'linux'",
"python-xlib >=0.33; sys_platform == 'linux'",
]
[dependency-groups]
@@ -66,7 +65,6 @@ dev = [
"types-pywin32 >=306.0.0.20240130; sys_platform == 'win32'",
#
# OS-specific, but installable on other OSes for convenient typing references
- "pillow >=13.0.0",
"pygrabber",
"python-xlib",
"typed-D3DShot[numpy]",
@@ -98,21 +96,25 @@ override-dependencies = [
# https://github.com/opencv/opencv-python/issues/1201
"numpy >=2.5.2",
]
+# python-xlib's setup.py imports pkg_resources, removed in setuptools 81
+# https://github.com/python-xlib/python-xlib/issues/286
+build-constraint-dependencies = ["setuptools <81"]
exclude-newer = "1 week"
[tool.uv.exclude-newer-package]
typed-D3DShot = false # I own it
-pillow = "6 day"
###
# Development channels
###
[tool.uv.sources]
beslogic-ruff-config = { git = "https://github.com/Beslogic/Beslogic-Ruff-Config", rev = "39c27278054fc428b54205581a0e23caca7ad77a" }
+# Quadratic receive buffer fix for large GetImage replies (X11 capture)
+# https://github.com/python-xlib/python-xlib/pull/298
+python-xlib = { git = "https://github.com/Avasam/python-xlib", branch = "fix-quadratic-recv" }
# pywin32 = { git = "https://github.com/mhammond/pywin32.git", marker = "python_version >= '3.15'" }
# pyinstaller = { git = "https://github.com/pyinstaller/pyinstaller.git", marker = "python_version >= '3.15'" }
# numpy = { index = "scientific-python-nightly-wheels", marker = "python_version >= '3.15'" }
-# pillow = { index = "scientific-python-nightly-wheels", marker = "python_version >= '3.15'" }
[[tool.uv.index]]
exclude-newer = false # Anaconda index doesn't have upload dates
diff --git a/scripts/build.ps1 b/scripts/build.ps1
index 7b53e9a4..56d73cff 100644
--- a/scripts/build.ps1
+++ b/scripts/build.ps1
@@ -118,7 +118,7 @@ try {
# Icon as PNG (freedesktop doesn't support .ico), converted from res/icon.ico.
# Not splash.png, which uses hard transparency for the Tcl/Tk splash.
New-Item -ItemType Directory -Path build/AppDir/usr/share/icons/hicolor/256x256/apps -Force | Out-Null
- uv run --active python -c "from PIL import Image; Image.open('res/icon.ico').save('build/AppDir/AutoSplit.png')"
+ uv run --active python -c "import sys; from PySide6.QtGui import QImage; sys.exit(not QImage('res/icon.ico').save('build/AppDir/AutoSplit.png'))"
# Top-level -> .DirIcon (file thumbnail); hicolor copy -> desktop integration (menu/taskbar).
Copy-Item build/AppDir/AutoSplit.png build/AppDir/usr/share/icons/hicolor/256x256/apps/AutoSplit.png
$date = Get-Date -Format 'yyyy-MM-dd'
diff --git a/src/capture_method/ScrotCaptureMethod.py b/src/capture_method/ScrotCaptureMethod.py
index 19420196..8088060d 100644
--- a/src/capture_method/ScrotCaptureMethod.py
+++ b/src/capture_method/ScrotCaptureMethod.py
@@ -43,7 +43,7 @@ def _scrot_screenshot(x: int, y: int, width: int, height: int):
class ScrotCaptureMethod(CaptureMethodBase):
name = "Scrot"
- short_description = "fast, may leave files in `/tmp`"
+ short_description = "slow, may leave files in `/tmp`"
description = """
Uses Scrot (SCReenshOT) to take screenshots.
Leaves behind a screenshot file in `/tmp` if interrupted."""
diff --git a/src/capture_method/X11CaptureMethod.py b/src/capture_method/X11CaptureMethod.py
new file mode 100644
index 00000000..d9e0cf18
--- /dev/null
+++ b/src/capture_method/X11CaptureMethod.py
@@ -0,0 +1,83 @@
+import sys
+
+if sys.platform != "linux":
+ raise OSError
+
+from typing import TYPE_CHECKING, override
+
+import cv2
+import numpy as np
+from pywinctl import getWindowsWithTitle
+from Xlib import X
+from Xlib.display import Display
+from Xlib.error import BadMatch, BadWindow
+
+from capture_method.CaptureMethodBase import CaptureMethodBase
+from utils import ALPHA_CHANNEL_COUNT, is_valid_image
+
+if TYPE_CHECKING:
+ from Xlib.protocol.request import GetImage
+
+ from AutoSplit import AutoSplit
+
+ALL_PLANES = 0xFFFFFFFF
+
+
+def _image_reply_to_bgr(reply: GetImage, width: int, height: int):
+ image = np.frombuffer(reply.data, dtype=np.uint8)
+ # X pads 24-bit depth to 32 bits per pixel (BGRX). The 4th byte is padding, not alpha.
+ # Other depths (16, 30) use different pixel layouts we don't decode, so return None.
+ if image.size != width * height * ALPHA_CHANNEL_COUNT:
+ return None
+ image = image.reshape((height, width, ALPHA_CHANNEL_COUNT))
+ if not is_valid_image(image):
+ return None
+ return cv2.cvtColor(image, cv2.COLOR_BGRA2BGR)
+
+
+class X11CaptureMethod(CaptureMethodBase):
+ name = "X11"
+ short_description = "fastest"
+ description = "\nRequests screenshots directly from the X11 server."
+
+ def __init__(self, autosplit: AutoSplit):
+ super().__init__(autosplit)
+ self._display = Display()
+
+ @override
+ def close(self):
+ self._display.close()
+
+ @override
+ def get_frame(self):
+ if not self.check_selected_region_exists():
+ return None
+
+ root = self._display.screen().root
+ try:
+ window_coords = root.translate_coords(self._autosplit_ref.hwnd, 0, 0)._data # noqa: SLF001
+ except BadWindow:
+ return None
+
+ selection = self._autosplit_ref.settings_dict["capture_region"]
+ try:
+ reply = root.get_image(
+ selection["x"] + window_coords["x"],
+ selection["y"] + window_coords["y"],
+ selection["width"],
+ selection["height"],
+ X.ZPixmap,
+ ALL_PLANES,
+ )
+ except BadMatch:
+ # Region is (partially) outside the screen
+ return None
+ return _image_reply_to_bgr(reply, selection["width"], selection["height"])
+
+ @override
+ def recover_window(self, captured_window_title: str):
+ windows = getWindowsWithTitle(captured_window_title)
+ if len(windows) == 0:
+ return False
+ self._autosplit_ref.hwnd = windows[0].getHandle()
+ return self.check_selected_region_exists()
diff --git a/src/capture_method/XcbCaptureMethod.py b/src/capture_method/XcbCaptureMethod.py
deleted file mode 100644
index 0cf3cf65..00000000
--- a/src/capture_method/XcbCaptureMethod.py
+++ /dev/null
@@ -1,80 +0,0 @@
-import sys
-
-if sys.platform != "linux":
- raise OSError
-
-from typing import TYPE_CHECKING, override
-
-import cv2
-import numpy as np
-from PIL import ImageGrab
-from pywinctl import getWindowsWithTitle
-from Xlib.display import Display
-from Xlib.error import BadWindow
-
-from capture_method.CaptureMethodBase import CaptureMethodBase
-from utils import is_valid_image
-
-if TYPE_CHECKING:
- from AutoSplit import AutoSplit
-
-
-class XcbCaptureMethod(CaptureMethodBase):
- name = "X11 XCB"
- short_description = "slower, requires XCB"
- description = "\nUses the XCB library to take screenshots of the X11 server."
-
- _xdisplay: str | None = "" # ":0"
-
- def __init__(self, autosplit: AutoSplit):
- super().__init__(autosplit)
- self._display = Display()
-
- @override
- def close(self):
- self._display.close()
-
- @override
- def get_frame(self):
- if not self.check_selected_region_exists():
- return None
-
- root = self._display.screen().root
- try:
- window_coords = root.translate_coords(self._autosplit_ref.hwnd, 0, 0)._data # noqa: SLF001
- except BadWindow:
- return None
- # image = window.get_image(
- # selection["x"],
- # selection["y"],
- # selection["width"],
- # selection["height"],
- # 1,
- # 0,
- # )
-
- selection = self._autosplit_ref.settings_dict["capture_region"]
- x = selection["x"] + window_coords["x"]
- y = selection["y"] + window_coords["y"]
- image = ImageGrab.grab(
- (
- x,
- y,
- x + selection["width"],
- y + selection["height"],
- ),
- # Passing a non-None xdisplay also prevents pillow falling back to a screenshot tool
- xdisplay=self._xdisplay,
- )
- image = np.array(image)
- if not is_valid_image(image):
- return None
- return cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
-
- @override
- def recover_window(self, captured_window_title: str):
- windows = getWindowsWithTitle(captured_window_title)
- if len(windows) == 0:
- return False
- self._autosplit_ref.hwnd = windows[0].getHandle()
- return self.check_selected_region_exists()
diff --git a/src/capture_method/__init__.py b/src/capture_method/__init__.py
index 9bc4b2a0..7c9cf4fa 100644
--- a/src/capture_method/__init__.py
+++ b/src/capture_method/__init__.py
@@ -32,10 +32,8 @@
if sys.platform == "linux":
import fcntl
- from PIL import features
-
from capture_method.ScrotCaptureMethod import IS_SCROT_SUPPORTED, ScrotCaptureMethod
- from capture_method.XcbCaptureMethod import XcbCaptureMethod
+ from capture_method.X11CaptureMethod import X11CaptureMethod
from vidioc_querycap import V4L2_CAP_VIDEO_CAPTURE, VIDIOC_QUERYCAP, v4l2_capability
@@ -82,7 +80,7 @@ def _generate_next_value_(
PRINTWINDOW_RENDERFULLCONTENT = auto()
DESKTOP_DUPLICATION = auto()
SCROT = auto()
- XCB = auto()
+ X11 = auto()
VIDEO_CAPTURE_DEVICE = auto()
@@ -138,8 +136,7 @@ def get(self, key: CaptureMethodEnum, default: object = None, /):
ForceFullContentRenderingCaptureMethod
)
elif sys.platform == "linux":
- if features.check_feature(feature="xcb"):
- CAPTURE_METHODS[CaptureMethodEnum.XCB] = XcbCaptureMethod
+ CAPTURE_METHODS[CaptureMethodEnum.X11] = X11CaptureMethod
if IS_SCROT_SUPPORTED:
CAPTURE_METHODS[CaptureMethodEnum.SCROT] = ScrotCaptureMethod
CAPTURE_METHODS[CaptureMethodEnum.VIDEO_CAPTURE_DEVICE] = VideoCaptureDeviceCaptureMethod
diff --git a/tests/test_import_all_modules.py b/tests/test_import_all_modules.py
index b69c7c4b..59b5f2d6 100644
--- a/tests/test_import_all_modules.py
+++ b/tests/test_import_all_modules.py
@@ -30,7 +30,7 @@
"linux": {
"capture_method.Screenshot using QT attempt",
"capture_method.ScrotCaptureMethod",
- "capture_method.XcbCaptureMethod",
+ "capture_method.X11CaptureMethod",
},
}
EXPECTED_OS_ERRORS = frozenset(
diff --git a/uv.lock b/uv.lock
index 46dc5def..4f57511f 100644
--- a/uv.lock
+++ b/uv.lock
@@ -19,11 +19,11 @@ exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for
exclude-newer-span = "P1W"
[options.exclude-newer-package]
-pillow = { timestamp = "0001-01-01T00:00:00Z", span = "P6D" }
typed-d3dshot = false
[manifest]
overrides = [{ name = "numpy", specifier = ">=2.5.2" }]
+build-constraints = [{ name = "setuptools", specifier = "<81" }]
[[manifest.dependency-metadata]]
name = "pyautogui"
@@ -88,7 +88,6 @@ dependencies = [
{ name = "opencv-contrib-python-headless", version = "4.11.0.86", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine == 'ARM64' and sys_platform == 'win32'" },
{ name = "opencv-contrib-python-headless", version = "5.0.0.93", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'ARM64' or sys_platform != 'win32'" },
{ name = "packaging" },
- { name = "pillow", marker = "sys_platform != 'win32'" },
{ name = "pyautogui" },
{ name = "pygrabber", marker = "sys_platform == 'win32'" },
{ name = "pyinstaller" },
@@ -113,7 +112,6 @@ dev = [
{ name = "beslogic-ruff-config" },
{ name = "dprint-py" },
{ name = "mypy" },
- { name = "pillow" },
{ name = "pygrabber" },
{ name = "pyright", extra = ["nodejs"] },
{ name = "python-xlib" },
@@ -139,12 +137,11 @@ requires-dist = [
{ name = "opencv-contrib-python-headless", marker = "platform_machine != 'ARM64' or sys_platform != 'win32'", specifier = ">=5.0" },
{ name = "opencv-contrib-python-headless", marker = "platform_machine == 'ARM64' and sys_platform == 'win32'", specifier = "==4.11.*" },
{ name = "packaging", specifier = ">=20.0" },
- { name = "pillow", marker = "sys_platform == 'linux'", specifier = ">=13.0.0" },
{ name = "pyautogui", specifier = ">=0.9.52" },
{ name = "pygrabber", marker = "sys_platform == 'win32'", specifier = ">=0.2" },
{ name = "pyinstaller", specifier = ">=6.21.0" },
{ name = "pyside6-essentials", specifier = ">=6.9.0" },
- { name = "python-xlib", marker = "sys_platform == 'linux'", specifier = ">=0.33" },
+ { name = "python-xlib", marker = "sys_platform == 'linux'", git = "https://github.com/Avasam/python-xlib?branch=fix-quadratic-recv" },
{ name = "pywin32", marker = "sys_platform == 'win32'", specifier = ">=312" },
{ name = "pywinctl", specifier = ">=0.0.42" },
{ name = "tomli-w", specifier = ">=1.1.0" },
@@ -164,10 +161,9 @@ dev = [
{ name = "beslogic-ruff-config", git = "https://github.com/Beslogic/Beslogic-Ruff-Config?rev=39c27278054fc428b54205581a0e23caca7ad77a" },
{ name = "dprint-py", specifier = ">=0.50.0.0" },
{ name = "mypy", specifier = ">=2.2" },
- { name = "pillow", specifier = ">=13.0.0" },
{ name = "pygrabber" },
{ name = "pyright", extras = ["nodejs"], specifier = ">=1.1.400" },
- { name = "python-xlib" },
+ { name = "python-xlib", git = "https://github.com/Avasam/python-xlib?branch=fix-quadratic-recv" },
{ name = "ruff" },
{ name = "typed-d3dshot", extras = ["numpy"] },
{ name = "types-keyboard" },
@@ -498,48 +494,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/54/16/12b82f791c7f50ddec566873d5bdd245baa1491bac11d15ffb98aecc8f8b/pefile-2024.8.26-py3-none-any.whl", hash = "sha256:76f8b485dcd3b1bb8166f1128d395fa3d87af26360c2358fb75b80019b957c6f", size = 74766, upload-time = "2024-08-26T21:01:02.632Z" },
]
-[[package]]
-name = "pillow"
-version = "13.0.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ce/a4/9cc30690a394a8305e8be4c9036aa16d0dfddadd09079860b582f562496d/pillow-13.0.0.tar.gz", hash = "sha256:cdcd1ba258178f7356d74170c9a81ba4eab6a5d55748bc402a5a2a6fe6be3510", size = 47058631, upload-time = "2026-10-16T22:18:09.718Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/8e/45/fbad9c7f5e2f1745c373a434d46072bc84eddfefaa28b852425ac957fda6/pillow-13.0.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:44629d4498c9cb6b2051806155f22141df0d8a929c56134b166655232dfb2f8c", size = 4190721, upload-time = "2026-10-16T22:16:27.524Z" },
- { url = "https://files.pythonhosted.org/packages/a2/bf/2074902a259a90692c5dbf132885d21671644cbbd3f690047516243a55db/pillow-13.0.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:bf97ba1b5542050e9b0c74e632323b6d8136cc99d64e193abebee22fe6013c76", size = 4290236, upload-time = "2026-10-16T22:16:29.562Z" },
- { url = "https://files.pythonhosted.org/packages/ff/a5/3d1bb93b8ea38d15d21356c1b8e9f50971ac592aaa248f7fb71739094c0e/pillow-13.0.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:aa694c49787040943570d61c9b8fc01271bd2a2cfdd55f0d896f8aecdce1a981", size = 3723093, upload-time = "2026-10-16T22:16:31.249Z" },
- { url = "https://files.pythonhosted.org/packages/83/16/b603274617b3e6728da3a0133d4fba1ea8d7fbe0c4ea5fef9cbffa398b8b/pillow-13.0.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5933fcea5a5ba64c686f55c886a115cbfd92f2900460784a1e79909fdbafd7b6", size = 6516058, upload-time = "2026-10-16T22:16:37.228Z" },
- { url = "https://files.pythonhosted.org/packages/1d/f2/1371707ce438ba9a43f9d24089828494ec6f9396e28cd7903aa704dd6306/pillow-13.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e91c2b1cbeae768c98752e67e717e283d68dae45717b41f2d91eaee648e37b7d", size = 7142574, upload-time = "2026-10-16T22:16:39.172Z" },
- { url = "https://files.pythonhosted.org/packages/b9/a9/62cf68587f910bc87c5951bcb6e9430c7643a65076adf5f18b4bf350e2bd/pillow-13.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f6299e494e46253d40c34653cac369412f44ca53b5f85d848d3f95b1ed0546c", size = 6614295, upload-time = "2026-10-16T22:16:41.117Z" },
- { url = "https://files.pythonhosted.org/packages/6c/c9/9e9b48178649730299be0bafbf5f810a5b6977538f5a92d47e4ba5f5d608/pillow-13.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f9bd7d260d986f3553518c5d723544346fa48031f8aef3265d786aeb59845bc0", size = 7224841, upload-time = "2026-10-16T22:16:43.118Z" },
- { url = "https://files.pythonhosted.org/packages/8f/48/c44e91384b1b6c072a714a63d00608acc16a6a968735e478e4be4384e5ac/pillow-13.0.0-cp314-cp314-win32.whl", hash = "sha256:2e31c4e68cffd7b1f116127626813b82fcba19edf6b940a7b0c266d6484d0055", size = 6508694, upload-time = "2026-10-16T22:16:45.883Z" },
- { url = "https://files.pythonhosted.org/packages/50/e2/7ad17747517e4c813dca78257d6bb593466f8231baf1384bc98d92745edb/pillow-13.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:f7cbb7a64e7ec664775195e4f81923bc8a93d9451b963f844e59118f63147a7a", size = 7281830, upload-time = "2026-10-16T22:16:47.725Z" },
- { url = "https://files.pythonhosted.org/packages/41/9f/e5b51f2ce8ef4563215334035403ce61a244f93a2e65c24a940131618f87/pillow-13.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:a6aaa7871aebca081eb5c45f83e845bbd9712ae99fa9286dbfdcee835e63f4b4", size = 2695468, upload-time = "2026-10-16T22:16:49.515Z" },
- { url = "https://files.pythonhosted.org/packages/c9/52/43ded0ac08e77e39d17bd6d894162572201b5e8fdc4a022b14c55a1960a4/pillow-13.0.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e8ca8f69b7c77fa96dad99f8638c1fc4f1c0bed4acaa1baf7a3c31bdb80a0ab1", size = 6607008, upload-time = "2026-10-16T22:16:55.525Z" },
- { url = "https://files.pythonhosted.org/packages/0d/2e/4a57ed216d5bf877387cba4d44c02aa87abe51ad2e06404aa6b597c4da39/pillow-13.0.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6bce6a29c3898191f4c9998e4d4e8f69790caeabde46e54da4bc235ce5bde683", size = 7224281, upload-time = "2026-10-16T22:16:57.738Z" },
- { url = "https://files.pythonhosted.org/packages/5c/3d/2929b248e135752d63aaeabf53f6de797e905b7b6263f9c1e2adade20aba/pillow-13.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:816752a5ef76a80484c8c441c85593b7ea9304f6231fc5776ad1fbff7e773058", size = 6695600, upload-time = "2026-10-16T22:16:59.842Z" },
- { url = "https://files.pythonhosted.org/packages/bc/33/fb0da992cf084ed1ee6ee05e96b6191e35c351eb707ca2df874feb24ce47/pillow-13.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5bdb38ba220b61b4ec31dd1046dea6b016d3c6850a23f43ed8b58332b2fc668e", size = 7300589, upload-time = "2026-10-16T22:17:01.686Z" },
- { url = "https://files.pythonhosted.org/packages/bb/af/c30dd9ef6a3dce84ecb1b2393cac629047a32e66279056c6cb46c3ffd65c/pillow-13.0.0-cp314-cp314t-win32.whl", hash = "sha256:06903aac074d00c988e77a1fc435286dc498506d4ca8d6f69df9babddf4ecc15", size = 6517204, upload-time = "2026-10-16T22:17:03.722Z" },
- { url = "https://files.pythonhosted.org/packages/85/d5/ef3a8ae7609c8ab26570abf18194882534b480d245a9148b895a2262c004/pillow-13.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9a635068d9793c7f0d929f39f18b58f1bd86c236e8e7b64167b86af9232ebca0", size = 7286695, upload-time = "2026-10-16T22:17:05.543Z" },
- { url = "https://files.pythonhosted.org/packages/84/3c/0861db0c69a03689ebe3e7599d35668a414deb5aaafd8255215e4e62e3d3/pillow-13.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0d1a9cf7bc6917538b20f8d06b9ab2147e542f68849e96fc58eb66bdf6acc692", size = 2698098, upload-time = "2026-10-16T22:17:07.674Z" },
- { url = "https://files.pythonhosted.org/packages/6e/eb/e86a41962a3a31ad495a3d6a912d9002b01bba4a6fa71c28703abdca7f69/pillow-13.0.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7d5e097eabd5d4915c27ac9f6e9325bb1e01a7c94d7348b5c4451d3fc30e8521", size = 4191213, upload-time = "2026-10-16T22:17:09.449Z" },
- { url = "https://files.pythonhosted.org/packages/dd/04/7e7445e706afec84ef06dc25b2c77f9cecff242c23730c62928ad1089d4a/pillow-13.0.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:51ebb529fc5e8abdd55302f6c3f8943037348ec2384329a3e55677f69ae2fdfc", size = 4290650, upload-time = "2026-10-16T22:17:11.196Z" },
- { url = "https://files.pythonhosted.org/packages/46/85/d534089068ac40c0f61f650389a296c812eab745a8a733f81feec39ae955/pillow-13.0.0-cp315-cp315-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:c87a04e7f152ee4db5dbe15f0bfa3fe628a1aeedbe66b81c43b87f52c813f9ac", size = 3723078, upload-time = "2026-10-16T22:17:12.772Z" },
- { url = "https://files.pythonhosted.org/packages/1e/d0/c0ff25bb0427ec70f32aac995453321fc3c4494a46c7292ff02d9d260b04/pillow-13.0.0-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f8db54177035a2b5f717f94475b74907e12a96dc74f6d487f3966b0a4694071", size = 6527349, upload-time = "2026-10-16T22:17:18.534Z" },
- { url = "https://files.pythonhosted.org/packages/85/11/e46fa0bcc0fc117f1eacc206b3d51f04d583fab328b4b740672d0438d83f/pillow-13.0.0-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f36aa77f74b14a2103622b196402c71330e4e7cf4122ee45c854e9a01e0eecf", size = 7152873, upload-time = "2026-10-16T22:17:20.415Z" },
- { url = "https://files.pythonhosted.org/packages/7c/53/2bdeca705f3a58679fa9201233193c86a30238340ca29b3a152c3fcc719b/pillow-13.0.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:149a1c323ba366f90b48e6f1a6db6582eb50512df374588de56fc909abc0f0f3", size = 6626871, upload-time = "2026-10-16T22:17:22.361Z" },
- { url = "https://files.pythonhosted.org/packages/14/de/3829030f209722ade3753bb456b805a0e7f885cf885b483407365c82e9ca/pillow-13.0.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:e892354a424b1e82a78ba16785822bdd40de06f19446a3d4a28047df1a4f8932", size = 7236439, upload-time = "2026-10-16T22:17:24.352Z" },
- { url = "https://files.pythonhosted.org/packages/44/b1/67fc37388382bc036a74f04da5bb6fcbf90ec3ac1f45893e06176c373fd3/pillow-13.0.0-cp315-cp315-win32.whl", hash = "sha256:8e459b613cf31f8980f584387b4fe47bf08e076e288d61aa8eba1f7fd4ddbb81", size = 6508688, upload-time = "2026-10-16T22:17:26.314Z" },
- { url = "https://files.pythonhosted.org/packages/44/05/fdb6a79aee10861165526b41b7b440e36ee1ff58613b2fbefe3acbf06d4f/pillow-13.0.0-cp315-cp315-win_amd64.whl", hash = "sha256:d4737f50d23ddd11d6685faacbb9252887dde0bc7997127757247983d74399d6", size = 7281107, upload-time = "2026-10-16T22:17:28.198Z" },
- { url = "https://files.pythonhosted.org/packages/e6/9c/6c6f7dc2a566467baf121394e83de25ac287cffe34cc030726beb4be0224/pillow-13.0.0-cp315-cp315-win_arm64.whl", hash = "sha256:2310e2adfc13c6d457d7d0f2ffdd5c56a74a317650cf765887df8d515a647ac0", size = 2695513, upload-time = "2026-10-16T22:17:30.013Z" },
- { url = "https://files.pythonhosted.org/packages/b3/64/0861ea4e03c50b14af9ed9dd8b9f388ab42f445c623a9fcbab19aef7aa55/pillow-13.0.0-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20822140c9a2ba9c5e42df342b0a892ab2e102fc0e3db967a133bb9ce3f898bb", size = 6612582, upload-time = "2026-10-16T22:17:35.415Z" },
- { url = "https://files.pythonhosted.org/packages/5c/20/cd1e9b3f60a9f3d5ebe46da36955c26004092a501f7f2c42f9fcc26611bc/pillow-13.0.0-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ffea6b2986a797c84b6d1d27d4f43e0b83c73fc16281a930891cd237cf0d08fe", size = 7230176, upload-time = "2026-10-16T22:17:37.362Z" },
- { url = "https://files.pythonhosted.org/packages/b6/ef/ffb0a2fc85690714fb7ac662956949645d57f15bf4426f520da361e7b6e3/pillow-13.0.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:05cd2cd39055d0b9650060e4513ef4a9a0aea870083950bd046a0d6490f93a22", size = 6703640, upload-time = "2026-10-16T22:17:39.313Z" },
- { url = "https://files.pythonhosted.org/packages/e0/c2/6402ee3ccb3363aeed8fdb3f9fe56703ac5377a539f0b81b53999d7e121f/pillow-13.0.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:c6fd031333dedcad7ce3e03d244093f43504f209e86925ebdb2b868fa4037630", size = 7309231, upload-time = "2026-10-16T22:17:41.021Z" },
- { url = "https://files.pythonhosted.org/packages/fc/96/d6b82e8b8c06ad143b66b12e0b31051e4bf489b4fabca1f390a14fe1ebde/pillow-13.0.0-cp315-cp315t-win32.whl", hash = "sha256:c50c80e3591e5cf9a8dfa6007d4f10732f02e32263850999286e56ccd650cfb2", size = 6517200, upload-time = "2026-10-16T22:17:42.886Z" },
- { url = "https://files.pythonhosted.org/packages/60/d4/0062e6005e9b270b165015aafd343364ac378ec5a685431ea42baad33357/pillow-13.0.0-cp315-cp315t-win_amd64.whl", hash = "sha256:adb3c2124c68e480f4632d06cb7caa3b48a51678475f483b4edb1c6dee29ea43", size = 7287488, upload-time = "2026-10-16T22:17:45.56Z" },
- { url = "https://files.pythonhosted.org/packages/e4/81/b8eb349746be7bce47dd95337c6634e84910af91b59aaa9159c034c614fa/pillow-13.0.0-cp315-cp315t-win_arm64.whl", hash = "sha256:4a25756244bdcce3b50580baadf320fa48297f0eeb8e114ba64139a40d4d0fc8", size = 2698073, upload-time = "2026-10-16T22:17:47.556Z" },
-]
-
[[package]]
name = "pyautogui"
version = "0.9.54"
@@ -646,14 +600,10 @@ wheels = [
[[package]]
name = "python-xlib"
version = "0.33"
-source = { registry = "https://pypi.org/simple" }
+source = { git = "https://github.com/Avasam/python-xlib?branch=fix-quadratic-recv#b1be4367f960ae06d7cb2fb24c58e716ba44877f" }
dependencies = [
{ name = "six" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/86/f5/8c0653e5bb54e0cbdfe27bf32d41f27bc4e12faa8742778c17f2a71be2c0/python-xlib-0.33.tar.gz", hash = "sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32", size = 269068, upload-time = "2022-12-25T18:53:00.824Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/fc/b8/ff33610932e0ee81ae7f1269c890f697d56ff74b9f5b2ee5d9b7fa2c5355/python_xlib-0.33-py2.py3-none-any.whl", hash = "sha256:c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398", size = 182185, upload-time = "2022-12-25T18:52:58.662Z" },
-]
[[package]]
name = "pywin32"