eye3 1.0.3__py3-none-win_amd64.whl
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.
- eye3/__init__.py +25 -0
- eye3/binding.py +165 -0
- eye3/thirdeye.dll +0 -0
- eye3-1.0.3.dist-info/METADATA +39 -0
- eye3-1.0.3.dist-info/RECORD +7 -0
- eye3-1.0.3.dist-info/WHEEL +5 -0
- eye3-1.0.3.dist-info/top_level.txt +1 -0
eye3/__init__.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""eye3 - screen capture bypassing WDA_MONITOR/WDA_EXCLUDEFROMCAPTURE.
|
|
2
|
+
|
|
3
|
+
Windows x64 native binary bundled in the wheel.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .binding import (
|
|
7
|
+
ThirdEyeError,
|
|
8
|
+
ThirdEyeOptions,
|
|
9
|
+
ThirdEyeSession,
|
|
10
|
+
ThirdeyeFormat,
|
|
11
|
+
ThirdeyeResult,
|
|
12
|
+
get_library_path,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__version__ = "1.0.0"
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"ThirdEyeError",
|
|
19
|
+
"ThirdEyeOptions",
|
|
20
|
+
"ThirdEyeSession",
|
|
21
|
+
"ThirdeyeFormat",
|
|
22
|
+
"ThirdeyeResult",
|
|
23
|
+
"get_library_path",
|
|
24
|
+
"__version__",
|
|
25
|
+
]
|
eye3/binding.py
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""ctypes binding to the bundled thirdeye.dll (Windows x64)."""
|
|
2
|
+
|
|
3
|
+
import ctypes
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
from enum import IntEnum
|
|
7
|
+
|
|
8
|
+
_DLL_NAME = "thirdeye.dll"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_library_path() -> str:
|
|
12
|
+
"""Absolute path to the bundled thirdeye.dll."""
|
|
13
|
+
path = os.path.join(os.path.dirname(__file__), _DLL_NAME)
|
|
14
|
+
if not os.path.exists(path):
|
|
15
|
+
raise ThirdEyeError(
|
|
16
|
+
f"thirdeye native library not found at {path}. "
|
|
17
|
+
"This wheel ships a Windows x64 binary only."
|
|
18
|
+
)
|
|
19
|
+
return path
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ThirdeyeResult(IntEnum):
|
|
23
|
+
OK = 0
|
|
24
|
+
ERROR_NOT_INITIALIZED = -1
|
|
25
|
+
ERROR_SYSCALL_INIT_FAILED = -2
|
|
26
|
+
ERROR_GDIPLUS_INIT_FAILED = -3
|
|
27
|
+
ERROR_ENCODER_NOT_FOUND = -4
|
|
28
|
+
ERROR_SAVE_FAILED = -5
|
|
29
|
+
ERROR_ALLOCATION_FAILED = -6
|
|
30
|
+
ERROR_INVALID_PARAM = -7
|
|
31
|
+
ERROR_NO_REMOTE_SECTION = -8
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ThirdeyeFormat(IntEnum):
|
|
35
|
+
JPEG = 0
|
|
36
|
+
PNG = 1
|
|
37
|
+
BMP = 2
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ThirdEyeError(RuntimeError):
|
|
41
|
+
"""Raised when a thirdeye call returns a non-OK result."""
|
|
42
|
+
|
|
43
|
+
def __init__(self, result, message: str):
|
|
44
|
+
self.result = result
|
|
45
|
+
super().__init__(f"{message} (result={int(result)})")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ThirdEyeOptions(ctypes.Structure):
|
|
49
|
+
_fields_ = [
|
|
50
|
+
("format", ctypes.c_int),
|
|
51
|
+
("quality", ctypes.c_int),
|
|
52
|
+
("bypassProtection", ctypes.c_int),
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
def __init__(self, format=ThirdeyeFormat.JPEG, quality=90, bypass_protection=True):
|
|
56
|
+
super().__init__(int(format), int(quality), 1 if bypass_protection else 0)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _load():
|
|
60
|
+
if sys.platform != "win32":
|
|
61
|
+
raise ThirdEyeError(
|
|
62
|
+
ThirdeyeResult.ERROR_NOT_INITIALIZED,
|
|
63
|
+
"thirdeye is supported on Windows x64 only",
|
|
64
|
+
)
|
|
65
|
+
lib = ctypes.WinDLL(get_library_path())
|
|
66
|
+
|
|
67
|
+
lib.Thirdeye_CreateContext.argtypes = [ctypes.POINTER(ctypes.c_void_p)]
|
|
68
|
+
lib.Thirdeye_CreateContext.restype = ctypes.c_int
|
|
69
|
+
|
|
70
|
+
lib.Thirdeye_DestroyContext.argtypes = [ctypes.c_void_p]
|
|
71
|
+
lib.Thirdeye_DestroyContext.restype = None
|
|
72
|
+
|
|
73
|
+
lib.Thirdeye_GetDefaultOptions.argtypes = [ctypes.POINTER(ThirdEyeOptions)]
|
|
74
|
+
lib.Thirdeye_GetDefaultOptions.restype = None
|
|
75
|
+
|
|
76
|
+
lib.Thirdeye_CaptureToFile.argtypes = [
|
|
77
|
+
ctypes.c_void_p,
|
|
78
|
+
ctypes.c_wchar_p,
|
|
79
|
+
ctypes.POINTER(ThirdEyeOptions),
|
|
80
|
+
]
|
|
81
|
+
lib.Thirdeye_CaptureToFile.restype = ctypes.c_int
|
|
82
|
+
|
|
83
|
+
lib.Thirdeye_CaptureToBuffer.argtypes = [
|
|
84
|
+
ctypes.c_void_p,
|
|
85
|
+
ctypes.POINTER(ctypes.POINTER(ctypes.c_uint8)),
|
|
86
|
+
ctypes.POINTER(ctypes.c_uint32),
|
|
87
|
+
ctypes.POINTER(ThirdEyeOptions),
|
|
88
|
+
]
|
|
89
|
+
lib.Thirdeye_CaptureToBuffer.restype = ctypes.c_int
|
|
90
|
+
|
|
91
|
+
lib.Thirdeye_FreeBuffer.argtypes = [ctypes.POINTER(ctypes.c_uint8)]
|
|
92
|
+
lib.Thirdeye_FreeBuffer.restype = None
|
|
93
|
+
|
|
94
|
+
lib.Thirdeye_GetLastError.argtypes = [ctypes.c_void_p]
|
|
95
|
+
lib.Thirdeye_GetLastError.restype = ctypes.c_char_p
|
|
96
|
+
|
|
97
|
+
lib.Thirdeye_GetVersion.argtypes = []
|
|
98
|
+
lib.Thirdeye_GetVersion.restype = ctypes.c_char_p
|
|
99
|
+
|
|
100
|
+
return lib
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class ThirdEyeSession:
|
|
104
|
+
"""Thread-safe-ish capture session. Use as a context manager."""
|
|
105
|
+
|
|
106
|
+
def __init__(self):
|
|
107
|
+
self._lib = _load()
|
|
108
|
+
self._ctx = ctypes.c_void_p()
|
|
109
|
+
rc = ThirdeyeResult(self._lib.Thirdeye_CreateContext(ctypes.byref(self._ctx)))
|
|
110
|
+
if rc != ThirdeyeResult.OK or not self._ctx:
|
|
111
|
+
raise ThirdEyeError(rc, "Thirdeye_CreateContext failed")
|
|
112
|
+
|
|
113
|
+
def default_options(self) -> ThirdEyeOptions:
|
|
114
|
+
opts = ThirdEyeOptions()
|
|
115
|
+
self._lib.Thirdeye_GetDefaultOptions(ctypes.byref(opts))
|
|
116
|
+
return opts
|
|
117
|
+
|
|
118
|
+
def capture_to_file(self, file_path: str, options: ThirdEyeOptions = None) -> None:
|
|
119
|
+
opts = options or self.default_options()
|
|
120
|
+
rc = ThirdeyeResult(
|
|
121
|
+
self._lib.Thirdeye_CaptureToFile(self._ctx, file_path, ctypes.byref(opts))
|
|
122
|
+
)
|
|
123
|
+
if rc != ThirdeyeResult.OK:
|
|
124
|
+
raise ThirdEyeError(rc, f"Thirdeye_CaptureToFile failed: {self.last_error()}")
|
|
125
|
+
|
|
126
|
+
def capture_to_buffer(self, options: ThirdEyeOptions = None) -> bytes:
|
|
127
|
+
opts = options or self.default_options()
|
|
128
|
+
buf = ctypes.POINTER(ctypes.c_uint8)()
|
|
129
|
+
size = ctypes.c_uint32(0)
|
|
130
|
+
rc = ThirdeyeResult(
|
|
131
|
+
self._lib.Thirdeye_CaptureToBuffer(
|
|
132
|
+
self._ctx, ctypes.byref(buf), ctypes.byref(size), ctypes.byref(opts)
|
|
133
|
+
)
|
|
134
|
+
)
|
|
135
|
+
if rc != ThirdeyeResult.OK or not buf or size.value == 0:
|
|
136
|
+
raise ThirdEyeError(rc, f"Thirdeye_CaptureToBuffer failed: {self.last_error()}")
|
|
137
|
+
try:
|
|
138
|
+
return ctypes.string_at(buf, size.value)
|
|
139
|
+
finally:
|
|
140
|
+
self._lib.Thirdeye_FreeBuffer(buf)
|
|
141
|
+
|
|
142
|
+
def last_error(self) -> str:
|
|
143
|
+
err = self._lib.Thirdeye_GetLastError(self._ctx)
|
|
144
|
+
return err.decode("utf-8", "replace") if err else ""
|
|
145
|
+
|
|
146
|
+
def version(self) -> str:
|
|
147
|
+
v = self._lib.Thirdeye_GetVersion()
|
|
148
|
+
return v.decode("utf-8", "replace") if v else ""
|
|
149
|
+
|
|
150
|
+
def close(self) -> None:
|
|
151
|
+
if self._ctx:
|
|
152
|
+
self._lib.Thirdeye_DestroyContext(self._ctx)
|
|
153
|
+
self._ctx = ctypes.c_void_p()
|
|
154
|
+
|
|
155
|
+
def __enter__(self) -> "ThirdEyeSession":
|
|
156
|
+
return self
|
|
157
|
+
|
|
158
|
+
def __exit__(self, *exc) -> None:
|
|
159
|
+
self.close()
|
|
160
|
+
|
|
161
|
+
def __del__(self):
|
|
162
|
+
try:
|
|
163
|
+
self.close()
|
|
164
|
+
except Exception:
|
|
165
|
+
pass
|
eye3/thirdeye.dll
ADDED
|
Binary file
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: eye3
|
|
3
|
+
Version: 1.0.3
|
|
4
|
+
Summary: Screen capture library bypassing WDA_MONITOR/WDA_EXCLUDEFROMCAPTURE (Windows x64 native binary)
|
|
5
|
+
Author: Matěj "lofcz" Štágl
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/lofcz/thirdeye
|
|
8
|
+
Project-URL: Repository, https://github.com/lofcz/thirdeye
|
|
9
|
+
Keywords: screenshot,screen-capture,windows,display-affinity,native
|
|
10
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Requires-Python: >=3.8
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# eye3
|
|
17
|
+
|
|
18
|
+
Screen capture library bypassing `WDA_MONITOR` / `WDA_EXCLUDEFROMCAPTURE` (Windows x64 native binary).
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from eye3 import ThirdEyeSession
|
|
22
|
+
|
|
23
|
+
with ThirdEyeSession() as session:
|
|
24
|
+
session.capture_to_file("screenshot.png")
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Options:
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from eye3 import ThirdEyeSession, ThirdEyeOptions, ThirdeyeFormat
|
|
31
|
+
|
|
32
|
+
with ThirdEyeSession() as session:
|
|
33
|
+
opts = ThirdEyeOptions(format=ThirdeyeFormat.JPEG, quality=90, bypass_protection=True)
|
|
34
|
+
session.capture_to_file("screenshot.jpeg", opts)
|
|
35
|
+
|
|
36
|
+
data: bytes = session.capture_to_buffer()
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Windows x64 only. Ships the native `thirdeye.dll` inside the wheel.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
eye3/__init__.py,sha256=hK74N2PEZpfUluBBfGYJr2Gsog0iQxURyx2GYCz4xuM,490
|
|
2
|
+
eye3/binding.py,sha256=oeCgba14y4xIoqEmiEust80744CNzOt_P-Ng41y0ws8,5356
|
|
3
|
+
eye3/thirdeye.dll,sha256=y3Yn-rvM2_7Fp_tEGPpciQYK4WRk-UsbzgX5Wm5X3T0,1071104
|
|
4
|
+
eye3-1.0.3.dist-info/METADATA,sha256=GjgPkt5THKMPR2HbuR6krlYBtVz3qzAySZwrVzdHfr4,1264
|
|
5
|
+
eye3-1.0.3.dist-info/WHEEL,sha256=hVx9elvUDfBjRmbl8JwIcXXikst35RZTZK9nspfI_28,98
|
|
6
|
+
eye3-1.0.3.dist-info/top_level.txt,sha256=49LwWwD5uM6wMwG-L9vs2w9tuJhPp01JvO51MR0jads,5
|
|
7
|
+
eye3-1.0.3.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
eye3
|