dxcam 0.1.0.dev1__tar.gz → 0.1.0.dev2__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.
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/PKG-INFO +10 -6
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/README.md +8 -4
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam/core/duplicator.py +1 -1
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam/dxcam.py +73 -10
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam/util/timer.py +4 -0
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam.egg-info/PKG-INFO +10 -6
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/pyproject.toml +3 -3
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/LICENSE +0 -0
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam/__init__.py +0 -0
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam/_libs/__init__.py +0 -0
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam/_libs/d3d11.py +0 -0
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam/_libs/dxgi.py +0 -0
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam/_libs/user32.py +0 -0
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam/core/__init__.py +0 -0
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam/core/device.py +0 -0
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam/core/output.py +0 -0
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam/core/stagesurf.py +0 -0
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam/processor/__init__.py +0 -0
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam/processor/base.py +0 -0
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam/processor/numpy_processor.py +0 -0
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam/types.py +0 -0
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam/util/__init__.py +0 -0
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam/util/io.py +0 -0
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam.egg-info/SOURCES.txt +0 -0
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam.egg-info/dependency_links.txt +0 -0
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam.egg-info/requires.txt +0 -0
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/dxcam.egg-info/top_level.txt +0 -0
- {dxcam-0.1.0.dev1 → dxcam-0.1.0.dev2}/setup.cfg +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dxcam
|
|
3
|
-
Version: 0.1.0.
|
|
3
|
+
Version: 0.1.0.dev2
|
|
4
4
|
Summary: A Python high-performance screenshot library for Windows using Desktop Duplication API
|
|
5
5
|
Author: ra1nty
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Source, https://github.com/ra1nty/DXcam
|
|
8
8
|
Project-URL: Tracker, https://github.com/ra1nty/DXcam/issues
|
|
9
9
|
Keywords: screen,screenshot,screencapture,screengrab,windows
|
|
10
|
-
Classifier: Development Status ::
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
13
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
@@ -20,7 +20,7 @@ Classifier: Operating System :: Microsoft :: Windows :: Windows 11
|
|
|
20
20
|
Classifier: Topic :: Multimedia :: Graphics
|
|
21
21
|
Classifier: Topic :: Multimedia :: Graphics :: Capture
|
|
22
22
|
Classifier: Topic :: Multimedia :: Graphics :: Capture :: Screen Capture
|
|
23
|
-
Requires-Python: >=3.
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE
|
|
26
26
|
Requires-Dist: comtypes
|
|
@@ -88,8 +88,9 @@ camera = dxcam.create() # primary output on device 0
|
|
|
88
88
|
```python
|
|
89
89
|
frame = camera.grab()
|
|
90
90
|
```
|
|
91
|
+
`grab()` returns a `numpy.ndarray`. `None` if no new frame is available since the last capture, mainly for backward compatibility. You can use `new_frame_only=False` to change this behavior.
|
|
91
92
|
|
|
92
|
-
`
|
|
93
|
+
Use `copy=False` (or `camera.grab_view()`) for a zero-copy view.
|
|
93
94
|
|
|
94
95
|
To capture a region:
|
|
95
96
|
```python
|
|
@@ -117,6 +118,9 @@ for _ in range(1000):
|
|
|
117
118
|
Useful variants:
|
|
118
119
|
- `camera.get_latest_frame(with_timestamp=True)` -> `(frame, frame_timestamp)` -> return frame timestamp
|
|
119
120
|
- `camera.get_latest_frame_view()` -> zero-copy view into the frame buffer
|
|
121
|
+
- `camera.grab(copy=False)` / `camera.grab_view()` -> zero-copy latest-frame snapshot
|
|
122
|
+
|
|
123
|
+
** When `start()` capture is running, calling `grab()` reads from the in-memory ring buffer instead of directly polling DXGI.
|
|
120
124
|
|
|
121
125
|
## Advanced Usage and Remarks
|
|
122
126
|
### Multiple monitors / GPUs
|
|
@@ -211,14 +215,14 @@ camera.release()
|
|
|
211
215
|
```
|
|
212
216
|
|
|
213
217
|
## Benchmarks
|
|
214
|
-
When using a similar logic (only
|
|
218
|
+
When using a similar logic (only capture newly rendered frames) running on a 240fps output, ```DXCam, python-mss, D3DShot``` benchmarked as follow:
|
|
215
219
|
|
|
216
220
|
| | DXcam | python-mss | D3DShot |
|
|
217
221
|
|-------------|--------|------------|---------|
|
|
218
222
|
| Average FPS | 239.19 :checkered_flag: | 75.87 | 118.36 |
|
|
219
223
|
| Std Dev | 1.25 | 0.5447 | 0.3224 |
|
|
220
224
|
|
|
221
|
-
The benchmark is across 5 runs, with a light-moderate usage on my PC (5900X + 3090; Chrome ~30tabs, VS Code opened, etc.), I used the [Blur Buster UFO test](https://www.testufo.com/framerates#count=5&background=stars&pps=960) to constantly render 240 fps on my monitor
|
|
225
|
+
The benchmark is across 5 runs, with a light-moderate usage on my PC (5900X + 3090; Chrome ~30tabs, VS Code opened, etc.), I used the [Blur Buster UFO test](https://www.testufo.com/framerates#count=5&background=stars&pps=960) to constantly render 240 fps on my monitor. DXcam captured almost every frame rendered. You will see some benchmarks online claiming 1000+fps capture while most of them is busy-spinning a for loop on a staled frame (no new frame rendered on screen in test scenario).
|
|
222
226
|
|
|
223
227
|
### For Targeting FPS:
|
|
224
228
|
| (Target)\\(mean,std) | DXcam | python-mss | D3DShot |
|
|
@@ -57,8 +57,9 @@ camera = dxcam.create() # primary output on device 0
|
|
|
57
57
|
```python
|
|
58
58
|
frame = camera.grab()
|
|
59
59
|
```
|
|
60
|
+
`grab()` returns a `numpy.ndarray`. `None` if no new frame is available since the last capture, mainly for backward compatibility. You can use `new_frame_only=False` to change this behavior.
|
|
60
61
|
|
|
61
|
-
`
|
|
62
|
+
Use `copy=False` (or `camera.grab_view()`) for a zero-copy view.
|
|
62
63
|
|
|
63
64
|
To capture a region:
|
|
64
65
|
```python
|
|
@@ -86,6 +87,9 @@ for _ in range(1000):
|
|
|
86
87
|
Useful variants:
|
|
87
88
|
- `camera.get_latest_frame(with_timestamp=True)` -> `(frame, frame_timestamp)` -> return frame timestamp
|
|
88
89
|
- `camera.get_latest_frame_view()` -> zero-copy view into the frame buffer
|
|
90
|
+
- `camera.grab(copy=False)` / `camera.grab_view()` -> zero-copy latest-frame snapshot
|
|
91
|
+
|
|
92
|
+
** When `start()` capture is running, calling `grab()` reads from the in-memory ring buffer instead of directly polling DXGI.
|
|
89
93
|
|
|
90
94
|
## Advanced Usage and Remarks
|
|
91
95
|
### Multiple monitors / GPUs
|
|
@@ -180,14 +184,14 @@ camera.release()
|
|
|
180
184
|
```
|
|
181
185
|
|
|
182
186
|
## Benchmarks
|
|
183
|
-
When using a similar logic (only
|
|
187
|
+
When using a similar logic (only capture newly rendered frames) running on a 240fps output, ```DXCam, python-mss, D3DShot``` benchmarked as follow:
|
|
184
188
|
|
|
185
189
|
| | DXcam | python-mss | D3DShot |
|
|
186
190
|
|-------------|--------|------------|---------|
|
|
187
191
|
| Average FPS | 239.19 :checkered_flag: | 75.87 | 118.36 |
|
|
188
192
|
| Std Dev | 1.25 | 0.5447 | 0.3224 |
|
|
189
193
|
|
|
190
|
-
The benchmark is across 5 runs, with a light-moderate usage on my PC (5900X + 3090; Chrome ~30tabs, VS Code opened, etc.), I used the [Blur Buster UFO test](https://www.testufo.com/framerates#count=5&background=stars&pps=960) to constantly render 240 fps on my monitor
|
|
194
|
+
The benchmark is across 5 runs, with a light-moderate usage on my PC (5900X + 3090; Chrome ~30tabs, VS Code opened, etc.), I used the [Blur Buster UFO test](https://www.testufo.com/framerates#count=5&background=stars&pps=960) to constantly render 240 fps on my monitor. DXcam captured almost every frame rendered. You will see some benchmarks online claiming 1000+fps capture while most of them is busy-spinning a for loop on a staled frame (no new frame rendered on screen in test scenario).
|
|
191
195
|
|
|
192
196
|
### For Targeting FPS:
|
|
193
197
|
| (Target)\\(mean,std) | DXcam | python-mss | D3DShot |
|
|
@@ -200,4 +204,4 @@ The benchmark is across 5 runs, with a light-moderate usage on my PC (5900X + 30
|
|
|
200
204
|
|
|
201
205
|
[OBS Studio](https://github.com/obsproject/obs-studio) - implementation ideas and references.
|
|
202
206
|
|
|
203
|
-
[D3DShot](https://github.com/SerpentAI/D3DShot/) : DXcam borrows the ctypes header directly from the no-longer maintained D3DShot.
|
|
207
|
+
[D3DShot](https://github.com/SerpentAI/D3DShot/) : DXcam borrows the ctypes header directly from the no-longer maintained D3DShot.
|
|
@@ -87,32 +87,93 @@ class DXCamera:
|
|
|
87
87
|
self.__frame_count = 0
|
|
88
88
|
self.__capture_start_time = 0
|
|
89
89
|
self.__latest_frame_ticks: int | None = None
|
|
90
|
+
self.__last_grab_entry: tuple[Region, Frame] | None = None
|
|
90
91
|
|
|
91
|
-
def grab(
|
|
92
|
+
def grab(
|
|
93
|
+
self,
|
|
94
|
+
region: Region | None = None,
|
|
95
|
+
copy: bool = True,
|
|
96
|
+
new_frame_only: bool = True,
|
|
97
|
+
) -> Frame | None:
|
|
98
|
+
"""Grab one frame.
|
|
99
|
+
|
|
100
|
+
Ownership contract:
|
|
101
|
+
- ``copy=True`` returns caller-owned memory.
|
|
102
|
+
- ``copy=False`` may return internal memory reused by future grabs.
|
|
103
|
+
- ``new_frame_only=True`` returns ``None`` when no new frame is available
|
|
104
|
+
in one-shot mode.
|
|
105
|
+
- ``new_frame_only=False`` falls back to the last successfully grabbed
|
|
106
|
+
frame for the same region in one-shot mode.
|
|
107
|
+
|
|
108
|
+
When capture is running (``start()``), this reads from the ring buffer
|
|
109
|
+
instead of touching DXGI objects directly. ``new_frame_only`` does not
|
|
110
|
+
apply while capture is running.
|
|
111
|
+
"""
|
|
92
112
|
self._ensure_not_released()
|
|
113
|
+
if self.is_capturing:
|
|
114
|
+
if region is not None and region != self.region:
|
|
115
|
+
raise ValueError(
|
|
116
|
+
"grab(region=...) is not supported while capture is running. "
|
|
117
|
+
"Use start(region=...) to configure capture region."
|
|
118
|
+
)
|
|
119
|
+
return self._peek_latest_buffered_frame(copy=copy)
|
|
93
120
|
if region is None:
|
|
94
121
|
region = self.region
|
|
95
122
|
else:
|
|
96
123
|
self._validate_region(region)
|
|
97
|
-
frame = self._grab(region)
|
|
124
|
+
frame = self._grab(region, copy=copy, new_frame_only=new_frame_only)
|
|
98
125
|
return frame
|
|
99
126
|
|
|
100
|
-
def
|
|
127
|
+
def grab_view(self, region: Region | None = None) -> Frame | None:
|
|
128
|
+
"""Zero-copy variant of ``grab()``."""
|
|
129
|
+
return self.grab(region=region, copy=False)
|
|
130
|
+
|
|
131
|
+
def _peek_latest_buffered_frame(self, copy: bool = True) -> Frame | None:
|
|
132
|
+
with self.__lock:
|
|
133
|
+
if self.__frame_buffer is None or not self.__has_frame:
|
|
134
|
+
return None
|
|
135
|
+
latest_idx = (self.__head - 1) % self.max_buffer_len
|
|
136
|
+
ret = self.__frame_buffer[latest_idx]
|
|
137
|
+
return np.array(ret, copy=True) if copy else ret
|
|
138
|
+
|
|
139
|
+
def _set_cached_grab_frame(self, region: Region, frame: Frame) -> None:
|
|
140
|
+
self.__last_grab_entry = (region, frame)
|
|
141
|
+
|
|
142
|
+
def _get_cached_grab_frame(
|
|
143
|
+
self, region: Region, copy: bool = True
|
|
144
|
+
) -> Frame | None:
|
|
145
|
+
entry = self.__last_grab_entry
|
|
146
|
+
if entry is None:
|
|
147
|
+
return None
|
|
148
|
+
cached_region, cached = entry
|
|
149
|
+
if cached_region != region:
|
|
150
|
+
return None
|
|
151
|
+
return np.array(cached, copy=True) if copy else cached
|
|
152
|
+
|
|
153
|
+
def _grab(
|
|
154
|
+
self,
|
|
155
|
+
region: Region,
|
|
156
|
+
copy: bool = True,
|
|
157
|
+
new_frame_only: bool = True,
|
|
158
|
+
) -> Frame | None:
|
|
101
159
|
if not self._duplicator.update_frame():
|
|
102
160
|
self._on_output_change()
|
|
103
|
-
|
|
161
|
+
if new_frame_only:
|
|
162
|
+
return None
|
|
163
|
+
return self._get_cached_grab_frame(region=region, copy=copy)
|
|
104
164
|
if not self._duplicator.updated:
|
|
105
|
-
|
|
165
|
+
if new_frame_only:
|
|
166
|
+
return None
|
|
167
|
+
return self._get_cached_grab_frame(region=region, copy=copy)
|
|
106
168
|
|
|
107
169
|
frame_width, frame_height = self._copy_region_to_stage(region)
|
|
108
170
|
frame = self._process_staging_frame(
|
|
109
171
|
frame_width=frame_width, frame_height=frame_height
|
|
110
172
|
)
|
|
111
|
-
|
|
112
|
-
if not
|
|
113
|
-
self.
|
|
114
|
-
|
|
115
|
-
return frame
|
|
173
|
+
result = np.array(frame, copy=True) if copy else frame
|
|
174
|
+
if not new_frame_only:
|
|
175
|
+
self._set_cached_grab_frame(region=region, frame=result)
|
|
176
|
+
return result
|
|
116
177
|
|
|
117
178
|
def _grab_into(self, region: Region, dst: Frame) -> tuple[bool, int, int, int]:
|
|
118
179
|
"""Capture into ``dst`` and return ``(captured, frame_ticks, width, height)``.
|
|
@@ -203,6 +264,7 @@ class DXCamera:
|
|
|
203
264
|
time.sleep(0.1) # Wait for Display mode change (Access Lost)
|
|
204
265
|
self._duplicator.release()
|
|
205
266
|
self._stagesurf.release()
|
|
267
|
+
self.__last_grab_entry = None
|
|
206
268
|
self._output.update_desc()
|
|
207
269
|
self.width, self.height = self._output.resolution
|
|
208
270
|
if not self._region_set_by_user:
|
|
@@ -303,6 +365,7 @@ class DXCamera:
|
|
|
303
365
|
self.__frame_time_ticks = None
|
|
304
366
|
self.__frame_count = 0
|
|
305
367
|
self.__latest_frame_ticks = None
|
|
368
|
+
self.__last_grab_entry = None
|
|
306
369
|
self.__head = 0
|
|
307
370
|
self.__tail = 0
|
|
308
371
|
self.__full = False
|
|
@@ -8,6 +8,10 @@ class _Timer:
|
|
|
8
8
|
def __init__(self):
|
|
9
9
|
self.period_s: float = 0.0
|
|
10
10
|
self._next_tick: Optional[float] = None
|
|
11
|
+
# Declared on the base type so static checkers can validate both
|
|
12
|
+
# modern and legacy timer paths through a shared interface.
|
|
13
|
+
self.cancelled: bool = False
|
|
14
|
+
self._handle: Optional[int] = None
|
|
11
15
|
|
|
12
16
|
|
|
13
17
|
if sys.version_info >= (3, 11):
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dxcam
|
|
3
|
-
Version: 0.1.0.
|
|
3
|
+
Version: 0.1.0.dev2
|
|
4
4
|
Summary: A Python high-performance screenshot library for Windows using Desktop Duplication API
|
|
5
5
|
Author: ra1nty
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Source, https://github.com/ra1nty/DXcam
|
|
8
8
|
Project-URL: Tracker, https://github.com/ra1nty/DXcam/issues
|
|
9
9
|
Keywords: screen,screenshot,screencapture,screengrab,windows
|
|
10
|
-
Classifier: Development Status ::
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
13
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
@@ -20,7 +20,7 @@ Classifier: Operating System :: Microsoft :: Windows :: Windows 11
|
|
|
20
20
|
Classifier: Topic :: Multimedia :: Graphics
|
|
21
21
|
Classifier: Topic :: Multimedia :: Graphics :: Capture
|
|
22
22
|
Classifier: Topic :: Multimedia :: Graphics :: Capture :: Screen Capture
|
|
23
|
-
Requires-Python: >=3.
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE
|
|
26
26
|
Requires-Dist: comtypes
|
|
@@ -88,8 +88,9 @@ camera = dxcam.create() # primary output on device 0
|
|
|
88
88
|
```python
|
|
89
89
|
frame = camera.grab()
|
|
90
90
|
```
|
|
91
|
+
`grab()` returns a `numpy.ndarray`. `None` if no new frame is available since the last capture, mainly for backward compatibility. You can use `new_frame_only=False` to change this behavior.
|
|
91
92
|
|
|
92
|
-
`
|
|
93
|
+
Use `copy=False` (or `camera.grab_view()`) for a zero-copy view.
|
|
93
94
|
|
|
94
95
|
To capture a region:
|
|
95
96
|
```python
|
|
@@ -117,6 +118,9 @@ for _ in range(1000):
|
|
|
117
118
|
Useful variants:
|
|
118
119
|
- `camera.get_latest_frame(with_timestamp=True)` -> `(frame, frame_timestamp)` -> return frame timestamp
|
|
119
120
|
- `camera.get_latest_frame_view()` -> zero-copy view into the frame buffer
|
|
121
|
+
- `camera.grab(copy=False)` / `camera.grab_view()` -> zero-copy latest-frame snapshot
|
|
122
|
+
|
|
123
|
+
** When `start()` capture is running, calling `grab()` reads from the in-memory ring buffer instead of directly polling DXGI.
|
|
120
124
|
|
|
121
125
|
## Advanced Usage and Remarks
|
|
122
126
|
### Multiple monitors / GPUs
|
|
@@ -211,14 +215,14 @@ camera.release()
|
|
|
211
215
|
```
|
|
212
216
|
|
|
213
217
|
## Benchmarks
|
|
214
|
-
When using a similar logic (only
|
|
218
|
+
When using a similar logic (only capture newly rendered frames) running on a 240fps output, ```DXCam, python-mss, D3DShot``` benchmarked as follow:
|
|
215
219
|
|
|
216
220
|
| | DXcam | python-mss | D3DShot |
|
|
217
221
|
|-------------|--------|------------|---------|
|
|
218
222
|
| Average FPS | 239.19 :checkered_flag: | 75.87 | 118.36 |
|
|
219
223
|
| Std Dev | 1.25 | 0.5447 | 0.3224 |
|
|
220
224
|
|
|
221
|
-
The benchmark is across 5 runs, with a light-moderate usage on my PC (5900X + 3090; Chrome ~30tabs, VS Code opened, etc.), I used the [Blur Buster UFO test](https://www.testufo.com/framerates#count=5&background=stars&pps=960) to constantly render 240 fps on my monitor
|
|
225
|
+
The benchmark is across 5 runs, with a light-moderate usage on my PC (5900X + 3090; Chrome ~30tabs, VS Code opened, etc.), I used the [Blur Buster UFO test](https://www.testufo.com/framerates#count=5&background=stars&pps=960) to constantly render 240 fps on my monitor. DXcam captured almost every frame rendered. You will see some benchmarks online claiming 1000+fps capture while most of them is busy-spinning a for loop on a staled frame (no new frame rendered on screen in test scenario).
|
|
222
226
|
|
|
223
227
|
### For Targeting FPS:
|
|
224
228
|
| (Target)\\(mean,std) | DXcam | python-mss | D3DShot |
|
|
@@ -4,15 +4,15 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "dxcam"
|
|
7
|
-
version = "0.1.0.
|
|
7
|
+
version = "0.1.0.dev2"
|
|
8
8
|
description = "A Python high-performance screenshot library for Windows using Desktop Duplication API"
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
11
|
license = "MIT"
|
|
12
12
|
authors = [{ name = "ra1nty" }]
|
|
13
13
|
keywords = ["screen", "screenshot", "screencapture", "screengrab", "windows"]
|
|
14
14
|
classifiers = [
|
|
15
|
-
"Development Status ::
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
16
|
"Intended Audience :: Developers",
|
|
17
17
|
"Programming Language :: Python :: 3",
|
|
18
18
|
"Programming Language :: Python :: 3 :: Only",
|
|
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
|