VirtualDesktop 1.4.0__tar.gz → 1.4.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/PKG-INFO +7 -5
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/README.md +5 -4
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/__init__.py +135 -135
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_desktop_runtime.py +6 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop.egg-info/PKG-INFO +7 -5
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop.egg-info/requires.txt +1 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/pyproject.toml +69 -64
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_packaging.py +3 -2
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/LICENSE +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/__main__.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_bundle.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_compat.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_devices.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_download.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_enumerate.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_helper.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_log.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_process.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_sigcheck.ps1 +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_sigcheck.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_winapi.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/agent.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/capture.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/cli.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/config.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/desktop.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/desktop_capture.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/desktop_session.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/display_setup.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/driver.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/driver_manifest.json +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/errors.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/manifest.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/session.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/window.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/workspace.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop.egg-info/SOURCES.txt +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop.egg-info/dependency_links.txt +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop.egg-info/entry_points.txt +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop.egg-info/top_level.txt +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/setup.cfg +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_accelerators.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_agent.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_artifact_regression.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_capture.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_cli.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_compat.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_config_and_winapi.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_desktop_isolation.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_destructive_lifecycle.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_display_setup.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_download.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_driver.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_hide_modes.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_integration_windows.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_manifest.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_no_dialogs.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_process.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_virtual_desktop.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_workspace.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_workspace_instances.py +0 -0
- {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_workspace_move.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: VirtualDesktop
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.1
|
|
4
4
|
Summary: Multi-window virtual-display workspaces on Windows: one pip install bootstraps the virtual display driver, hosts programs on a virtual or private desktop, keeps them off the physical screens, and hands out real window handles for automation and PrintWindow capture.
|
|
5
5
|
Author: VirtualDesktop contributors
|
|
6
6
|
License: MIT License
|
|
@@ -47,6 +47,7 @@ Requires-Python: >=3.9
|
|
|
47
47
|
Description-Content-Type: text/markdown
|
|
48
48
|
License-File: LICENSE
|
|
49
49
|
Requires-Dist: Pillow>=9.0
|
|
50
|
+
Requires-Dist: comtypes>=1.2.0
|
|
50
51
|
Provides-Extra: test
|
|
51
52
|
Requires-Dist: pytest>=7.0; extra == "test"
|
|
52
53
|
Provides-Extra: build
|
|
@@ -78,7 +79,8 @@ pip install VirtualDesktop # pip
|
|
|
78
79
|
uv add VirtualDesktop # uv(写入 pyproject.toml 并锁定)
|
|
79
80
|
```
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
运行时依赖:**Pillow**(帧图像)与 **comtypes**(UI Automation 是 COM API)。不需要 pywin32——
|
|
83
|
+
库访问 Win32 全部走 ctypes。若国内镜像同步滞后导致找不到最新版,加
|
|
82
84
|
`--default-index https://pypi.org/simple`(uv)或 `-i https://pypi.org/simple`(pip)。
|
|
83
85
|
|
|
84
86
|
| | 私有桌面 | 虚拟显示器 |
|
|
@@ -145,7 +147,7 @@ uv add VirtualDesktop # uv(写入 pyproject.toml 并锁定)
|
|
|
145
147
|
| 系统 | Windows 10 / 11(无 IddCx 版本要求) |
|
|
146
148
|
| 权限 | 当前用户即可,**不提权** |
|
|
147
149
|
| 驱动 | 不需要 |
|
|
148
|
-
| 依赖 |
|
|
150
|
+
| 依赖 | Pillow + comtypes |
|
|
149
151
|
|
|
150
152
|
## 基本用法
|
|
151
153
|
|
|
@@ -214,7 +216,7 @@ with VirtualWorkspace("lab") as space, space.agent() as agent:
|
|
|
214
216
|
| `agent.exec("python 代码")` | 在桌面内跑任意 Python,返回打印内容 |
|
|
215
217
|
|
|
216
218
|
`exec` 是逃生舱:里面的代码能 `import VirtualDesktop`、能用 `list_windows()`、`CaptureEngine`,
|
|
217
|
-
也能用你自己装的任何库。代理本身只依赖 ctypes
|
|
219
|
+
也能用你自己装的任何库。代理本身只依赖 ctypes 与 comtypes。
|
|
218
220
|
|
|
219
221
|
### 私有桌面不能截图
|
|
220
222
|
|
|
@@ -296,7 +298,7 @@ with VirtualWorkspace("lab") as space, space.agent() as agent:
|
|
|
296
298
|
| 系统 | **Windows 10 1809 (build 17763) 或更高**(更低版本没有 IddCx 框架) |
|
|
297
299
|
| 权限 | 安装/卸载驱动需要管理员,弹**一次 UAC** |
|
|
298
300
|
| 驱动 | 首次使用时自动下载安装(也可预先装好) |
|
|
299
|
-
| 依赖 |
|
|
301
|
+
| 依赖 | Pillow + comtypes |
|
|
300
302
|
|
|
301
303
|
## 装驱动
|
|
302
304
|
|
|
@@ -22,7 +22,8 @@ pip install VirtualDesktop # pip
|
|
|
22
22
|
uv add VirtualDesktop # uv(写入 pyproject.toml 并锁定)
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
运行时依赖:**Pillow**(帧图像)与 **comtypes**(UI Automation 是 COM API)。不需要 pywin32——
|
|
26
|
+
库访问 Win32 全部走 ctypes。若国内镜像同步滞后导致找不到最新版,加
|
|
26
27
|
`--default-index https://pypi.org/simple`(uv)或 `-i https://pypi.org/simple`(pip)。
|
|
27
28
|
|
|
28
29
|
| | 私有桌面 | 虚拟显示器 |
|
|
@@ -89,7 +90,7 @@ uv add VirtualDesktop # uv(写入 pyproject.toml 并锁定)
|
|
|
89
90
|
| 系统 | Windows 10 / 11(无 IddCx 版本要求) |
|
|
90
91
|
| 权限 | 当前用户即可,**不提权** |
|
|
91
92
|
| 驱动 | 不需要 |
|
|
92
|
-
| 依赖 |
|
|
93
|
+
| 依赖 | Pillow + comtypes |
|
|
93
94
|
|
|
94
95
|
## 基本用法
|
|
95
96
|
|
|
@@ -158,7 +159,7 @@ with VirtualWorkspace("lab") as space, space.agent() as agent:
|
|
|
158
159
|
| `agent.exec("python 代码")` | 在桌面内跑任意 Python,返回打印内容 |
|
|
159
160
|
|
|
160
161
|
`exec` 是逃生舱:里面的代码能 `import VirtualDesktop`、能用 `list_windows()`、`CaptureEngine`,
|
|
161
|
-
也能用你自己装的任何库。代理本身只依赖 ctypes
|
|
162
|
+
也能用你自己装的任何库。代理本身只依赖 ctypes 与 comtypes。
|
|
162
163
|
|
|
163
164
|
### 私有桌面不能截图
|
|
164
165
|
|
|
@@ -240,7 +241,7 @@ with VirtualWorkspace("lab") as space, space.agent() as agent:
|
|
|
240
241
|
| 系统 | **Windows 10 1809 (build 17763) 或更高**(更低版本没有 IddCx 框架) |
|
|
241
242
|
| 权限 | 安装/卸载驱动需要管理员,弹**一次 UAC** |
|
|
242
243
|
| 驱动 | 首次使用时自动下载安装(也可预先装好) |
|
|
243
|
-
| 依赖 |
|
|
244
|
+
| 依赖 | Pillow + comtypes |
|
|
244
245
|
|
|
245
246
|
## 装驱动
|
|
246
247
|
|
|
@@ -1,135 +1,135 @@
|
|
|
1
|
-
"""``VirtualDesktop``
|
|
2
|
-
|
|
3
|
-
The public surface is deliberately small:
|
|
4
|
-
|
|
5
|
-
* :class:`~VirtualDesktop.session.OffscreenSession`
|
|
6
|
-
* :func:`~VirtualDesktop.session.probe_environment`
|
|
7
|
-
* :class:`~VirtualDesktop.driver.DriverInstaller`
|
|
8
|
-
* :mod:`VirtualDesktop.errors`
|
|
9
|
-
|
|
10
|
-
Typical use::
|
|
11
|
-
|
|
12
|
-
from VirtualDesktop import OffscreenSession
|
|
13
|
-
|
|
14
|
-
with OffscreenSession(target_window_title="WeChat") as session:
|
|
15
|
-
session.mount()
|
|
16
|
-
frame = session.capture() # PIL image
|
|
17
|
-
frame.image.save("wechat.png")
|
|
18
|
-
|
|
19
|
-
Nothing is installed until ``mount()`` actually needs a virtual display, and nothing is
|
|
20
|
-
moved until a target window has been found, so a failed call leaves the machine as it was.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
from __future__ import annotations
|
|
24
|
-
|
|
25
|
-
from ._log import enable_debug_logging, get_logger
|
|
26
|
-
from ._winapi import (
|
|
27
|
-
IS_WINDOWS,
|
|
28
|
-
MonitorInfo,
|
|
29
|
-
enumerate_monitors,
|
|
30
|
-
is_admin,
|
|
31
|
-
list_windows,
|
|
32
|
-
select_virtual_monitor,
|
|
33
|
-
)
|
|
34
|
-
from .capture import DEFAULT_METHODS, CaptureEngine, Frame, analyze_image
|
|
35
|
-
from .config import DriverConfig, SessionConfig
|
|
36
|
-
from .desktop import DesktopBinding, current_desktop_name, ensure_default_desktop
|
|
37
|
-
from .driver import DriverInstaller, InstallReport, virtual_display_present
|
|
38
|
-
from .errors import (
|
|
39
|
-
AmbiguousWindowError,
|
|
40
|
-
CaptureError,
|
|
41
|
-
ConfigError,
|
|
42
|
-
DesktopBindError,
|
|
43
|
-
DownloadError,
|
|
44
|
-
DriverInstallError,
|
|
45
|
-
DriverNotFoundError,
|
|
46
|
-
DriverVerificationError,
|
|
47
|
-
ElevationDenied,
|
|
48
|
-
ElevationRequired,
|
|
49
|
-
ExtractionError,
|
|
50
|
-
IntegrityError,
|
|
51
|
-
MissingDependencyError,
|
|
52
|
-
OffscreenMountError,
|
|
53
|
-
SessionStateError,
|
|
54
|
-
UnsupportedPlatformError,
|
|
55
|
-
WindowMountError,
|
|
56
|
-
WindowNotFoundError,
|
|
57
|
-
)
|
|
58
|
-
from .manifest import load_manifest
|
|
59
|
-
from .session import OffscreenSession, probe_environment
|
|
60
|
-
from .desktop_session import DesktopProcess, VirtualDesktop, current_desktop_name
|
|
61
|
-
from .window import WindowMounter, WindowRecord, find_window, window_diagnostics
|
|
62
|
-
from .workspace import SHARED, VirtualWorkspace, WorkspaceWindow
|
|
63
|
-
from .agent import AgentProcess, AgentSession
|
|
64
|
-
from .display_setup import configure_virtual_display, current_primary_dpi, current_primary_size
|
|
65
|
-
|
|
66
|
-
__version__ = "1.4.
|
|
67
|
-
|
|
68
|
-
__all__ = [
|
|
69
|
-
"__version__",
|
|
70
|
-
# session
|
|
71
|
-
"OffscreenSession",
|
|
72
|
-
"probe_environment",
|
|
73
|
-
# drivers
|
|
74
|
-
"DriverInstaller",
|
|
75
|
-
"InstallReport",
|
|
76
|
-
"virtual_display_present",
|
|
77
|
-
"load_manifest",
|
|
78
|
-
# windows and displays
|
|
79
|
-
"WindowMounter",
|
|
80
|
-
"WindowRecord",
|
|
81
|
-
"find_window",
|
|
82
|
-
"window_diagnostics",
|
|
83
|
-
"enumerate_monitors",
|
|
84
|
-
"select_virtual_monitor",
|
|
85
|
-
"MonitorInfo",
|
|
86
|
-
"list_windows",
|
|
87
|
-
"ensure_default_desktop",
|
|
88
|
-
"current_desktop_name",
|
|
89
|
-
"DesktopBinding",
|
|
90
|
-
# private virtual desktop (no admin, no driver)
|
|
91
|
-
"VirtualDesktop",
|
|
92
|
-
"DesktopProcess",
|
|
93
|
-
# multi-window virtual-display workspace (externally controllable)
|
|
94
|
-
"VirtualWorkspace",
|
|
95
|
-
"WorkspaceWindow",
|
|
96
|
-
"SHARED",
|
|
97
|
-
# in-desktop agent (for isolated workspaces / private desktops)
|
|
98
|
-
"AgentSession",
|
|
99
|
-
"AgentProcess",
|
|
100
|
-
# virtual display geometry
|
|
101
|
-
"configure_virtual_display",
|
|
102
|
-
"current_primary_size",
|
|
103
|
-
"current_primary_dpi",
|
|
104
|
-
# capture
|
|
105
|
-
"CaptureEngine",
|
|
106
|
-
"Frame",
|
|
107
|
-
"analyze_image",
|
|
108
|
-
"DEFAULT_METHODS",
|
|
109
|
-
# configuration
|
|
110
|
-
"DriverConfig",
|
|
111
|
-
"SessionConfig",
|
|
112
|
-
# platform helpers
|
|
113
|
-
"IS_WINDOWS",
|
|
114
|
-
"is_admin",
|
|
115
|
-
"enable_debug_logging",
|
|
116
|
-
# errors
|
|
117
|
-
"OffscreenMountError",
|
|
118
|
-
"UnsupportedPlatformError",
|
|
119
|
-
"MissingDependencyError",
|
|
120
|
-
"ConfigError",
|
|
121
|
-
"IntegrityError",
|
|
122
|
-
"DownloadError",
|
|
123
|
-
"ExtractionError",
|
|
124
|
-
"ElevationRequired",
|
|
125
|
-
"ElevationDenied",
|
|
126
|
-
"DriverInstallError",
|
|
127
|
-
"DriverNotFoundError",
|
|
128
|
-
"DriverVerificationError",
|
|
129
|
-
"DesktopBindError",
|
|
130
|
-
"WindowNotFoundError",
|
|
131
|
-
"AmbiguousWindowError",
|
|
132
|
-
"WindowMountError",
|
|
133
|
-
"CaptureError",
|
|
134
|
-
"SessionStateError",
|
|
135
|
-
]
|
|
1
|
+
"""``VirtualDesktop`` 鈥?an offscreen window workstation for Windows.
|
|
2
|
+
|
|
3
|
+
The public surface is deliberately small:
|
|
4
|
+
|
|
5
|
+
* :class:`~VirtualDesktop.session.OffscreenSession` 鈥?mount, capture, restore.
|
|
6
|
+
* :func:`~VirtualDesktop.session.probe_environment` 鈥?diagnostics without side effects.
|
|
7
|
+
* :class:`~VirtualDesktop.driver.DriverInstaller` 鈥?explicit driver control.
|
|
8
|
+
* :mod:`VirtualDesktop.errors` 鈥?every exception, each with a ``code`` and a ``hint``.
|
|
9
|
+
|
|
10
|
+
Typical use::
|
|
11
|
+
|
|
12
|
+
from VirtualDesktop import OffscreenSession
|
|
13
|
+
|
|
14
|
+
with OffscreenSession(target_window_title="WeChat") as session:
|
|
15
|
+
session.mount()
|
|
16
|
+
frame = session.capture() # PIL image
|
|
17
|
+
frame.image.save("wechat.png")
|
|
18
|
+
|
|
19
|
+
Nothing is installed until ``mount()`` actually needs a virtual display, and nothing is
|
|
20
|
+
moved until a target window has been found, so a failed call leaves the machine as it was.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
from ._log import enable_debug_logging, get_logger
|
|
26
|
+
from ._winapi import (
|
|
27
|
+
IS_WINDOWS,
|
|
28
|
+
MonitorInfo,
|
|
29
|
+
enumerate_monitors,
|
|
30
|
+
is_admin,
|
|
31
|
+
list_windows,
|
|
32
|
+
select_virtual_monitor,
|
|
33
|
+
)
|
|
34
|
+
from .capture import DEFAULT_METHODS, CaptureEngine, Frame, analyze_image
|
|
35
|
+
from .config import DriverConfig, SessionConfig
|
|
36
|
+
from .desktop import DesktopBinding, current_desktop_name, ensure_default_desktop
|
|
37
|
+
from .driver import DriverInstaller, InstallReport, virtual_display_present
|
|
38
|
+
from .errors import (
|
|
39
|
+
AmbiguousWindowError,
|
|
40
|
+
CaptureError,
|
|
41
|
+
ConfigError,
|
|
42
|
+
DesktopBindError,
|
|
43
|
+
DownloadError,
|
|
44
|
+
DriverInstallError,
|
|
45
|
+
DriverNotFoundError,
|
|
46
|
+
DriverVerificationError,
|
|
47
|
+
ElevationDenied,
|
|
48
|
+
ElevationRequired,
|
|
49
|
+
ExtractionError,
|
|
50
|
+
IntegrityError,
|
|
51
|
+
MissingDependencyError,
|
|
52
|
+
OffscreenMountError,
|
|
53
|
+
SessionStateError,
|
|
54
|
+
UnsupportedPlatformError,
|
|
55
|
+
WindowMountError,
|
|
56
|
+
WindowNotFoundError,
|
|
57
|
+
)
|
|
58
|
+
from .manifest import load_manifest
|
|
59
|
+
from .session import OffscreenSession, probe_environment
|
|
60
|
+
from .desktop_session import DesktopProcess, VirtualDesktop, current_desktop_name
|
|
61
|
+
from .window import WindowMounter, WindowRecord, find_window, window_diagnostics
|
|
62
|
+
from .workspace import SHARED, VirtualWorkspace, WorkspaceWindow
|
|
63
|
+
from .agent import AgentProcess, AgentSession
|
|
64
|
+
from .display_setup import configure_virtual_display, current_primary_dpi, current_primary_size
|
|
65
|
+
|
|
66
|
+
__version__ = "1.4.1"
|
|
67
|
+
|
|
68
|
+
__all__ = [
|
|
69
|
+
"__version__",
|
|
70
|
+
# session
|
|
71
|
+
"OffscreenSession",
|
|
72
|
+
"probe_environment",
|
|
73
|
+
# drivers
|
|
74
|
+
"DriverInstaller",
|
|
75
|
+
"InstallReport",
|
|
76
|
+
"virtual_display_present",
|
|
77
|
+
"load_manifest",
|
|
78
|
+
# windows and displays
|
|
79
|
+
"WindowMounter",
|
|
80
|
+
"WindowRecord",
|
|
81
|
+
"find_window",
|
|
82
|
+
"window_diagnostics",
|
|
83
|
+
"enumerate_monitors",
|
|
84
|
+
"select_virtual_monitor",
|
|
85
|
+
"MonitorInfo",
|
|
86
|
+
"list_windows",
|
|
87
|
+
"ensure_default_desktop",
|
|
88
|
+
"current_desktop_name",
|
|
89
|
+
"DesktopBinding",
|
|
90
|
+
# private virtual desktop (no admin, no driver)
|
|
91
|
+
"VirtualDesktop",
|
|
92
|
+
"DesktopProcess",
|
|
93
|
+
# multi-window virtual-display workspace (externally controllable)
|
|
94
|
+
"VirtualWorkspace",
|
|
95
|
+
"WorkspaceWindow",
|
|
96
|
+
"SHARED",
|
|
97
|
+
# in-desktop agent (for isolated workspaces / private desktops)
|
|
98
|
+
"AgentSession",
|
|
99
|
+
"AgentProcess",
|
|
100
|
+
# virtual display geometry
|
|
101
|
+
"configure_virtual_display",
|
|
102
|
+
"current_primary_size",
|
|
103
|
+
"current_primary_dpi",
|
|
104
|
+
# capture
|
|
105
|
+
"CaptureEngine",
|
|
106
|
+
"Frame",
|
|
107
|
+
"analyze_image",
|
|
108
|
+
"DEFAULT_METHODS",
|
|
109
|
+
# configuration
|
|
110
|
+
"DriverConfig",
|
|
111
|
+
"SessionConfig",
|
|
112
|
+
# platform helpers
|
|
113
|
+
"IS_WINDOWS",
|
|
114
|
+
"is_admin",
|
|
115
|
+
"enable_debug_logging",
|
|
116
|
+
# errors
|
|
117
|
+
"OffscreenMountError",
|
|
118
|
+
"UnsupportedPlatformError",
|
|
119
|
+
"MissingDependencyError",
|
|
120
|
+
"ConfigError",
|
|
121
|
+
"IntegrityError",
|
|
122
|
+
"DownloadError",
|
|
123
|
+
"ExtractionError",
|
|
124
|
+
"ElevationRequired",
|
|
125
|
+
"ElevationDenied",
|
|
126
|
+
"DriverInstallError",
|
|
127
|
+
"DriverNotFoundError",
|
|
128
|
+
"DriverVerificationError",
|
|
129
|
+
"DesktopBindError",
|
|
130
|
+
"WindowNotFoundError",
|
|
131
|
+
"AmbiguousWindowError",
|
|
132
|
+
"WindowMountError",
|
|
133
|
+
"CaptureError",
|
|
134
|
+
"SessionStateError",
|
|
135
|
+
]
|
|
@@ -242,6 +242,12 @@ def _uia_tree(hwnd: int, *, depth: int = 8, max_nodes: int = 500, view: str = "R
|
|
|
242
242
|
"""
|
|
243
243
|
try:
|
|
244
244
|
import comtypes.client
|
|
245
|
+
|
|
246
|
+
# ``comtypes.gen.UIAutomationClient`` is generated on first use from the type library, so
|
|
247
|
+
# importing it directly fails on a machine that has never spoken to UIA (measured: a fresh
|
|
248
|
+
# venv dies with "cannot import name 'UIAutomationClient' from 'comtypes.gen'"). GetModule
|
|
249
|
+
# generates it if needed, and is what makes this work out of the box.
|
|
250
|
+
comtypes.client.GetModule("UIAutomationCore.dll")
|
|
245
251
|
from comtypes.gen import UIAutomationClient as UIA
|
|
246
252
|
except Exception as exc: # noqa: BLE001 - comtypes is optional
|
|
247
253
|
return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: VirtualDesktop
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.1
|
|
4
4
|
Summary: Multi-window virtual-display workspaces on Windows: one pip install bootstraps the virtual display driver, hosts programs on a virtual or private desktop, keeps them off the physical screens, and hands out real window handles for automation and PrintWindow capture.
|
|
5
5
|
Author: VirtualDesktop contributors
|
|
6
6
|
License: MIT License
|
|
@@ -47,6 +47,7 @@ Requires-Python: >=3.9
|
|
|
47
47
|
Description-Content-Type: text/markdown
|
|
48
48
|
License-File: LICENSE
|
|
49
49
|
Requires-Dist: Pillow>=9.0
|
|
50
|
+
Requires-Dist: comtypes>=1.2.0
|
|
50
51
|
Provides-Extra: test
|
|
51
52
|
Requires-Dist: pytest>=7.0; extra == "test"
|
|
52
53
|
Provides-Extra: build
|
|
@@ -78,7 +79,8 @@ pip install VirtualDesktop # pip
|
|
|
78
79
|
uv add VirtualDesktop # uv(写入 pyproject.toml 并锁定)
|
|
79
80
|
```
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
运行时依赖:**Pillow**(帧图像)与 **comtypes**(UI Automation 是 COM API)。不需要 pywin32——
|
|
83
|
+
库访问 Win32 全部走 ctypes。若国内镜像同步滞后导致找不到最新版,加
|
|
82
84
|
`--default-index https://pypi.org/simple`(uv)或 `-i https://pypi.org/simple`(pip)。
|
|
83
85
|
|
|
84
86
|
| | 私有桌面 | 虚拟显示器 |
|
|
@@ -145,7 +147,7 @@ uv add VirtualDesktop # uv(写入 pyproject.toml 并锁定)
|
|
|
145
147
|
| 系统 | Windows 10 / 11(无 IddCx 版本要求) |
|
|
146
148
|
| 权限 | 当前用户即可,**不提权** |
|
|
147
149
|
| 驱动 | 不需要 |
|
|
148
|
-
| 依赖 |
|
|
150
|
+
| 依赖 | Pillow + comtypes |
|
|
149
151
|
|
|
150
152
|
## 基本用法
|
|
151
153
|
|
|
@@ -214,7 +216,7 @@ with VirtualWorkspace("lab") as space, space.agent() as agent:
|
|
|
214
216
|
| `agent.exec("python 代码")` | 在桌面内跑任意 Python,返回打印内容 |
|
|
215
217
|
|
|
216
218
|
`exec` 是逃生舱:里面的代码能 `import VirtualDesktop`、能用 `list_windows()`、`CaptureEngine`,
|
|
217
|
-
也能用你自己装的任何库。代理本身只依赖 ctypes
|
|
219
|
+
也能用你自己装的任何库。代理本身只依赖 ctypes 与 comtypes。
|
|
218
220
|
|
|
219
221
|
### 私有桌面不能截图
|
|
220
222
|
|
|
@@ -296,7 +298,7 @@ with VirtualWorkspace("lab") as space, space.agent() as agent:
|
|
|
296
298
|
| 系统 | **Windows 10 1809 (build 17763) 或更高**(更低版本没有 IddCx 框架) |
|
|
297
299
|
| 权限 | 安装/卸载驱动需要管理员,弹**一次 UAC** |
|
|
298
300
|
| 驱动 | 首次使用时自动下载安装(也可预先装好) |
|
|
299
|
-
| 依赖 |
|
|
301
|
+
| 依赖 | Pillow + comtypes |
|
|
300
302
|
|
|
301
303
|
## 装驱动
|
|
302
304
|
|
|
@@ -1,64 +1,69 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["setuptools>=68", "wheel"]
|
|
3
|
-
build-backend = "setuptools.build_meta"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
name = "VirtualDesktop"
|
|
7
|
-
version = "1.4.
|
|
8
|
-
description = "Multi-window virtual-display workspaces on Windows: one pip install bootstraps the virtual display driver, hosts programs on a virtual or private desktop, keeps them off the physical screens, and hands out real window handles for automation and PrintWindow capture."
|
|
9
|
-
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.9"
|
|
11
|
-
license = { file = "LICENSE" }
|
|
12
|
-
authors = [{ name = "VirtualDesktop contributors" }]
|
|
13
|
-
keywords = ["windows", "virtual-display", "idd", "printwindow", "automation", "screenshot", "ocr"]
|
|
14
|
-
classifiers = [
|
|
15
|
-
"Development Status :: 5 - Production/Stable",
|
|
16
|
-
"Environment :: Win32 (MS Windows)",
|
|
17
|
-
"Intended Audience :: Developers",
|
|
18
|
-
"License :: OSI Approved :: MIT License",
|
|
19
|
-
"Operating System :: Microsoft :: Windows :: Windows 10",
|
|
20
|
-
"Operating System :: Microsoft :: Windows :: Windows 11",
|
|
21
|
-
"Programming Language :: Python :: 3",
|
|
22
|
-
"Programming Language :: Python :: 3.9",
|
|
23
|
-
"Programming Language :: Python :: 3.10",
|
|
24
|
-
"Programming Language :: Python :: 3.11",
|
|
25
|
-
"Programming Language :: Python :: 3.12",
|
|
26
|
-
"Topic :: Software Development :: Libraries",
|
|
27
|
-
"Topic :: System :: Systems Administration",
|
|
28
|
-
"Typing :: Typed",
|
|
29
|
-
]
|
|
30
|
-
# The library talks to Win32 exclusively through ``ctypes``; it does not import ``pywin32``.
|
|
31
|
-
# Declaring it would force every user to install a large wheel they never use
|
|
32
|
-
#
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
[
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
[
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
[
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
]
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "VirtualDesktop"
|
|
7
|
+
version = "1.4.1"
|
|
8
|
+
description = "Multi-window virtual-display workspaces on Windows: one pip install bootstraps the virtual display driver, hosts programs on a virtual or private desktop, keeps them off the physical screens, and hands out real window handles for automation and PrintWindow capture."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { file = "LICENSE" }
|
|
12
|
+
authors = [{ name = "VirtualDesktop contributors" }]
|
|
13
|
+
keywords = ["windows", "virtual-display", "idd", "printwindow", "automation", "screenshot", "ocr"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 5 - Production/Stable",
|
|
16
|
+
"Environment :: Win32 (MS Windows)",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: Microsoft :: Windows :: Windows 10",
|
|
20
|
+
"Operating System :: Microsoft :: Windows :: Windows 11",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Topic :: Software Development :: Libraries",
|
|
27
|
+
"Topic :: System :: Systems Administration",
|
|
28
|
+
"Typing :: Typed",
|
|
29
|
+
]
|
|
30
|
+
# The library talks to Win32 exclusively through ``ctypes``; it does not import ``pywin32``.
|
|
31
|
+
# Declaring it would force every user to install a large wheel they never use.
|
|
32
|
+
#
|
|
33
|
+
# ``comtypes`` is a hard dependency because UI Automation is a COM API and the library now offers
|
|
34
|
+
# ``agent.uia_tree()`` as a first-class capability: shipping it unusable by default would make the
|
|
35
|
+
# documented feature silently return an empty tree. comtypes is a small pure-Python package with
|
|
36
|
+
# no pywin32 dependency.
|
|
37
|
+
dependencies = [
|
|
38
|
+
"Pillow>=9.0",
|
|
39
|
+
"comtypes>=1.2.0",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[project.optional-dependencies]
|
|
43
|
+
test = ["pytest>=7.0"]
|
|
44
|
+
build = ["build>=1.0", "twine>=5.0"]
|
|
45
|
+
|
|
46
|
+
[project.scripts]
|
|
47
|
+
VirtualDesktop = "VirtualDesktop.cli:main"
|
|
48
|
+
|
|
49
|
+
[project.urls]
|
|
50
|
+
Documentation = "https://github.com/Luofeng-Cloud/VirtualDesktop#readme"
|
|
51
|
+
Source = "https://github.com/Luofeng-Cloud/VirtualDesktop"
|
|
52
|
+
Issues = "https://github.com/Luofeng-Cloud/VirtualDesktop/issues"
|
|
53
|
+
|
|
54
|
+
[tool.setuptools]
|
|
55
|
+
packages = ["VirtualDesktop"]
|
|
56
|
+
|
|
57
|
+
[tool.setuptools.package-data]
|
|
58
|
+
# Both files are load-bearing at runtime: the manifest pins every digest that is downloaded, and
|
|
59
|
+
# the PowerShell script is the Authenticode verifier used before a driver is installed (its
|
|
60
|
+
# absence degrades to an inline copy, but shipping it keeps the verified path).
|
|
61
|
+
VirtualDesktop = ["driver_manifest.json", "_sigcheck.ps1"]
|
|
62
|
+
|
|
63
|
+
[tool.pytest.ini_options]
|
|
64
|
+
testpaths = ["tests"]
|
|
65
|
+
addopts = "-ra -q"
|
|
66
|
+
markers = [
|
|
67
|
+
"integration: needs a real interactive Windows desktop",
|
|
68
|
+
"destructive: installs or removes a system driver (never run in CI)",
|
|
69
|
+
]
|
|
@@ -124,10 +124,11 @@ def test_wheel_metadata_and_console_script():
|
|
|
124
124
|
metadata = archive.read(metadata_name).decode("utf-8")
|
|
125
125
|
entry = archive.read(entry_name).decode("utf-8")
|
|
126
126
|
assert "Requires-Python: >=3.9" in metadata
|
|
127
|
-
# Pillow
|
|
128
|
-
#
|
|
127
|
+
# Pillow (frame images) and comtypes (UI Automation, a COM API) are the hard dependencies.
|
|
128
|
+
# pywin32 must not appear: the library reaches Win32 exclusively through ctypes.
|
|
129
129
|
requires = [line for line in metadata.splitlines() if line.startswith("Requires-Dist:")]
|
|
130
130
|
assert any(line.startswith("Requires-Dist: Pillow>=") for line in requires), requires
|
|
131
|
+
assert any(line.startswith("Requires-Dist: comtypes>=") for line in requires), requires
|
|
131
132
|
assert not any("pywin32" in line for line in requires), (
|
|
132
133
|
f"pywin32 must not be a runtime dependency: {requires}"
|
|
133
134
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|