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.
Files changed (62) hide show
  1. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/PKG-INFO +7 -5
  2. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/README.md +5 -4
  3. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/__init__.py +135 -135
  4. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_desktop_runtime.py +6 -0
  5. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop.egg-info/PKG-INFO +7 -5
  6. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop.egg-info/requires.txt +1 -0
  7. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/pyproject.toml +69 -64
  8. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_packaging.py +3 -2
  9. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/LICENSE +0 -0
  10. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/__main__.py +0 -0
  11. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_bundle.py +0 -0
  12. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_compat.py +0 -0
  13. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_devices.py +0 -0
  14. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_download.py +0 -0
  15. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_enumerate.py +0 -0
  16. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_helper.py +0 -0
  17. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_log.py +0 -0
  18. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_process.py +0 -0
  19. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_sigcheck.ps1 +0 -0
  20. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_sigcheck.py +0 -0
  21. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/_winapi.py +0 -0
  22. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/agent.py +0 -0
  23. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/capture.py +0 -0
  24. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/cli.py +0 -0
  25. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/config.py +0 -0
  26. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/desktop.py +0 -0
  27. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/desktop_capture.py +0 -0
  28. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/desktop_session.py +0 -0
  29. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/display_setup.py +0 -0
  30. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/driver.py +0 -0
  31. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/driver_manifest.json +0 -0
  32. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/errors.py +0 -0
  33. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/manifest.py +0 -0
  34. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/session.py +0 -0
  35. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/window.py +0 -0
  36. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop/workspace.py +0 -0
  37. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop.egg-info/SOURCES.txt +0 -0
  38. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop.egg-info/dependency_links.txt +0 -0
  39. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop.egg-info/entry_points.txt +0 -0
  40. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/VirtualDesktop.egg-info/top_level.txt +0 -0
  41. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/setup.cfg +0 -0
  42. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_accelerators.py +0 -0
  43. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_agent.py +0 -0
  44. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_artifact_regression.py +0 -0
  45. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_capture.py +0 -0
  46. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_cli.py +0 -0
  47. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_compat.py +0 -0
  48. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_config_and_winapi.py +0 -0
  49. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_desktop_isolation.py +0 -0
  50. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_destructive_lifecycle.py +0 -0
  51. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_display_setup.py +0 -0
  52. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_download.py +0 -0
  53. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_driver.py +0 -0
  54. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_hide_modes.py +0 -0
  55. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_integration_windows.py +0 -0
  56. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_manifest.py +0 -0
  57. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_no_dialogs.py +0 -0
  58. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_process.py +0 -0
  59. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_virtual_desktop.py +0 -0
  60. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_workspace.py +0 -0
  61. {virtualdesktop-1.4.0 → virtualdesktop-1.4.1}/tests/test_workspace_instances.py +0 -0
  62. {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.0
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
- 运行时依赖只有 **Pillow**。若国内镜像同步滞后导致找不到最新版,加
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
- | 依赖 | 仅 Pillow |
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
- | 依赖 | 仅 Pillow |
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
- 运行时依赖只有 **Pillow**。若国内镜像同步滞后导致找不到最新版,加
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
- | 依赖 | 仅 Pillow |
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
- | 依赖 | 仅 Pillow |
244
+ | 依赖 | Pillow + comtypes |
244
245
 
245
246
  ## 装驱动
246
247
 
@@ -1,135 +1,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.0"
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.0
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
- 运行时依赖只有 **Pillow**。若国内镜像同步滞后导致找不到最新版,加
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
- | 依赖 | 仅 Pillow |
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
- | 依赖 | 仅 Pillow |
301
+ | 依赖 | Pillow + comtypes |
300
302
 
301
303
  ## 装驱动
302
304
 
@@ -1,4 +1,5 @@
1
1
  Pillow>=9.0
2
+ comtypes>=1.2.0
2
3
 
3
4
  [build]
4
5
  build>=1.0
@@ -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.0"
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, so the only hard
32
- # dependency is Pillow, which is needed to build and save the captured frames.
33
- dependencies = [
34
- "Pillow>=9.0",
35
- ]
36
-
37
- [project.optional-dependencies]
38
- test = ["pytest>=7.0"]
39
- build = ["build>=1.0", "twine>=5.0"]
40
-
41
- [project.scripts]
42
- VirtualDesktop = "VirtualDesktop.cli:main"
43
-
44
- [project.urls]
45
- Documentation = "https://github.com/Luofeng-Cloud/VirtualDesktop#readme"
46
- Source = "https://github.com/Luofeng-Cloud/VirtualDesktop"
47
- Issues = "https://github.com/Luofeng-Cloud/VirtualDesktop/issues"
48
-
49
- [tool.setuptools]
50
- packages = ["VirtualDesktop"]
51
-
52
- [tool.setuptools.package-data]
53
- # Both files are load-bearing at runtime: the manifest pins every digest that is downloaded, and
54
- # the PowerShell script is the Authenticode verifier used before a driver is installed (its
55
- # absence degrades to an inline copy, but shipping it keeps the verified path).
56
- VirtualDesktop = ["driver_manifest.json", "_sigcheck.ps1"]
57
-
58
- [tool.pytest.ini_options]
59
- testpaths = ["tests"]
60
- addopts = "-ra -q"
61
- markers = [
62
- "integration: needs a real interactive Windows desktop",
63
- "destructive: installs or removes a system driver (never run in CI)",
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 is the only hard dependency: the library reaches Win32 exclusively through ctypes,
128
- # so declaring pywin32 would force every user to install a wheel that is never imported.
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