ok-script 0.0.7__py3-none-any.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.
Files changed (122) hide show
  1. autohelper/AutoHelper.py +116 -0
  2. autohelper/__init__.py +0 -0
  3. autohelper/capture/BaseCaptureMethod.py +30 -0
  4. autohelper/capture/HwndWindow.py +138 -0
  5. autohelper/capture/__init__.py +0 -0
  6. autohelper/capture/adb/ADBCaptureMethod.py +29 -0
  7. autohelper/capture/adb/DeviceManager.py +209 -0
  8. autohelper/capture/adb/__init__.py +0 -0
  9. autohelper/capture/adb/bin/__init__.py +0 -0
  10. autohelper/capture/adb/targets.py +61 -0
  11. autohelper/capture/adb/vbox.py +140 -0
  12. autohelper/capture/windows/WindowsGraphicsCaptureMethod.py +228 -0
  13. autohelper/capture/windows/__init__.py +0 -0
  14. autohelper/capture/windows/d3d11.py +74 -0
  15. autohelper/capture/windows/utils.py +149 -0
  16. autohelper/capture/windows/window.py +36 -0
  17. autohelper/color/Color.py +41 -0
  18. autohelper/color/__init__.py +0 -0
  19. autohelper/config/Config.py +79 -0
  20. autohelper/config/InfoDict.py +16 -0
  21. autohelper/config/__init__.py +0 -0
  22. autohelper/feature/Box.py +199 -0
  23. autohelper/feature/Feature.py +20 -0
  24. autohelper/feature/FeatureSet.py +207 -0
  25. autohelper/feature/FindFeature.py +52 -0
  26. autohelper/feature/__init__.py +0 -0
  27. autohelper/gui/App.py +120 -0
  28. autohelper/gui/Communicate.py +22 -0
  29. autohelper/gui/MainWindow.py +69 -0
  30. autohelper/gui/TabTitles.py +56 -0
  31. autohelper/gui/__init__.py +5 -0
  32. autohelper/gui/debug/AspectRatioWidget.py +31 -0
  33. autohelper/gui/debug/DebugTab.py +37 -0
  34. autohelper/gui/debug/FrameWidget.py +125 -0
  35. autohelper/gui/debug/InfoWidget.py +40 -0
  36. autohelper/gui/debug/LoggerWidget.py +74 -0
  37. autohelper/gui/debug/__init__.py +0 -0
  38. autohelper/gui/i18n/__init__.py +0 -0
  39. autohelper/gui/i18n/path.py +3 -0
  40. autohelper/gui/icon/__init__.py +0 -0
  41. autohelper/gui/icon/icon.py +6 -0
  42. autohelper/gui/loading/LoadingWindow.py +142 -0
  43. autohelper/gui/loading/__init__.py +0 -0
  44. autohelper/gui/overlay/OverlayWindow.py +34 -0
  45. autohelper/gui/overlay/__init__.py +0 -0
  46. autohelper/gui/resources.py +485 -0
  47. autohelper/gui/tasks/ConfigItemFactory.py +14 -0
  48. autohelper/gui/tasks/StartButton.py +47 -0
  49. autohelper/gui/tasks/TaskOpButton.py +33 -0
  50. autohelper/gui/tasks/TaskTab.py +121 -0
  51. autohelper/gui/tasks/TooltipTableWidget.py +39 -0
  52. autohelper/gui/tasks/__init__.py +0 -0
  53. autohelper/gui/util/Alert.py +20 -0
  54. autohelper/gui/util/InitWorker.py +17 -0
  55. autohelper/gui/util/__init__.py +0 -0
  56. autohelper/gui/widget/ListTableWidgetItem.py +27 -0
  57. autohelper/gui/widget/NumericTableWidgetItem.py +33 -0
  58. autohelper/gui/widget/RoundCornerContainer.py +59 -0
  59. autohelper/gui/widget/TabWidget.py +30 -0
  60. autohelper/gui/widget/UpdateConfigWidgetItem.py +16 -0
  61. autohelper/gui/widget/YesNonWidgetItem.py +23 -0
  62. autohelper/gui/widget/__init__.py +0 -0
  63. autohelper/interaction/ADBInteraction.py +33 -0
  64. autohelper/interaction/BaseInteraction.py +36 -0
  65. autohelper/interaction/Win32Interaction.py +56 -0
  66. autohelper/interaction/__init__.py +0 -0
  67. autohelper/logging/Logger.py +103 -0
  68. autohelper/logging/__init__.py +0 -0
  69. autohelper/ocr/OCR.py +50 -0
  70. autohelper/ocr/__init__.py +0 -0
  71. autohelper/predict/__init__.py +0 -0
  72. autohelper/predict/predict.py +45 -0
  73. autohelper/rotypes/Windows/Foundation/Collections.py +60 -0
  74. autohelper/rotypes/Windows/Foundation/__init__.py +153 -0
  75. autohelper/rotypes/Windows/Globalization/__init__.py +25 -0
  76. autohelper/rotypes/Windows/Graphics/Capture/__init__.py +110 -0
  77. autohelper/rotypes/Windows/Graphics/DirectX/Direct3D11/__init__.py +41 -0
  78. autohelper/rotypes/Windows/Graphics/DirectX/__init__.py +167 -0
  79. autohelper/rotypes/Windows/Graphics/Imaging/__init__.py +34 -0
  80. autohelper/rotypes/Windows/Graphics/__init__.py +5 -0
  81. autohelper/rotypes/Windows/Media/Ocr/__init__.py +76 -0
  82. autohelper/rotypes/Windows/Media/__init__.py +0 -0
  83. autohelper/rotypes/Windows/Security/Cryptography/__init__.py +18 -0
  84. autohelper/rotypes/Windows/Security/__init__.py +0 -0
  85. autohelper/rotypes/Windows/Storage/Streams/__init__.py +27 -0
  86. autohelper/rotypes/Windows/Storage/__init__.py +0 -0
  87. autohelper/rotypes/Windows/__init__.py +0 -0
  88. autohelper/rotypes/__init__.py +4 -0
  89. autohelper/rotypes/delegate.py +105 -0
  90. autohelper/rotypes/export.py +21 -0
  91. autohelper/rotypes/idldsl.py +272 -0
  92. autohelper/rotypes/inspectable.py +79 -0
  93. autohelper/rotypes/roapi.py +30 -0
  94. autohelper/rotypes/types.py +77 -0
  95. autohelper/rotypes/winstring.py +44 -0
  96. autohelper/save/BlackBarProcessor.py +25 -0
  97. autohelper/save/PostProcessor.py +6 -0
  98. autohelper/save/SaveByInterval.py +30 -0
  99. autohelper/save/SaveByKeyPress.py +27 -0
  100. autohelper/save/SaveMethodBase.py +46 -0
  101. autohelper/save/__init__.py +0 -0
  102. autohelper/scene/FeatureScene.py +14 -0
  103. autohelper/scene/Scene.py +21 -0
  104. autohelper/scene/__init__.py +0 -0
  105. autohelper/stats/StreamStats.py +34 -0
  106. autohelper/stats/__init__.py +0 -0
  107. autohelper/task/BaseTask.py +109 -0
  108. autohelper/task/ExecutorOperation.py +119 -0
  109. autohelper/task/FindFeatureTask.py +6 -0
  110. autohelper/task/TaskExecutor.py +249 -0
  111. autohelper/task/__init__.py +0 -0
  112. autohelper/util/__init__.py +0 -0
  113. autohelper/util/json.py +27 -0
  114. autohelper/util/list.py +2 -0
  115. autohelper/util/path.py +37 -0
  116. autohelper/util/thread.py +12 -0
  117. autohelper/util/win32_process.py +86 -0
  118. autohelper/util/yaml.py +28 -0
  119. ok_script-0.0.7.dist-info/METADATA +64 -0
  120. ok_script-0.0.7.dist-info/RECORD +122 -0
  121. ok_script-0.0.7.dist-info/WHEEL +5 -0
  122. ok_script-0.0.7.dist-info/top_level.txt +1 -0
@@ -0,0 +1,140 @@
1
+ # https://github.com/ArknightsAutoHelper/ArknightsAutoHelper/blob/5a9284773b68e12012c338cce50cb732f6a3542c/automator/control/adb/targets/vbox.py
2
+
3
+ import winreg
4
+ from dataclasses import dataclass
5
+
6
+ import adbutils
7
+ import win32com.client
8
+
9
+ from autohelper.capture.adb.targets import ADBControllerTarget
10
+ from autohelper.logging.Logger import get_logger
11
+ from autohelper.util import win32_process
12
+
13
+ logger = get_logger(__name__)
14
+
15
+
16
+ @dataclass
17
+ class VBoxClass:
18
+ clsid: str
19
+ vendor: str
20
+ tag: str
21
+
22
+
23
+ @dataclass
24
+ class VBoxSerever(VBoxClass):
25
+ path: str
26
+ pid: int = 0
27
+
28
+
29
+ import ctypes
30
+
31
+ vbox_clsids = [
32
+ VBoxClass('{20190809-47b9-4a1e-82b2-07ccd5323c3f}', 'LDPlayer', 'ld'),
33
+ VBoxClass('{20191216-47b9-4a1e-82b2-07ccd5323c3f}', 'LDPlayer9', 'ld9'),
34
+ VBoxClass('{88888888-47b9-4a1e-82b2-07ccd5323c3f}', 'Nox', 'nox'),
35
+ VBoxClass('{baf3f651-58d8-429d-97ad-2b5699b43567}', 'BlueStacks', 'bstk'),
36
+ VBoxClass('{b1a7a4f2-47b9-4a1e-82b2-07ccd5323c3a}', 'Memu', 'memu'),
37
+ VBoxClass('{81919390-a492-11e5-a837-0800200c9a66}', 'MuMu', 'mumu'),
38
+ VBoxClass('{23cd1535-edaa-4f21-a4ab-45d97fd1d58b}', 'MuMu 12',
39
+ 'mumu 12'),
40
+ # ('{B1A7A4F2-47B9-4A1E-82B2-07CCD5323C3F}', 'Oracle', 'oracle'), # nobody uses this
41
+ ]
42
+
43
+ OpenProcess = ctypes.windll.kernel32.OpenProcess
44
+ OpenProcess.argtypes = [ctypes.c_uint32, ctypes.c_bool, ctypes.c_uint32]
45
+ OpenProcess.restype = ctypes.c_void_p
46
+ PROCESS_QUERY_INFORMATION = 0x0400
47
+ CloseHandle = ctypes.windll.kernel32.CloseHandle
48
+ CloseHandle.argtypes = [ctypes.c_void_p]
49
+
50
+
51
+ def get_server(clsid):
52
+ try:
53
+ with winreg.OpenKeyEx(winreg.HKEY_CLASSES_ROOT, 'CLSID\\%s\\LocalServer32' % clsid, 0,
54
+ winreg.KEY_READ) as hkey:
55
+ value = winreg.QueryValue(hkey, None)
56
+ filename = value.strip('"')
57
+ return win32_process.get_final_path(filename)
58
+ except OSError:
59
+ return None
60
+
61
+
62
+ # current process's privilege must equal to the target process's privilege, can raise a error if not
63
+ def installed_emulator():
64
+ logger.debug('enumerating vbox targets')
65
+ vbox_servers: list[VBoxSerever] = []
66
+ for vboxcls in vbox_clsids:
67
+ server = get_server(vboxcls.clsid)
68
+ if server:
69
+ logger.debug(f"enumerating vbox {server}")
70
+ vbox_servers.append(VBoxSerever(vboxcls.clsid, vboxcls.vendor, vboxcls.tag, server))
71
+
72
+ logger.debug(f'installed VirtualBox servers: {vbox_servers}')
73
+
74
+ running_processes = {pid: win32_process.resolve_image_name(pid) for pid in win32_process.all_pids()}
75
+ # logger.debug(f'running processes: {running_processes}')
76
+ for pid, path in running_processes.items():
77
+ # logger.debug(f'running_processes: {pid, path}')
78
+ for vbox_server in vbox_servers:
79
+ if vbox_server.path == path:
80
+ vbox_server.pid = pid
81
+ logger.debug(f'found vbox server: {vbox_server} {pid, path}')
82
+ break
83
+ running_servers = [x for x in vbox_servers if x.pid]
84
+ # running_servers = [x for x in vbox_servers if x[3] in running_processes]
85
+
86
+ logger.debug(f'running VirtualBox servers: {running_servers}')
87
+ results = []
88
+ for server in running_servers:
89
+ logger.debug(f'checking {server}')
90
+ hproc = OpenProcess(PROCESS_QUERY_INFORMATION, False, server.pid)
91
+ if not hproc:
92
+ logger.debug(f'try OpenProcess failed for PID {server.pid}')
93
+ continue
94
+ CloseHandle(hproc)
95
+ logger.debug(f'checking hproc end {server}')
96
+ try:
97
+ client = win32com.client.Dispatch(server.clsid)
98
+ for machine in client.Machines:
99
+ machine_name = machine.Name
100
+ logger.debug(f'checking machine {machine_name}')
101
+ if not machine.State == 5:
102
+ # machine is not running
103
+ continue
104
+ for adapter_id in range(4):
105
+ adapter = machine.GetNetworkAdapter(adapter_id)
106
+ if adapter.Enabled and adapter.AttachmentType == 1:
107
+ # adapter is enabled and attached to per-machine NAT
108
+ forwards = adapter.NatEngine.Redirects
109
+ for forward in forwards:
110
+ rule_name, proto, host, port, guest_host, guest_port = forward.split(',', 5)
111
+ if proto == '1' and guest_port == '5555':
112
+ address = f'127.0.0.1:{port}'
113
+ identifier = f'vbox:{server.tag}:{machine_name}'
114
+ logger.debug(
115
+ f'adb_server, None, {server.vendor}: {machine_name}, {address}, 2, 1, override_identifier={identifier}')
116
+ results.append(
117
+ ADBControllerTarget(None, f'{server.vendor}',
118
+ address, 2, 1, override_identifier=identifier,
119
+ preload_device_info={'emulator_hypervisor': 'vbox'}))
120
+ logger.debug(f'try checking {server}')
121
+ except Exception as e:
122
+ logger.error(f'failed to check server :{server} {e}', e)
123
+ return results
124
+
125
+
126
+ def _main():
127
+ logger.config({'debug': True})
128
+ adb = adbutils.AdbClient(host="127.0.0.1", port=5037)
129
+ for x in installed_emulator():
130
+ logger.debug(f'installed emulators {x}')
131
+ adb.connect(x.adb_address)
132
+
133
+ logger.debug(adb.device_list())
134
+ for device in adb.device_list():
135
+ logger.debug(device.shell('settings get secure android_id'))
136
+ logger.debug(f'{device.info}')
137
+
138
+
139
+ if __name__ == '__main__':
140
+ _main()
@@ -0,0 +1,228 @@
1
+ # original https://github.com/dantmnf & https://github.com/hakaboom/winAuto
2
+ import ctypes
3
+ import ctypes.wintypes
4
+ import time
5
+
6
+ import numpy as np
7
+ from typing_extensions import override
8
+
9
+ from autohelper.capture.BaseCaptureMethod import BaseCaptureMethod
10
+ from autohelper.capture.HwndWindow import HwndWindow
11
+ from autohelper.capture.windows import d3d11
12
+ from autohelper.capture.windows.utils import WINDOWS_BUILD_NUMBER
13
+ from autohelper.logging.Logger import get_logger
14
+ from autohelper.rotypes import IInspectable
15
+ from autohelper.rotypes.Windows.Foundation import TypedEventHandler
16
+ from autohelper.rotypes.Windows.Graphics.Capture import Direct3D11CaptureFramePool, IGraphicsCaptureItemInterop, \
17
+ IGraphicsCaptureItem, GraphicsCaptureItem
18
+ from autohelper.rotypes.Windows.Graphics.DirectX import DirectXPixelFormat
19
+ from autohelper.rotypes.Windows.Graphics.DirectX.Direct3D11 import IDirect3DDevice, \
20
+ CreateDirect3D11DeviceFromDXGIDevice, \
21
+ IDirect3DDxgiInterfaceAccess
22
+ from autohelper.rotypes.roapi import GetActivationFactory
23
+
24
+ PBYTE = ctypes.POINTER(ctypes.c_ubyte)
25
+ WGC_NO_BORDER_MIN_BUILD = 20348
26
+
27
+ logger = get_logger(__name__)
28
+
29
+
30
+ class WindowsGraphicsCaptureMethod(BaseCaptureMethod):
31
+ name = "Windows Graphics Capture"
32
+ description = "fast, most compatible, capped at 60fps"
33
+ last_frame = None
34
+ last_frame_time = 0
35
+ hwnd_window: HwndWindow = None
36
+ frame_pool = None
37
+ item = None
38
+ session = None
39
+ cputex = None
40
+
41
+ def __init__(self, hwnd_window: HwndWindow):
42
+ super().__init__()
43
+ self.hwnd_window = hwnd_window
44
+
45
+ def frame_arrived_callback(self, x, y):
46
+ try:
47
+ frame = self.frame_pool.TryGetNextFrame()
48
+ except Exception as e:
49
+ logger.error(f"TryGetNextFrame error {e}")
50
+ self.close()
51
+ return
52
+ if not frame:
53
+ return
54
+ self.last_frame_time = time.time()
55
+ img = None
56
+ with frame:
57
+ need_reset_framepool = False
58
+ need_reset_device = False
59
+ if frame.ContentSize.Width != self.last_size.Width or frame.ContentSize.Height != self.last_size.Height:
60
+ # print('size changed')
61
+ need_reset_framepool = True
62
+ self.last_size = frame.ContentSize
63
+
64
+ if need_reset_framepool:
65
+ self.reset_framepool(frame.ContentSize)
66
+ return
67
+ tex = None
68
+
69
+ cputex = None
70
+ try:
71
+ tex = frame.Surface.astype(IDirect3DDxgiInterfaceAccess).GetInterface(
72
+ d3d11.ID3D11Texture2D.GUID).astype(d3d11.ID3D11Texture2D)
73
+ desc = tex.GetDesc()
74
+ desc2 = d3d11.D3D11_TEXTURE2D_DESC()
75
+ desc2.Width = desc.Width
76
+ desc2.Height = desc.Height
77
+ desc2.MipLevels = desc.MipLevels
78
+ desc2.ArraySize = desc.ArraySize
79
+ desc2.Format = desc.Format
80
+ desc2.SampleDesc = desc.SampleDesc
81
+ desc2.Usage = d3d11.D3D11_USAGE_STAGING
82
+ desc2.CPUAccessFlags = d3d11.D3D11_CPU_ACCESS_READ
83
+ desc2.BindFlags = 0
84
+ desc2.MiscFlags = 0
85
+ cputex = self.dxdevice.CreateTexture2D(ctypes.byref(desc2), None)
86
+ self.immediatedc.CopyResource(cputex, tex)
87
+ mapinfo = self.immediatedc.Map(cputex, 0, d3d11.D3D11_MAP_READ, 0)
88
+ img = np.ctypeslib.as_array(ctypes.cast(mapinfo.pData, PBYTE),
89
+ (desc.Height, mapinfo.RowPitch // 4, 4))[
90
+ :, :desc.Width].copy()
91
+ self.immediatedc.Unmap(cputex, 0)
92
+ except OSError as e:
93
+ if e.winerror == d3d11.DXGI_ERROR_DEVICE_REMOVED or e.winerror == d3d11.DXGI_ERROR_DEVICE_RESET:
94
+ need_reset_framepool = True
95
+ need_reset_device = True
96
+ else:
97
+ raise
98
+ finally:
99
+ if tex is not None:
100
+ tex.Release()
101
+ if cputex is not None:
102
+ cputex.Release()
103
+ if need_reset_framepool:
104
+ self.reset_framepool(frame.ContentSize, need_reset_device)
105
+ return self.get_frame()
106
+ self.last_frame = img
107
+
108
+ def start_or_stop(self, capture_cursor=False):
109
+ if self.hwnd_window.exists and self.frame_pool is None:
110
+ logger.info('init windows capture')
111
+ self.rtdevice = IDirect3DDevice()
112
+ self.dxdevice = d3d11.ID3D11Device()
113
+ self.immediatedc = d3d11.ID3D11DeviceContext()
114
+ self.create_device()
115
+ interop = GetActivationFactory('Windows.Graphics.Capture.GraphicsCaptureItem').astype(
116
+ IGraphicsCaptureItemInterop)
117
+ item = interop.CreateForWindow(self.hwnd_window.hwnd, IGraphicsCaptureItem.GUID)
118
+ self.item = item
119
+ self.last_size = item.Size
120
+ delegate = TypedEventHandler(GraphicsCaptureItem, IInspectable).delegate(
121
+ self.close)
122
+ self.evtoken = item.add_Closed(delegate)
123
+ self.frame_pool = Direct3D11CaptureFramePool.CreateFreeThreaded(self.rtdevice,
124
+ DirectXPixelFormat.B8G8R8A8UIntNormalized,
125
+ 1, item.Size)
126
+ self.session = self.frame_pool.CreateCaptureSession(item)
127
+ pool = self.frame_pool
128
+ pool.add_FrameArrived(
129
+ TypedEventHandler(Direct3D11CaptureFramePool, IInspectable).delegate(
130
+ self.frame_arrived_callback))
131
+ self.session.IsCursorCaptureEnabled = capture_cursor
132
+ if WINDOWS_BUILD_NUMBER >= WGC_NO_BORDER_MIN_BUILD:
133
+ self.session.IsBorderRequired = False
134
+ self.session.StartCapture()
135
+ elif not self.hwnd_window.exists and self.frame_pool is not None:
136
+ self.close()
137
+ return self.hwnd_window.exists
138
+
139
+ def create_device(self):
140
+ d3d11.D3D11CreateDevice(
141
+ None,
142
+ d3d11.D3D_DRIVER_TYPE_HARDWARE,
143
+ None,
144
+ d3d11.D3D11_CREATE_DEVICE_BGRA_SUPPORT,
145
+ None,
146
+ 0,
147
+ d3d11.D3D11_SDK_VERSION,
148
+ ctypes.byref(self.dxdevice),
149
+ None,
150
+ ctypes.byref(self.immediatedc)
151
+ )
152
+ self.rtdevice = CreateDirect3D11DeviceFromDXGIDevice(self.dxdevice)
153
+ self.evtoken = None
154
+
155
+ @override
156
+ def close(self):
157
+ logger.info('destroy windows capture')
158
+ if self.frame_pool is not None:
159
+ self.frame_pool.Close()
160
+ self.frame_pool = None
161
+ if self.session is not None:
162
+ self.session.Close() # E_UNEXPECTED ???
163
+ self.session = None
164
+ self.item = None
165
+ self.rtdevice.Release()
166
+ self.dxdevice.Release()
167
+ if self.cputex:
168
+ self.cputex.Release()
169
+
170
+ def get_frame(self):
171
+ if self.start_or_stop():
172
+ frame = self.last_frame
173
+ self.last_frame = None
174
+ latency = time.time() - self.last_frame_time
175
+ self.last_frame_time = time.time()
176
+ if latency > 1:
177
+ logger.warning(f"latency too large return None frame: {latency}")
178
+ return None
179
+ if frame is not None:
180
+ x, y = self.hwnd_window.get_top_left_frame_offset()
181
+ if x > 0 or y > 0:
182
+ frame = crop_image(frame, x, y)
183
+
184
+ if frame is not None:
185
+ new_height, new_width = frame.shape[:2]
186
+ if new_width <= 0 or new_width <= 0:
187
+ logger.warning(f"get_frame size <=0 {new_width}x{new_height}")
188
+ frame = None
189
+ else:
190
+ self.width = new_width
191
+ self.height = new_height
192
+ if frame.shape[2] == 4:
193
+ frame = frame[:, :, :3]
194
+ return frame
195
+
196
+ def reset_framepool(self, size, reset_device=False):
197
+ if reset_device:
198
+ self.create_device()
199
+ self.frame_pool.Recreate(self.rtdevice,
200
+ DirectXPixelFormat.B8G8R8A8UIntNormalized, 1, size)
201
+
202
+ def get_abs_cords(self, x, y):
203
+ return self.hwnd_window.get_abs_cords(x, y)
204
+
205
+ def clickable(self):
206
+ return self.hwnd_window.visible
207
+
208
+
209
+ def crop_image(image, border, title_height):
210
+ # Load the image
211
+ # Image dimensions
212
+ height, width = image.shape[:2]
213
+
214
+ # Calculate the coordinates for the bottom-right corner
215
+ x2 = width - border
216
+ y2 = height - border
217
+
218
+ # Crop the image
219
+ cropped_image = image[title_height:y2, border:x2]
220
+
221
+ # print(f"cropped image: {title_height}-{y2}, {border}-{x2} {cropped_image.shape}")
222
+ #
223
+ # cv2.imshow('Image Window', cropped_image)
224
+ #
225
+ # # Wait for any key to be pressed before closing the window
226
+ # cv2.waitKey(0)
227
+
228
+ return cropped_image
File without changes
@@ -0,0 +1,74 @@
1
+ import ctypes.wintypes
2
+
3
+ from autohelper.rotypes import IUnknown
4
+ from autohelper.rotypes import idldsl
5
+
6
+ D3D11_SDK_VERSION = 7
7
+ D3D_DRIVER_TYPE_HARDWARE = 1
8
+ D3D11_CREATE_DEVICE_BGRA_SUPPORT = 0x20
9
+ D3D11_USAGE_STAGING = 3
10
+ D3D11_CPU_ACCESS_READ = 0x20000
11
+ D3D11_MAP_READ = 1
12
+ DXGI_ERROR_DEVICE_REMOVED = 0x887A0005
13
+ DXGI_ERROR_DEVICE_RESET = 0x887A0007
14
+
15
+
16
+ class DXGI_SAMPLE_DESC(ctypes.Structure):
17
+ _fields_ = [
18
+ ('Count', ctypes.wintypes.UINT),
19
+ ('Quality', ctypes.wintypes.UINT)
20
+ ]
21
+
22
+
23
+ class D3D11_TEXTURE2D_DESC(ctypes.Structure):
24
+ _fields_ = [
25
+ ('Width', ctypes.wintypes.UINT),
26
+ ('Height', ctypes.wintypes.UINT),
27
+ ('MipLevels', ctypes.wintypes.UINT),
28
+ ('ArraySize', ctypes.wintypes.UINT),
29
+ ('Format', ctypes.wintypes.UINT),
30
+ ('SampleDesc', DXGI_SAMPLE_DESC),
31
+ ('Usage', ctypes.wintypes.UINT),
32
+ ('BindFlags', ctypes.wintypes.UINT),
33
+ ('CPUAccessFlags', ctypes.wintypes.UINT),
34
+ ('MiscFlags', ctypes.wintypes.UINT),
35
+ ]
36
+
37
+
38
+ class D3D11_MAPPED_SUBRESOURCE(ctypes.Structure):
39
+ _fields_ = [
40
+ ('pData', ctypes.c_void_p),
41
+ ('RowPitch', ctypes.wintypes.UINT),
42
+ ('DepthPitch', ctypes.wintypes.UINT)
43
+ ]
44
+
45
+
46
+ @idldsl.GUID('db6f6ddb-ac77-4e88-8253-819df9bbf140')
47
+ class ID3D11Device(IUnknown):
48
+ pass
49
+
50
+
51
+ @idldsl.GUID('6f15aaf2-d208-4e89-9ab4-489535d34f9c')
52
+ class ID3D11Texture2D(IUnknown):
53
+ pass
54
+
55
+
56
+ @idldsl.GUID('c0bfa96c-e089-44fb-8eaf-26f8796190da')
57
+ class ID3D11DeviceContext(IUnknown):
58
+ pass
59
+
60
+
61
+ idldsl.define_winrt_com_method(ID3D11Device, 'CreateTexture2D', ctypes.POINTER(D3D11_TEXTURE2D_DESC), ctypes.c_void_p,
62
+ retval=ID3D11Texture2D, vtbl=5)
63
+ idldsl.define_winrt_com_method(ID3D11Device, 'GetImmediateContext', retval=ID3D11DeviceContext, vtbl=40)
64
+ # idldsl.define_winrt_com_method(ID3D11Texture2D, 'GetDevice', retval=ID3D11Texture2D, vtbl=3)
65
+ idldsl.define_winrt_com_method(ID3D11Texture2D, 'GetDesc', retval=D3D11_TEXTURE2D_DESC, vtbl=10)
66
+ idldsl.define_winrt_com_method(ID3D11DeviceContext, 'Map', IUnknown, ctypes.c_uint, ctypes.c_int, ctypes.c_uint,
67
+ retval=D3D11_MAPPED_SUBRESOURCE, vtbl=14)
68
+ idldsl.define_winrt_com_method(ID3D11DeviceContext, 'Unmap', IUnknown, ctypes.c_uint, vtbl=15)
69
+ idldsl.define_winrt_com_method(ID3D11DeviceContext, 'CopyResource', IUnknown, IUnknown, vtbl=47)
70
+
71
+ D3D11CreateDevice = ctypes.oledll.d3d11.D3D11CreateDevice
72
+ D3D11CreateDevice.argtypes = (
73
+ IUnknown, ctypes.c_int, ctypes.c_void_p, ctypes.wintypes.UINT, ctypes.c_void_p, ctypes.c_uint, ctypes.c_uint,
74
+ ID3D11Device, ctypes.c_void_p, ID3D11DeviceContext)
@@ -0,0 +1,149 @@
1
+ import asyncio
2
+ import ctypes
3
+ import ctypes.wintypes
4
+ import os
5
+ import sys
6
+ from collections.abc import Callable, Iterable
7
+ from enum import IntEnum
8
+ from itertools import chain
9
+ from platform import version
10
+ from threading import Thread
11
+ from typing import TYPE_CHECKING, Any, TypeGuard, TypeVar
12
+
13
+ import numpy as np
14
+ import win32gui
15
+ import win32ui
16
+
17
+ if TYPE_CHECKING:
18
+ # Source does not exist, keep this under TYPE_CHECKING
19
+ from _win32typing import PyCDC # pyright: ignore[reportMissingModuleSource]
20
+
21
+ T = TypeVar("T")
22
+
23
+ ONE_SECOND = 1000
24
+ """1000 milliseconds in 1 second"""
25
+ DWMWA_EXTENDED_FRAME_BOUNDS = 9
26
+ MAXBYTE = 255
27
+ BGR_CHANNEL_COUNT = 3
28
+ """How many channels in a BGR image"""
29
+ BGRA_CHANNEL_COUNT = 4
30
+ """How many channels in a BGRA image"""
31
+
32
+
33
+ class ImageShape(IntEnum):
34
+ Y = 0
35
+ X = 1
36
+ Channels = 2
37
+
38
+
39
+ class ColorChannel(IntEnum):
40
+ Blue = 0
41
+ Green = 1
42
+ Red = 2
43
+ Alpha = 3
44
+
45
+
46
+ def decimal(value: float):
47
+ # Using ljust instead of :2f because of python float rounding errors
48
+ return f"{int(value * 100) / 100}".ljust(4, "0")
49
+
50
+
51
+ def is_digit(value: str | int | None):
52
+ """Checks if `value` is a single-digit string from 0-9."""
53
+ if value is None:
54
+ return False
55
+ try:
56
+ return 0 <= int(value) <= 9 # noqa: PLR2004
57
+ except (ValueError, TypeError):
58
+ return False
59
+
60
+
61
+ def is_valid_image(image: np.ndarray | None) -> TypeGuard[np.ndarray]:
62
+ return image is not None and bool(image.size)
63
+
64
+
65
+ def is_valid_hwnd(hwnd: int):
66
+ """Validate the hwnd points to a valid window and not the desktop or whatever window obtained with `""`."""
67
+ if not hwnd:
68
+ return False
69
+ if sys.platform == "win32":
70
+ return bool(win32gui.IsWindow(hwnd) and win32gui.GetWindowText(hwnd))
71
+ return True
72
+
73
+
74
+ def first(iterable: Iterable[T]) -> T:
75
+ """@return: The first element of a collection. Dictionaries will return the first key."""
76
+ return next(iter(iterable))
77
+
78
+
79
+ def try_delete_dc(dc: "PyCDC"):
80
+ try:
81
+ dc.DeleteDC()
82
+ except win32ui.error:
83
+ pass
84
+
85
+
86
+ def get_window_bounds(hwnd: int) -> tuple[int, int, int, int]:
87
+ extended_frame_bounds = ctypes.wintypes.RECT()
88
+ ctypes.windll.dwmapi.DwmGetWindowAttribute(
89
+ hwnd,
90
+ DWMWA_EXTENDED_FRAME_BOUNDS,
91
+ ctypes.byref(extended_frame_bounds),
92
+ ctypes.sizeof(extended_frame_bounds),
93
+ )
94
+
95
+ window_rect = win32gui.GetWindowRect(hwnd)
96
+ window_left_bounds = extended_frame_bounds.left - window_rect[0]
97
+ window_top_bounds = extended_frame_bounds.top - window_rect[1]
98
+ window_width = extended_frame_bounds.right - extended_frame_bounds.left
99
+ window_height = extended_frame_bounds.bottom - extended_frame_bounds.top
100
+ return window_left_bounds, window_top_bounds, window_width, window_height
101
+
102
+
103
+ def open_file(file_path: str | bytes | os.PathLike[str] | os.PathLike[bytes]):
104
+ os.startfile(file_path) # noqa: S606
105
+
106
+
107
+ def get_or_create_eventloop():
108
+ try:
109
+ return asyncio.get_event_loop()
110
+ except RuntimeError:
111
+ loop = asyncio.new_event_loop()
112
+ asyncio.set_event_loop(loop)
113
+ return asyncio.get_event_loop()
114
+
115
+
116
+ def fire_and_forget(func: Callable[..., Any]):
117
+ """
118
+ Runs synchronous function asynchronously without waiting for a response.
119
+
120
+ Uses threads on Windows because ~~`RuntimeError: There is no current event loop in thread 'MainThread'.`~~
121
+ Because maybe asyncio has issues. Unsure. See alpha.5 and https://github.com/Avasam/AutoSplit/issues/36
122
+
123
+ Uses asyncio on Linux because of a `Segmentation fault (core dumped)`
124
+ """
125
+
126
+ def wrapped(*args: Any, **kwargs: Any):
127
+ if sys.platform == "win32":
128
+ thread = Thread(target=func, args=args, kwargs=kwargs)
129
+ thread.start()
130
+ return thread
131
+ return get_or_create_eventloop().run_in_executor(None, func, *args, *kwargs)
132
+
133
+ return wrapped
134
+
135
+
136
+ def flatten(nested_iterable: Iterable[Iterable[T]]) -> chain[T]:
137
+ return chain.from_iterable(nested_iterable)
138
+
139
+
140
+ # Environment specifics
141
+ WINDOWS_BUILD_NUMBER = int(version().split(".")[-1]) if sys.platform == "win32" else -1
142
+ FIRST_WIN_11_BUILD = 22000
143
+ """AutoSplit Version number"""
144
+ WGC_MIN_BUILD = 17134
145
+ """https://docs.microsoft.com/en-us/uwp/api/windows.graphics.capture.graphicscapturepicker#applies-to"""
146
+ FROZEN = hasattr(sys, "frozen")
147
+ """Running from build made by PyInstaller"""
148
+ auto_split_directory = os.path.dirname(sys.executable if FROZEN else os.path.abspath(__file__))
149
+ """The directory of either the AutoSplit executable or AutoSplit.py"""
@@ -0,0 +1,36 @@
1
+ import ctypes
2
+ import ctypes.wintypes
3
+
4
+ import win32gui
5
+
6
+ user32 = ctypes.WinDLL('user32', use_last_error=True)
7
+
8
+ DWMWA_EXTENDED_FRAME_BOUNDS = 9
9
+
10
+
11
+ def is_window_minimized(hWnd):
12
+ return user32.IsIconic(hWnd) != 0
13
+
14
+
15
+ def get_window_bounds(hwnd):
16
+ extended_frame_bounds = ctypes.wintypes.RECT()
17
+ ctypes.windll.dwmapi.DwmGetWindowAttribute(
18
+ hwnd,
19
+ DWMWA_EXTENDED_FRAME_BOUNDS,
20
+ ctypes.byref(extended_frame_bounds),
21
+ ctypes.sizeof(extended_frame_bounds),
22
+ )
23
+ scaling = user32.GetDpiForWindow(hwnd) / 96
24
+ client_x, client_y, client_width, client_height = win32gui.GetClientRect(hwnd)
25
+ client_width = int(client_width / scaling)
26
+ client_height = int(client_height / scaling)
27
+ window_width = int((extended_frame_bounds.right - extended_frame_bounds.left) / scaling)
28
+ window_height = int((extended_frame_bounds.bottom - extended_frame_bounds.top) / scaling)
29
+ border = int((window_width - client_width) / 2)
30
+ title = window_height - client_height - border
31
+ return int(extended_frame_bounds.left / scaling), int(
32
+ extended_frame_bounds.top / scaling), border, title, client_width, client_height, scaling
33
+
34
+
35
+ def is_foreground_window(hwnd):
36
+ return win32gui.IsWindowVisible(hwnd) and win32gui.GetForegroundWindow() == hwnd
@@ -0,0 +1,41 @@
1
+ import cv2
2
+
3
+ black_color = {
4
+ 'r': (0, 0), # Red range
5
+ 'g': (0, 0), # Green range
6
+ 'b': (0, 0) # Blue range
7
+ }
8
+
9
+ white_color = {
10
+ 'r': (255, 255), # Red range
11
+ 'g': (255, 255), # Green range
12
+ 'b': (255, 255) # Blue range
13
+ }
14
+
15
+
16
+ def calculate_color_percentage(image, color_ranges, box=None):
17
+ # Check if the ROI is within the image bounds
18
+ if box is not None:
19
+ if (box.x >= 0 and box.y >= 0 and
20
+ box.x + box.width <= image.shape[1] and # image.shape[1] is the width of the image
21
+ box.y + box.height <= image.shape[0]): # image.shape[0] is the height of the image
22
+
23
+ # Extract the region of interest (ROI) using slicing
24
+
25
+ image = image[box.y:box.y + box.height, box.x:box.x + box.width, :3]
26
+ else:
27
+ # Return some error value or raise an exception
28
+ # For example, return 0 or None
29
+ return 0 # or None, or raise an exception
30
+ else:
31
+ image = image[:, :, :3]
32
+ # Create a mask for the pixels within the desired color range
33
+ mask = cv2.inRange(image,
34
+ (color_ranges['b'][0], color_ranges['g'][0], color_ranges['r'][0]),
35
+ (color_ranges['b'][1], color_ranges['g'][1], color_ranges['r'][1]))
36
+
37
+ # Calculate the percentage of pixels within the color range
38
+ target_pixels = cv2.countNonZero(mask)
39
+ total_pixels = image.size / 3 # Divide by 3 for an RGB image
40
+ percentage = target_pixels / total_pixels
41
+ return percentage
File without changes