cautogui 1.0.0__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.
cautogui-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 danckard
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,4 @@
1
+ include src/*.cpp
2
+ include cautogui/*.py
3
+ include LICENSE
4
+ include README.md
@@ -0,0 +1,8 @@
1
+ Metadata-Version: 2.4
2
+ Name: cautogui
3
+ Version: 1.0.0
4
+ Summary: Native high-performance UI automation for Windows
5
+ Requires-Python: >=3.7
6
+ License-File: LICENSE
7
+ Requires-Dist: Pillow>=9.0.0
8
+ Dynamic: license-file
@@ -0,0 +1,40 @@
1
+ # CAutoGUI 🚀
2
+ **A Native High-Performance Multi-Monitor UI Automation Library.**
3
+
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
+ [![Platform: Windows](https://img.shields.io/badge/platform-windows-blue)](https://www.microsoft.com/windows)
6
+ [![Language: C++](https://img.shields.io/badge/language-C++-red)](src/core.cpp)
7
+ [![Donate with USDT](https://img.shields.io/badge/Donate-USDT-green.svg?style=flat-square&logo=tether)](#-support-this-project)
8
+
9
+ CAutoGUI is a UI automation engine built for speed. Unlike other libraries that rely purely on Python, CAutoGUI uses a **custom-built C++ core** to interact directly with the Windows API and GDI, bypassing high-latency bottlenecks.
10
+
11
+ ## 🌟 Key Features
12
+ - **Ultra-Fast Vision:** Image searching takes **0.09s** on average, up to 20x faster than traditional libraries.
13
+ - **DPI-Aware Captures:** Pixel-perfect accuracy even on screens with Windows scaling enabled.
14
+ - **Native Multi-Monitor Support:** Seamlessly control multiple displays using a unified coordinate system.
15
+ - **Advanced Tweening:** Professional easing functions (Bounce, Elastic, Exponential) for human-like mouse movement.
16
+ - **Compiled Efficiency:** Zero heavy dependencies like OpenCV; runs light and fast.
17
+
18
+ ## 📊 Performance Benchmark (Full Screen Scan)
19
+ | Library | Search Time (Avg) | Technology |
20
+ | :--- | :--- | :--- |
21
+ | PyAutoGUI | ~1.50s - 2.10s | Pure Python / MSS |
22
+ | **CAutoGUI** | **0.09s** ⚡ | **C++ Native / GDI** |
23
+
24
+
25
+
26
+ ## 🛠️ Installation
27
+
28
+ ### Using Pre-compiled Wheels (Recommended)
29
+ import cautogui as pyautogui
30
+ # Use it exactly like PyAutoGUI
31
+ ---
32
+
33
+ ## ☕ Support this project
34
+ If CAutoGUI saved you time and improved your automation speed, consider supporting its development!
35
+
36
+ **USDT (TRC20):** `TZ9idiUf39JmSYCapp6wZEDdnpm5yJz5HX`
37
+
38
+ **USDT (ERC20):** `0x97246ff700f1504755eb329b619ebb6a6889c50d`
39
+
40
+ ---
@@ -0,0 +1,53 @@
1
+ """
2
+ CAutoGUI - Motor de automatización de alto rendimiento
3
+ """
4
+
5
+ from .cautogui import (
6
+ cautogui,
7
+ Tweens,
8
+ # Exponemos los tweens más comunes directamente para facilidad de uso
9
+ linear,
10
+ easeInQuad,
11
+ easeOutQuad,
12
+ easeInOutQuad,
13
+ easeInCubic,
14
+ easeOutCubic,
15
+ easeInOutCubic,
16
+ easeInSine,
17
+ easeOutSine,
18
+ easeInOutSine,
19
+ easeInExpo,
20
+ easeOutExpo,
21
+ easeInElastic,
22
+ easeOutElastic,
23
+ easeInBack,
24
+ easeOutBack,
25
+ easeInBounce,
26
+ easeOutBounce
27
+ )
28
+
29
+ # Definimos qué se exporta cuando alguien hace 'from cautogui import *'
30
+ __all__ = [
31
+ 'cautogui',
32
+ 'Tweens',
33
+ 'linear',
34
+ 'easeInQuad',
35
+ 'easeOutQuad',
36
+ 'easeInOutQuad',
37
+ 'easeInCubic',
38
+ 'easeOutCubic',
39
+ 'easeInOutCubic',
40
+ 'easeInSine',
41
+ 'easeOutSine',
42
+ 'easeInOutSine',
43
+ 'easeInExpo',
44
+ 'easeOutExpo',
45
+ 'easeInElastic',
46
+ 'easeOutElastic',
47
+ 'easeInBack',
48
+ 'easeOutBack',
49
+ 'easeInBounce',
50
+ 'easeOutBounce'
51
+ ]
52
+
53
+ __version__ = '1.0.0'
@@ -0,0 +1,358 @@
1
+ import cautogui_core
2
+ from PIL import Image
3
+ import ctypes
4
+ import time
5
+ import math
6
+
7
+ class Tweens:
8
+ """Mathematical easing functions for smooth mouse movement."""
9
+ @staticmethod
10
+ def linear(n):
11
+ return n
12
+
13
+ @staticmethod
14
+ def easeInQuad(n): return n**2
15
+
16
+ @staticmethod
17
+ def easeOutQuad(n): return -n * (n - 2)
18
+
19
+ @staticmethod
20
+ def easeInOutQuad(n):
21
+ n *= 2
22
+ if n < 1: return 0.5 * n**2
23
+ return -0.5 * ((n - 1) * (n - 3) - 1)
24
+
25
+ @staticmethod
26
+ def easeInCubic(n): return n**3
27
+
28
+ @staticmethod
29
+ def easeOutCubic(n): return (n - 1)**3 + 1
30
+
31
+ @staticmethod
32
+ def easeInOutCubic(n):
33
+ n *= 2
34
+ if n < 1: return 0.5 * n**3
35
+ return 0.5 * ((n - 2)**3 + 2)
36
+
37
+ @staticmethod
38
+ def easeInSine(n): return -1 * math.cos(n * (math.pi / 2)) + 1
39
+
40
+ @staticmethod
41
+ def easeOutSine(n): return math.sin(n * (math.pi / 2))
42
+
43
+ @staticmethod
44
+ def easeInOutSine(n): return -0.5 * (math.cos(math.pi * n) - 1)
45
+
46
+ @staticmethod
47
+ def easeInExpo(n): return 0 if n == 0 else 2**(10 * (n - 1))
48
+
49
+ @staticmethod
50
+ def easeOutExpo(n): return 1 if n == 1 else 1 - 2**(-10 * n)
51
+
52
+ @staticmethod
53
+ def easeInElastic(n, period=0.3):
54
+ if n == 0 or n == 1: return n
55
+ p = period
56
+ s = p / 4
57
+ return -(2**(10 * (n - 1)) * math.sin((n - 1 - s) * (2 * math.pi) / p))
58
+
59
+ @staticmethod
60
+ def easeOutElastic(n, period=0.3):
61
+ if n == 0 or n == 1: return n
62
+ p = period
63
+ s = p / 4
64
+ return (2**(-10 * n) * math.sin((n - s) * (2 * math.pi) / p) + 1)
65
+
66
+ @staticmethod
67
+ def easeInBack(n, s=1.70158): return n**2 * ((s + 1) * n - s)
68
+
69
+ @staticmethod
70
+ def easeOutBack(n, s=1.70158): return (n - 1)**2 * ((s + 1) * (n - 1) + s) + 1
71
+ @staticmethod
72
+ def easeOutBounce(n):
73
+ if n < 1 / 2.75:
74
+ return 7.5625 * n**2
75
+ elif n < 2 / 2.75:
76
+ n -= 1.5 / 2.75
77
+ return 7.5625 * n**2 + 0.75
78
+ elif n < 2.5 / 2.75:
79
+ n -= 2.25 / 2.75
80
+ return 7.5625 * n**2 + 0.9375
81
+ else:
82
+ n -= 2.625 / 2.75
83
+ return 7.5625 * n**2 + 0.984375
84
+
85
+ @staticmethod
86
+ def easeInBounce(n): return 1 - Tweens.easeOutBounce(1 - n)
87
+
88
+ @staticmethod
89
+ def easeInOutBounce(n):
90
+ if n < 0.5: return Tweens.easeInBounce(n * 2) * 0.5
91
+ return Tweens.easeOutBounce(n * 2 - 1) * 0.5 + 0.5
92
+
93
+ class CAutoGUI:
94
+ FAILSAFE = True
95
+ FAILSAFE_POINTS = [(0, 0)]
96
+ _TWEEN_MAP = {name: func for name, func in Tweens.__dict__.items()
97
+ if isinstance(func, staticmethod)}
98
+ KEY_MAP = {
99
+ 'enter': 0x0D,
100
+ 'esc': 0x1B,
101
+ 'tab': 0x09,
102
+ 'space': 0x20,
103
+ 'backspace': 0x08,
104
+ 'shift': 0x10,
105
+ 'ctrl': 0x11,
106
+ 'alt': 0x12,
107
+ 'f5': 0x74,
108
+ }
109
+
110
+
111
+ def position(self):
112
+ """
113
+ return the current xy coordinates of the mouse cursor as a two-integer tuple. multimonitor
114
+ """
115
+ class POINT(ctypes.Structure):
116
+ _fields_ = [("x", ctypes.c_long), ("y", ctypes.c_long)]
117
+
118
+ pt = POINT()
119
+ ctypes.windll.user32.GetCursorPos(ctypes.byref(pt))
120
+ return (pt.x, pt.y)
121
+
122
+
123
+
124
+
125
+ def __init__(self):
126
+ self.user32 = ctypes.windll.user32
127
+
128
+ def _check_failsafe(self):
129
+ if self.FAILSAFE:
130
+ pos = self.position()
131
+ if pos in self.FAILSAFE_POINTS:
132
+ raise Exception("Failsafe activated: Mouse moved to a corner.")
133
+
134
+ def moveTo(self, x, y, duration=0.0 , tween=None):
135
+ """
136
+ Move the mouse cursor to absolute coordinates.
137
+
138
+ Args:
139
+ x (int): Destination X coordinate.
140
+ y (int): Destination Y coordinate.
141
+ duration (float): Time in seconds to perform movement.
142
+ tween (str|callable): Easing function or alias.
143
+ """
144
+ self._check_failsafe()
145
+
146
+ if isinstance(tween, str):
147
+ tween_func = self._TWEEN_MAP.get(tween, Tweens.linear)
148
+ elif callable(tween):
149
+ tween_func = tween
150
+ else:
151
+ tween_func = Tweens.linear
152
+
153
+ start_x, start_y = self.position()
154
+ dx, dy = x - start_x, y - start_y
155
+
156
+ if duration <= 0:
157
+ cautogui_core.move_mouse_abs(int(x), int(y))
158
+ return
159
+
160
+ start_time = time.perf_counter()
161
+ while True:
162
+ elapsed = time.perf_counter() - start_time
163
+ if elapsed >= duration:
164
+ break
165
+
166
+ # Calculate progress (0.0 to 1.0)
167
+ progress = tween(elapsed / duration)
168
+
169
+ curr_x = start_x + int(dx * progress)
170
+ curr_y = start_y + int(dy * progress)
171
+
172
+ cautogui_core.move_mouse_abs(curr_x, curr_y)
173
+ # Control frequency for smoothness
174
+ time.sleep(0.001)
175
+
176
+ # Asegurar posición final
177
+ cautogui_core.move_mouse_abs(int(x), int(y))
178
+ def dragTo(self, x, y, duration=0.5):
179
+ """drag the mouse from its current position to (x, y)."""
180
+ self._check_failsafe()
181
+ # MOUSEEVENTF_LEFTDOWN = 0x0002
182
+ cautogui_core.mouse_event_raw(0x0002, 0, 0)
183
+ self.moveTo(x, y, duration)
184
+ # MOUSEEVENTF_LEFTUP = 0x0004
185
+ cautogui_core.mouse_event_raw(0x0004, 0, 0)
186
+
187
+ VK_SHIFT = 0x10
188
+ KEYEVENTF_KEYUP = 0x0002
189
+
190
+ def press(self, key):
191
+ """press and release a physical key."""
192
+ vk = self._get_vk(key)
193
+ needs_shift = self._needs_shift(key)
194
+
195
+ if needs_shift:
196
+ cautogui_core.key_event(self.VK_SHIFT, 0) # Shift Down
197
+
198
+ cautogui_core.key_event(vk, 0) # Key Down
199
+ cautogui_core.key_event(vk, self.KEYEVENTF_KEYUP) # Key Up
200
+
201
+ if needs_shift:
202
+ cautogui_core.key_event(self.VK_SHIFT, self.KEYEVENTF_KEYUP) # Shift Up
203
+
204
+ def write(self, text, interval=0.01):
205
+ """text typing handling for uppercase and lowercase."""
206
+ for char in text:
207
+ self.press(char)
208
+ if interval > 0:
209
+ time.sleep(interval)
210
+
211
+ def _get_vk(self, char):
212
+ """convert a character to a Windows Virtual Key Code."""
213
+ # Handle special characters
214
+ special = {'enter': 0x0D, 'esc': 0x1B, 'tab': 0x09, ' ': 0x20}
215
+ if char.lower() in special:
216
+ return special[char.lower()]
217
+
218
+ # For letters and numbers
219
+ res = ctypes.windll.user32.VkKeyScanW(ord(char))
220
+ return res & 0xFF
221
+
222
+ def _needs_shift(self, char):
223
+ """detect if the character requires the SHIFT key."""
224
+ if len(char) > 1: return False
225
+ res = ctypes.windll.user32.VkKeyScanW(ord(char))
226
+ return (res >> 8) & 0x01
227
+
228
+ def press(self, key):
229
+ """Simulate pressing a physical key."""
230
+ key = key.lower()
231
+ if key in self.KEY_MAP:
232
+ code = self.KEY_MAP[key]
233
+ elif len(key) == 1:
234
+ code = ord(key.upper())
235
+ else:
236
+ raise ValueError(f"Key not recognized: {key}")
237
+
238
+ cautogui_core.press_key(code)
239
+
240
+ def typewrite(self, text, interval=0.0):
241
+ """write a string of text."""
242
+ for char in text:
243
+ self.press(char)
244
+ if interval > 0:
245
+ time.sleep(interval)
246
+
247
+ def size(self):
248
+ w = self.user32.GetSystemMetrics(78) # SM_CXVIRTUALSCREEN
249
+ h = self.user32.GetSystemMetrics(79) # SM_CYVIRTUALSCREEN
250
+ return (w, h)
251
+ def locateAllOnScreen(self, image_path, confidence=0.9):
252
+ img = Image.open(image_path).convert('RGBA')
253
+ # Swap to BGRA for the C++ extension
254
+ r, g, b, a = img.split()
255
+ img_bgra = Image.merge("RGBA", (b, g, r, a))
256
+
257
+ tw, th = img_bgra.size
258
+ sw, sh = self.size()
259
+ screen_bytes = cautogui_core.capture_all()
260
+
261
+ # Return a list of tuples [(x,y), (x,y), ...]
262
+ return cautogui_core.locate_all(screen_bytes, sw, sh, img_bgra.tobytes(), tw, th, confidence)
263
+ def locateOnScreen(self, image_path, confidence=0.9, grayscale=False, region=None):
264
+ """
265
+ image_path: path to target
266
+ confidence: 0.0 a 1.0 (tolerance)
267
+ grayscale: True/False (for compatibility)
268
+ region: (x, y, width, height) - Limit search to an area
269
+ """
270
+ # 1. Load and prepare template
271
+ img = Image.open(image_path).convert('RGBA')
272
+
273
+ # Swap to BGRA for the C++ extension
274
+ r, g, b, a = img.split()
275
+ img_bgra = Image.merge("RGBA", (b, g, r, a))
276
+ tw, th = img_bgra.size
277
+ templ_bytes = img_bgra.tobytes()
278
+
279
+ # 2. Get screen capture
280
+ sw, sh = self.size()
281
+ screen_bytes = cautogui_core.capture_all()
282
+
283
+ # If there is a region, the C++ extension will only search within those limits
284
+ # region_data = (x_start, y_start, x_end, y_end)
285
+ if region:
286
+ # Convert region (x, y, w, h) to limits for the C++ loop
287
+ rx, ry, rw, rh = region
288
+ # Adjust virtual coordinates to local buffer
289
+ v_left = ctypes.windll.user32.GetSystemMetrics(76)
290
+ v_top = ctypes.windll.user32.GetSystemMetrics(77)
291
+
292
+ search_area = (
293
+ max(0, rx - v_left),
294
+ max(0, ry - v_top),
295
+ min(sw, rx - v_left + rw),
296
+ min(sh, ry - v_top + rh)
297
+ )
298
+ else:
299
+ search_area = (0, 0, sw, sh)
300
+
301
+ # 3. Call the C++ extension
302
+ # The extension now receives: buffer, sw, sh, template, tw, th, conf, gray, search_area
303
+ return cautogui_core.find_image(
304
+ screen_bytes, sw, sh,
305
+ templ_bytes, tw, th,
306
+ confidence,
307
+ 1 if grayscale else 0,
308
+ search_area
309
+ )
310
+ def locateCenterOnScreen(self, image_path, confidence=0.9, grayscale=False, region=None):
311
+ """
312
+ Search for the image and return the center coordinates (x, y).
313
+ Useful for passing directly to cautogui.click().
314
+ """
315
+ # 1. Search for the normal position (upper left corner)
316
+ coords = self.locateOnScreen(image_path, confidence, grayscale, region)
317
+
318
+ if coords:
319
+ # 2. Open the image only to know its dimensions
320
+ with Image.open(image_path) as img:
321
+ tw, th = img.size
322
+
323
+ # 3. Calculate the center
324
+ center_x = coords[0] + (tw // 2)
325
+ center_y = coords[1] + (th // 2)
326
+
327
+ return (center_x, center_y)
328
+
329
+ return None
330
+ def displayMousePosition(self):
331
+ """show the current mouse position in real-time (useful for debugging)."""
332
+ print("Press Ctrl+C to exit.")
333
+ try:
334
+ while True:
335
+ x, y = self.position()
336
+ print(f"\rX: {str(x).ljust(5)} Y: {str(y).ljust(5)}", end="")
337
+ time.sleep(0.1)
338
+ except KeyboardInterrupt:
339
+ print("\nDone.")
340
+ def click(self, x=None, y=None, clicks=1, interval=0.1, button='left', duration=0.0, tween=Tweens.linear):
341
+ """move the mouse to (x,y) with smooth movement and click."""
342
+ if x is not None and y is not None:
343
+ self.moveTo(x, y, duration=duration, tween=tween)
344
+
345
+ # Map of buttons for SendInput
346
+ down_flag = 0x0002 if button == 'left' else 0x0008
347
+ up_flag = 0x0004 if button == 'left' else 0x0010
348
+
349
+ for i in range(clicks):
350
+ self._check_failsafe()
351
+ cautogui_core.mouse_event_raw(down_flag, 0, 0)
352
+ time.sleep(0.01) # Small delay for physical movement
353
+ cautogui_core.mouse_event_raw(up_flag, 0, 0)
354
+ if i < clicks - 1:
355
+ time.sleep(interval)
356
+ cautogui = CAutoGUI()
357
+ for name, func in CAutoGUI._TWEEN_MAP.items():
358
+ globals()[name] = func.__func__
@@ -0,0 +1,8 @@
1
+ Metadata-Version: 2.4
2
+ Name: cautogui
3
+ Version: 1.0.0
4
+ Summary: Native high-performance UI automation for Windows
5
+ Requires-Python: >=3.7
6
+ License-File: LICENSE
7
+ Requires-Dist: Pillow>=9.0.0
8
+ Dynamic: license-file
@@ -0,0 +1,13 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ setup.py
6
+ cautogui/__init__.py
7
+ cautogui/cautogui.py
8
+ cautogui.egg-info/PKG-INFO
9
+ cautogui.egg-info/SOURCES.txt
10
+ cautogui.egg-info/dependency_links.txt
11
+ cautogui.egg-info/requires.txt
12
+ cautogui.egg-info/top_level.txt
13
+ src/core.cpp
@@ -0,0 +1 @@
1
+ Pillow>=9.0.0
@@ -0,0 +1,2 @@
1
+ cautogui
2
+ cautogui_core
@@ -0,0 +1,12 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "cautogui"
7
+ version = "1.0.0"
8
+ description = "Native high-performance UI automation for Windows"
9
+ requires-python = ">=3.7"
10
+ dependencies = [
11
+ "Pillow>=9.0.0",
12
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,15 @@
1
+ from setuptools import setup, Extension, find_packages
2
+ import os
3
+
4
+ ext_module = Extension(
5
+ 'cautogui_core',
6
+ sources=[os.path.join('src', 'core.cpp')],
7
+ libraries=['user32', 'gdi32', 'gdiplus'],
8
+ extra_compile_args=['/O2', '/std:c++17']
9
+ )
10
+
11
+ setup(
12
+ ext_modules=[ext_module],
13
+ packages=find_packages(),
14
+ include_package_data=True,
15
+ )
@@ -0,0 +1,253 @@
1
+ #define PY_SSIZE_T_CLEAN
2
+ #include <Python.h>
3
+ #include <windows.h>
4
+ #include <vector>
5
+
6
+ struct MonitorData {
7
+ int x, y, w, h;
8
+ bool is_primary;
9
+ };
10
+
11
+ BOOL CALLBACK EnumMonitorsProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
12
+ auto* monitors = reinterpret_cast<std::vector<MonitorData>*>(dwData);
13
+ MONITORINFO mi;
14
+ mi.cbSize = sizeof(mi);
15
+ if (GetMonitorInfo(hMonitor, &mi)) {
16
+ monitors->push_back({
17
+ (int)mi.rcMonitor.left,
18
+ (int)mi.rcMonitor.top,
19
+ (int)(mi.rcMonitor.right - mi.rcMonitor.left),
20
+ (int)(mi.rcMonitor.bottom - mi.rcMonitor.top),
21
+ (bool)(mi.dwFlags & MONITORINFOF_PRIMARY)
22
+ });
23
+ }
24
+ return TRUE;
25
+ }
26
+
27
+ static PyObject* get_monitors(PyObject* self, PyObject* args) {
28
+ std::vector<MonitorData> monitors;
29
+ EnumDisplayMonitors(NULL, NULL, EnumMonitorsProc, reinterpret_cast<LPARAM>(&monitors));
30
+ PyObject* py_list = PyList_New(monitors.size());
31
+ for (size_t i = 0; i < monitors.size(); ++i) {
32
+ PyObject* dict = Py_BuildValue("{s:i, s:i, s:i, s:i, s:b}",
33
+ "x", monitors[i].x, "y", monitors[i].y,
34
+ "width", monitors[i].w, "height", monitors[i].h,
35
+ "is_primary", monitors[i].is_primary);
36
+ PyList_SetItem(py_list, i, dict);
37
+ }
38
+ return py_list;
39
+ }
40
+ static PyObject* key_event(PyObject* self, PyObject* args) {
41
+ int vk, flags;
42
+ if (!PyArg_ParseTuple(args, "ii", &vk, &flags)) return NULL;
43
+
44
+ INPUT input = {0};
45
+ input.type = INPUT_KEYBOARD;
46
+ input.ki.wVk = (WORD)vk;
47
+ input.ki.dwFlags = flags;
48
+ SendInput(1, &input, sizeof(INPUT));
49
+
50
+ Py_RETURN_NONE;
51
+ }
52
+ static PyObject* capture_all(PyObject* self, PyObject* args) {
53
+ int x = GetSystemMetrics(SM_XVIRTUALSCREEN);
54
+ int y = GetSystemMetrics(SM_YVIRTUALSCREEN);
55
+ int w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
56
+ int h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
57
+
58
+ HDC hScreen = GetDC(NULL);
59
+ HDC hDC = CreateCompatibleDC(hScreen);
60
+ HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, w, h);
61
+ SelectObject(hDC, hBitmap);
62
+
63
+ BitBlt(hDC, 0, 0, w, h, hScreen, x, y, SRCCOPY);
64
+
65
+ BITMAPINFOHEADER bi = { sizeof(BITMAPINFOHEADER), w, -h, 1, 32, BI_RGB };
66
+ std::vector<unsigned char> pixels(w * h * 4);
67
+ GetDIBits(hDC, hBitmap, 0, h, pixels.data(), (BITMAPINFO*)&bi, DIB_RGB_COLORS);
68
+
69
+ DeleteObject(hBitmap);
70
+ DeleteDC(hDC);
71
+ ReleaseDC(NULL, hScreen);
72
+
73
+ return PyBytes_FromStringAndSize((char*)pixels.data(), pixels.size());
74
+ }
75
+ static PyObject* locate_all(PyObject* self, PyObject* args) {
76
+ Py_buffer screen_buf, templ_buf;
77
+ int s_w, s_h, t_w, t_h;
78
+ float confidence;
79
+
80
+ if (!PyArg_ParseTuple(args, "y*iiy*iif", &screen_buf, &s_w, &s_h, &templ_buf, &t_w, &t_h, &confidence))
81
+ return NULL;
82
+
83
+ unsigned char* screen = (unsigned char*)screen_buf.buf;
84
+ unsigned char* templ = (unsigned char*)templ_buf.buf;
85
+ int max_diff = (int)((1.0f - confidence) * 255);
86
+
87
+ PyObject* results_list = PyList_New(0);
88
+ int v_left = GetSystemMetrics(SM_XVIRTUALSCREEN);
89
+ int v_top = GetSystemMetrics(SM_YVIRTUALSCREEN);
90
+
91
+ for (int y = 0; y <= s_h - t_h; ++y) {
92
+ for (int x = 0; x <= s_w - t_w; ++x) {
93
+ bool match = true;
94
+ for (int ty = 0; ty < t_h; ++ty) {
95
+ for (int tx = 0; tx < t_w; ++tx) {
96
+ int s_ptr = ((y + ty) * s_w + (x + tx)) * 4;
97
+ int t_ptr = (ty * t_w + tx) * 4;
98
+
99
+ if (abs(screen[s_ptr] - templ[t_ptr]) > max_diff ||
100
+ abs(screen[s_ptr+1] - templ[t_ptr+1]) > max_diff ||
101
+ abs(screen[s_ptr+2] - templ[t_ptr+2]) > max_diff) {
102
+ match = false;
103
+ break;
104
+ }
105
+ }
106
+ if (!match) break;
107
+ }
108
+
109
+ if (match) {
110
+ PyObject* pos = Py_BuildValue("(ii)", x + v_left, y + v_top);
111
+ PyList_Append(results_list, pos);
112
+ Py_DECREF(pos);
113
+
114
+
115
+ x += (t_w - 1);
116
+ }
117
+ }
118
+ }
119
+
120
+ PyBuffer_Release(&screen_buf);
121
+ PyBuffer_Release(&templ_buf);
122
+ return results_list;
123
+ }
124
+ static PyObject* press_key(PyObject* self, PyObject* args) {
125
+ int key_code;
126
+ if (!PyArg_ParseTuple(args, "i", &key_code)) return NULL;
127
+
128
+ INPUT inputs[2] = {0};
129
+
130
+ // Key Down
131
+ inputs[0].type = INPUT_KEYBOARD;
132
+ inputs[0].ki.wVk = (WORD)key_code;
133
+
134
+ // Key Up
135
+ inputs[1].type = INPUT_KEYBOARD;
136
+ inputs[1].ki.wVk = (WORD)key_code;
137
+ inputs[1].ki.dwFlags = KEYEVENTF_KEYUP;
138
+
139
+ SendInput(2, inputs, sizeof(INPUT));
140
+ Py_RETURN_NONE;
141
+ }
142
+ static PyObject* find_image(PyObject* self, PyObject* args) {
143
+ Py_buffer screen_buf, templ_buf;
144
+ int s_w, s_h, t_w, t_h, grayscale;
145
+ float confidence;
146
+ PyObject* region_obj;
147
+
148
+ if (!PyArg_ParseTuple(args, "y*iiy*iifiO", &screen_buf, &s_w, &s_h, &templ_buf, &t_w, &t_h, &confidence, &grayscale, &region_obj))
149
+ return NULL;
150
+
151
+ // Extract region limits
152
+ int rx_start, ry_start, rx_end, ry_end;
153
+ PyArg_ParseTuple(region_obj, "iiii", &rx_start, &ry_start, &rx_end, &ry_end);
154
+
155
+ unsigned char* screen = (unsigned char*)screen_buf.buf;
156
+ unsigned char* templ = (unsigned char*)templ_buf.buf;
157
+ int max_diff = (int)((1.0f - confidence) * 255);
158
+
159
+ // The loop now only searches within the specified region
160
+ for (int y = ry_start; y <= ry_end - t_h; ++y) {
161
+ for (int x = rx_start; x <= rx_end - t_w; ++x) {
162
+ bool match = true;
163
+ for (int ty = 0; ty < t_h; ++ty) {
164
+ for (int tx = 0; tx < t_w; ++tx) {
165
+ int s_ptr = ((y + ty) * s_w + (x + tx)) * 4;
166
+ int t_ptr = (ty * t_w + tx) * 4;
167
+
168
+ if (grayscale) {
169
+ int s_lum = (screen[s_ptr+2]*2 + screen[s_ptr+1]*5 + screen[s_ptr]) >> 3;
170
+ int t_lum = (templ[t_ptr+2]*2 + templ[t_ptr+1]*5 + templ[t_ptr]) >> 3;
171
+ if (abs(s_lum - t_lum) > max_diff) { match = false; break; }
172
+ } else {
173
+ if (abs(screen[s_ptr] - templ[t_ptr]) > max_diff ||
174
+ abs(screen[s_ptr+1] - templ[t_ptr+1]) > max_diff ||
175
+ abs(screen[s_ptr+2] - templ[t_ptr+2]) > max_diff) {
176
+ match = false; break;
177
+ }
178
+ }
179
+ }
180
+ if (!match) break;
181
+ }
182
+ if (match) {
183
+ int v_left = GetSystemMetrics(SM_XVIRTUALSCREEN);
184
+ int v_top = GetSystemMetrics(SM_YVIRTUALSCREEN);
185
+ PyBuffer_Release(&screen_buf);
186
+ PyBuffer_Release(&templ_buf);
187
+ return Py_BuildValue("(ii)", x + v_left, y + v_top);
188
+ }
189
+ }
190
+ }
191
+ PyBuffer_Release(&screen_buf);
192
+ PyBuffer_Release(&templ_buf);
193
+ Py_RETURN_NONE;
194
+ }
195
+ static PyObject* move_mouse_abs(PyObject* self, PyObject* args) {
196
+ int x, y;
197
+ if (!PyArg_ParseTuple(args, "ii", &x, &y)) return NULL;
198
+
199
+ // Get dimensions of the entire virtual desktop
200
+ int v_left = GetSystemMetrics(SM_XVIRTUALSCREEN);
201
+ int v_top = GetSystemMetrics(SM_YVIRTUALSCREEN);
202
+ int v_width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
203
+ int v_height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
204
+
205
+ INPUT input = {0};
206
+ input.type = INPUT_MOUSE;
207
+
208
+ // Normalization to 0 to 65535 (required by MOUSEEVENTF_ABSOLUTE)
209
+ input.mi.dx = (long)((x - v_left) * (65536.0f / v_width));
210
+ input.mi.dy = (long)((y - v_top) * (65536.0f / v_height));
211
+
212
+ input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK;
213
+
214
+ SendInput(1, &input, sizeof(INPUT));
215
+ Py_RETURN_NONE;
216
+ }
217
+ // For pressing/releasing individual buttons (necessary for drag)
218
+ static PyObject* mouse_event_raw(PyObject* self, PyObject* args) {
219
+ int dwFlags, x, y;
220
+ if (!PyArg_ParseTuple(args, "iii", &dwFlags, &x, &y)) return NULL;
221
+
222
+ INPUT input = {0};
223
+ input.type = INPUT_MOUSE;
224
+ input.mi.dx = x * (65535 / GetSystemMetrics(SM_CXVIRTUALSCREEN));
225
+ input.mi.dy = y * (65535 / GetSystemMetrics(SM_CYVIRTUALSCREEN));
226
+ input.mi.dwFlags = dwFlags | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK;
227
+
228
+ SendInput(1, &input, sizeof(INPUT));
229
+ Py_RETURN_NONE;
230
+ }
231
+ // Actualiza tu tabla de métodos
232
+ static PyMethodDef CAutoGuiMethods[] = {
233
+ {"get_monitors", get_monitors, METH_VARARGS, "get all monitors"},
234
+ {"capture_all", capture_all, METH_VARARGS, "capture all screen"},
235
+ {"find_image", find_image, METH_VARARGS, "find an image in the buffer"},
236
+ {"locate_all", locate_all, METH_VARARGS, "find all instances"},
237
+ {"key_event", (PyCFunction)key_event, METH_VARARGS, "raw key event"},
238
+ {"press_key", (PyCFunction)press_key, METH_VARARGS, "simulate pressing and releasing a physical key"},
239
+ {"move_mouse_abs", (PyCFunction)move_mouse_abs, METH_VARARGS, "move mouse to an absolute position"},
240
+ {NULL, NULL, 0, NULL}
241
+ };
242
+
243
+ static struct PyModuleDef cautogui_core_module = {
244
+ PyModuleDef_HEAD_INIT,
245
+ "cautogui_core",
246
+ "Internal C++ extension for high-performance UI automation.",
247
+ -1,
248
+ CAutoGuiMethods
249
+ };
250
+
251
+ PyMODINIT_FUNC PyInit_cautogui_core(void) {
252
+ return PyModule_Create(&cautogui_core_module);
253
+ }