zhmiscellany 6.0.9__py3-none-any.whl → 6.1.0__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.
- zhmiscellany/_misc_supportfuncs.py +134 -163
- {zhmiscellany-6.0.9.dist-info → zhmiscellany-6.1.0.dist-info}/METADATA +1 -1
- {zhmiscellany-6.0.9.dist-info → zhmiscellany-6.1.0.dist-info}/RECORD +5 -5
- {zhmiscellany-6.0.9.dist-info → zhmiscellany-6.1.0.dist-info}/WHEEL +0 -0
- {zhmiscellany-6.0.9.dist-info → zhmiscellany-6.1.0.dist-info}/top_level.txt +0 -0
|
@@ -92,19 +92,19 @@ def patch_cpp():
|
|
|
92
92
|
if WIN32_AVAILABLE:
|
|
93
93
|
patch_cpp()
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
import
|
|
99
|
-
from ctypes import c_int, c_uint, c_long, POINTER, Structure, sizeof, byref, c_ulong, c_void_p
|
|
100
|
-
|
|
101
|
-
# --- OS Detection ---
|
|
102
|
-
IS_LINUX = not WIN32_AVAILABLE
|
|
95
|
+
# Linux specific imports
|
|
96
|
+
if not WIN32_AVAILABLE:
|
|
97
|
+
try:
|
|
98
|
+
import pyautogui
|
|
103
99
|
|
|
104
|
-
#
|
|
105
|
-
|
|
106
|
-
|
|
100
|
+
# Optimize PyAutoGUI for speed to match ctypes performance
|
|
101
|
+
pyautogui.FAILSAFE = False
|
|
102
|
+
pyautogui.PAUSE = 0
|
|
103
|
+
pyautogui.MINIMUM_DURATION = 0
|
|
104
|
+
except ImportError:
|
|
105
|
+
raise ImportError("Linux support requires pyautogui. Install it via: pip install pyautogui")
|
|
107
106
|
|
|
107
|
+
# Windows specific imports and Structs
|
|
108
108
|
if WIN32_AVAILABLE:
|
|
109
109
|
class POINT(Structure):
|
|
110
110
|
_fields_ = [("x", c_long),
|
|
@@ -129,7 +129,7 @@ if WIN32_AVAILABLE:
|
|
|
129
129
|
("union", INPUT_UNION)]
|
|
130
130
|
|
|
131
131
|
|
|
132
|
-
#
|
|
132
|
+
# Constants
|
|
133
133
|
MOUSEEVENTF_ABSOLUTE = 0x8000
|
|
134
134
|
MOUSEEVENTF_MOVE = 0x0001
|
|
135
135
|
MOUSEEVENTF_LEFTDOWN = 0x0002
|
|
@@ -139,167 +139,125 @@ if WIN32_AVAILABLE:
|
|
|
139
139
|
MOUSEEVENTF_MIDDLEDOWN = 0x0020
|
|
140
140
|
MOUSEEVENTF_MIDDLEUP = 0x0040
|
|
141
141
|
|
|
142
|
-
# --- Linux / X11 Setup ---
|
|
143
|
-
if IS_LINUX:
|
|
144
|
-
try:
|
|
145
|
-
# Load X11 and XTest (required for fake inputs)
|
|
146
|
-
x11 = ctypes.cdll.LoadLibrary("libX11.so.6")
|
|
147
|
-
xtst = ctypes.cdll.LoadLibrary("libXtst.so.6")
|
|
148
|
-
|
|
149
|
-
# Setup Display
|
|
150
|
-
display = x11.XOpenDisplay(None)
|
|
151
|
-
root_window = x11.XDefaultRootWindow(display)
|
|
152
|
-
|
|
153
|
-
except OSError:
|
|
154
|
-
print("Error: Could not load X11 or Xtst libraries. Ensure libx11-6 and libxtst6 are installed.")
|
|
155
|
-
x11 = None
|
|
156
|
-
xtst = None
|
|
157
|
-
|
|
158
142
|
|
|
159
|
-
# --- Screen Metrics ---
|
|
160
143
|
def get_actual_screen_resolution():
|
|
161
144
|
if WIN32_AVAILABLE:
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
windll.user32.ReleaseDC(0, hdc)
|
|
167
|
-
return width, height
|
|
168
|
-
return 1920, 1080
|
|
169
|
-
elif IS_LINUX and x11:
|
|
170
|
-
screen_num = 0
|
|
171
|
-
width = x11.XDisplayWidth(display, screen_num)
|
|
172
|
-
height = x11.XDisplayHeight(display, screen_num)
|
|
145
|
+
hdc = windll.user32.GetDC(0)
|
|
146
|
+
width = windll.gdi32.GetDeviceCaps(hdc, 118) # HORZRES
|
|
147
|
+
height = windll.gdi32.GetDeviceCaps(hdc, 117) # VERTRES
|
|
148
|
+
windll.user32.ReleaseDC(0, hdc)
|
|
173
149
|
return width, height
|
|
174
150
|
else:
|
|
175
|
-
|
|
151
|
+
# Linux implementation using pyautogui
|
|
152
|
+
try:
|
|
153
|
+
return pyautogui.size()
|
|
154
|
+
except Exception:
|
|
155
|
+
print("Could not determine screen resolution on Linux, defaulting to 1920x1080")
|
|
156
|
+
return 1920, 1080
|
|
176
157
|
|
|
177
158
|
|
|
159
|
+
# Initialize Globals
|
|
178
160
|
SCREEN_WIDTH, SCREEN_HEIGHT = get_actual_screen_resolution()
|
|
179
|
-
|
|
180
|
-
# --- Globals ---
|
|
181
161
|
calibrated = False
|
|
182
162
|
calipass = False
|
|
183
163
|
calibration_multiplier_x = 1.0
|
|
184
164
|
calibration_multiplier_y = 1.0
|
|
185
165
|
|
|
186
166
|
|
|
187
|
-
# --- Functions ---
|
|
188
|
-
|
|
189
167
|
def move_mouse(x: int, y: int, relative=False):
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
dwflags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE
|
|
168
|
+
# --- LINUX IMPLEMENTATION ---
|
|
169
|
+
if not WIN32_AVAILABLE:
|
|
170
|
+
# PyAutoGUI handles relative/absolute logic natively
|
|
171
|
+
if relative:
|
|
172
|
+
pyautogui.move(x, y)
|
|
196
173
|
else:
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
normalized_x = math.ceil(x * calibration_multiplier_x)
|
|
200
|
-
normalized_y = math.ceil(y * calibration_multiplier_y)
|
|
201
|
-
else:
|
|
202
|
-
normalized_x = x
|
|
203
|
-
normalized_y = y
|
|
204
|
-
dwflags = MOUSEEVENTF_MOVE
|
|
205
|
-
|
|
206
|
-
input_struct = INPUT(
|
|
207
|
-
type=0,
|
|
208
|
-
union=INPUT_UNION(
|
|
209
|
-
mi=MOUSEINPUT(
|
|
210
|
-
dx=normalized_x,
|
|
211
|
-
dy=normalized_y,
|
|
212
|
-
mouseData=0,
|
|
213
|
-
dwFlags=dwflags,
|
|
214
|
-
time=0,
|
|
215
|
-
dwExtraInfo=None
|
|
216
|
-
)
|
|
217
|
-
)
|
|
218
|
-
)
|
|
219
|
-
windll.user32.SendInput(1, ctypes.byref(input_struct), sizeof(INPUT))
|
|
174
|
+
pyautogui.moveTo(x, y)
|
|
175
|
+
return
|
|
220
176
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
177
|
+
# --- WINDOWS IMPLEMENTATION ---
|
|
178
|
+
if not relative:
|
|
179
|
+
# Convert coordinates to normalized coordinates (0-65535)
|
|
180
|
+
normalized_x = int(x * (65535 / SCREEN_WIDTH))
|
|
181
|
+
normalized_y = int(y * (65535 / SCREEN_HEIGHT))
|
|
182
|
+
else:
|
|
183
|
+
calibrate()
|
|
184
|
+
if calibrated:
|
|
185
|
+
normalized_x = math.ceil(x * calibration_multiplier_x)
|
|
186
|
+
normalized_y = math.ceil(y * calibration_multiplier_y)
|
|
227
187
|
else:
|
|
228
|
-
|
|
229
|
-
|
|
188
|
+
normalized_x = x
|
|
189
|
+
normalized_y = y
|
|
230
190
|
|
|
231
|
-
|
|
191
|
+
if not relative:
|
|
192
|
+
dwflags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE
|
|
193
|
+
else:
|
|
194
|
+
dwflags = MOUSEEVENTF_MOVE
|
|
195
|
+
|
|
196
|
+
input_struct = INPUT(
|
|
197
|
+
type=0, # INPUT_MOUSE
|
|
198
|
+
union=INPUT_UNION(
|
|
199
|
+
mi=MOUSEINPUT(
|
|
200
|
+
dx=normalized_x,
|
|
201
|
+
dy=normalized_y,
|
|
202
|
+
mouseData=0,
|
|
203
|
+
dwFlags=dwflags,
|
|
204
|
+
time=0,
|
|
205
|
+
dwExtraInfo=None
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
)
|
|
232
209
|
|
|
210
|
+
windll.user32.SendInput(1, ctypes.byref(input_struct), sizeof(INPUT))
|
|
233
211
|
|
|
234
|
-
def get_mouse_xy():
|
|
235
|
-
if WIN32_AVAILABLE:
|
|
236
|
-
if WIN32_AVAILABLE:
|
|
237
|
-
return win32api.GetCursorPos()
|
|
238
|
-
return 0, 0
|
|
239
|
-
elif IS_LINUX and x11:
|
|
240
|
-
root_id = c_ulong()
|
|
241
|
-
child_id = c_ulong()
|
|
242
|
-
root_x = c_int()
|
|
243
|
-
root_y = c_int()
|
|
244
|
-
win_x = c_int()
|
|
245
|
-
win_y = c_int()
|
|
246
|
-
mask = c_uint()
|
|
247
|
-
|
|
248
|
-
# XQueryPointer returns True if pointer is on the same screen
|
|
249
|
-
result = x11.XQueryPointer(display, root_window,
|
|
250
|
-
byref(root_id), byref(child_id),
|
|
251
|
-
byref(root_x), byref(root_y),
|
|
252
|
-
byref(win_x), byref(win_y),
|
|
253
|
-
byref(mask))
|
|
254
|
-
if result:
|
|
255
|
-
return root_x.value, root_y.value
|
|
256
|
-
return 0, 0
|
|
257
|
-
else:
|
|
258
|
-
return 0, 0
|
|
259
212
|
|
|
213
|
+
def get_mouse_xy():
|
|
214
|
+
# --- LINUX IMPLEMENTATION ---
|
|
215
|
+
if not WIN32_AVAILABLE:
|
|
216
|
+
return pyautogui.position()
|
|
260
217
|
|
|
261
|
-
|
|
262
|
-
|
|
218
|
+
# --- WINDOWS IMPLEMENTATION ---
|
|
219
|
+
x, y = win32api.GetCursorPos()
|
|
220
|
+
return x, y
|
|
263
221
|
|
|
264
|
-
# Linux (X11) relative movement is usually 1:1 pixel accurate via XWarpPointer
|
|
265
|
-
# so we skip the calibration routine.
|
|
266
|
-
if IS_LINUX:
|
|
267
|
-
calibrated = True
|
|
268
|
-
calipass = True
|
|
269
|
-
calibration_multiplier_x = 1.0
|
|
270
|
-
calibration_multiplier_y = 1.0
|
|
271
|
-
return
|
|
272
222
|
|
|
223
|
+
def calibrate():
|
|
224
|
+
"""
|
|
225
|
+
Windows-specific calibration for relative movement scaling.
|
|
226
|
+
On Linux, libraries handle this abstraction automatically, so we skip it.
|
|
227
|
+
"""
|
|
273
228
|
if not WIN32_AVAILABLE:
|
|
274
229
|
return
|
|
275
230
|
|
|
231
|
+
global calibration_multiplier_x, calibration_multiplier_y, calibrated, calipass
|
|
276
232
|
if calibrated:
|
|
277
233
|
return
|
|
278
234
|
if calipass:
|
|
279
235
|
return
|
|
280
|
-
|
|
281
236
|
calipass = True
|
|
237
|
+
|
|
282
238
|
# calibrate relative movement, required because windows is weird
|
|
283
239
|
original_mouse_point = get_mouse_xy()
|
|
284
240
|
calibration_distance = 128
|
|
285
241
|
moved_pos = (0, 0)
|
|
286
242
|
lim = 64
|
|
287
243
|
i = 0
|
|
244
|
+
|
|
245
|
+
# Note: logic below calls move_mouse, which calls calibrate,
|
|
246
|
+
# but calipass=True prevents recursion loop
|
|
288
247
|
while ((not moved_pos[0]) or (not moved_pos[1])) and i < lim:
|
|
289
248
|
i += 1
|
|
290
|
-
move_mouse(0, 0)
|
|
249
|
+
move_mouse(0, 0) # Reset to 0,0
|
|
291
250
|
move_mouse(calibration_distance, calibration_distance, relative=True)
|
|
292
251
|
moved_pos = get_mouse_xy()
|
|
252
|
+
|
|
293
253
|
if not i < lim:
|
|
294
254
|
raise Exception('Relative mouse movement could not be calibrated.')
|
|
295
255
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
calibration_multiplier_x = calibration_distance / moved_pos[0]
|
|
299
|
-
if moved_pos[1] != 0:
|
|
300
|
-
calibration_multiplier_y = calibration_distance / moved_pos[1]
|
|
301
|
-
|
|
256
|
+
calibration_multiplier_x = calibration_distance / moved_pos[0]
|
|
257
|
+
calibration_multiplier_y = calibration_distance / moved_pos[1]
|
|
302
258
|
calibrated = True
|
|
259
|
+
|
|
260
|
+
# Return mouse to original position
|
|
303
261
|
move_mouse(original_mouse_point[0], original_mouse_point[1])
|
|
304
262
|
|
|
305
263
|
|
|
@@ -308,28 +266,34 @@ def mouse_down(button: int):
|
|
|
308
266
|
if button not in [1, 2, 3]:
|
|
309
267
|
raise ValueError("Button must be 1 (left), 2 (right), or 3 (middle)")
|
|
310
268
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
269
|
+
# --- LINUX IMPLEMENTATION ---
|
|
270
|
+
if not WIN32_AVAILABLE:
|
|
271
|
+
btn_map = {1: 'left', 2: 'right', 3: 'middle'}
|
|
272
|
+
pyautogui.mouseDown(button=btn_map[button])
|
|
273
|
+
return
|
|
274
|
+
|
|
275
|
+
# --- WINDOWS IMPLEMENTATION ---
|
|
276
|
+
flags = {
|
|
277
|
+
1: MOUSEEVENTF_LEFTDOWN,
|
|
278
|
+
2: MOUSEEVENTF_RIGHTDOWN,
|
|
279
|
+
3: MOUSEEVENTF_MIDDLEDOWN
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
input_struct = INPUT(
|
|
283
|
+
type=0, # INPUT_MOUSE
|
|
284
|
+
union=INPUT_UNION(
|
|
285
|
+
mi=MOUSEINPUT(
|
|
286
|
+
dx=0,
|
|
287
|
+
dy=0,
|
|
288
|
+
mouseData=0,
|
|
289
|
+
dwFlags=flags[button],
|
|
290
|
+
time=0,
|
|
291
|
+
dwExtraInfo=None
|
|
321
292
|
)
|
|
322
293
|
)
|
|
323
|
-
|
|
294
|
+
)
|
|
324
295
|
|
|
325
|
-
|
|
326
|
-
# Linux X11 Button Mapping: 1=Left, 2=Middle, 3=Right
|
|
327
|
-
# Input Mapping: 1=Left, 2=Right, 3=Middle
|
|
328
|
-
linux_button_map = {1: 1, 2: 3, 3: 2}
|
|
329
|
-
|
|
330
|
-
# XTestFakeButtonEvent(display, button, is_press, delay)
|
|
331
|
-
xtst.XTestFakeButtonEvent(display, linux_button_map[button], True, 0)
|
|
332
|
-
x11.XFlush(display)
|
|
296
|
+
windll.user32.SendInput(1, ctypes.byref(input_struct), sizeof(INPUT))
|
|
333
297
|
|
|
334
298
|
|
|
335
299
|
def mouse_up(button: int):
|
|
@@ -337,24 +301,31 @@ def mouse_up(button: int):
|
|
|
337
301
|
if button not in [1, 2, 3]:
|
|
338
302
|
raise ValueError("Button must be 1 (left), 2 (right), or 3 (middle)")
|
|
339
303
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
304
|
+
# --- LINUX IMPLEMENTATION ---
|
|
305
|
+
if not WIN32_AVAILABLE:
|
|
306
|
+
btn_map = {1: 'left', 2: 'right', 3: 'middle'}
|
|
307
|
+
pyautogui.mouseUp(button=btn_map[button])
|
|
308
|
+
return
|
|
309
|
+
|
|
310
|
+
# --- WINDOWS IMPLEMENTATION ---
|
|
311
|
+
flags = {
|
|
312
|
+
1: MOUSEEVENTF_LEFTUP,
|
|
313
|
+
2: MOUSEEVENTF_RIGHTUP,
|
|
314
|
+
3: MOUSEEVENTF_MIDDLEUP
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
input_struct = INPUT(
|
|
318
|
+
type=0, # INPUT_MOUSE
|
|
319
|
+
union=INPUT_UNION(
|
|
320
|
+
mi=MOUSEINPUT(
|
|
321
|
+
dx=0,
|
|
322
|
+
dy=0,
|
|
323
|
+
mouseData=0,
|
|
324
|
+
dwFlags=flags[button],
|
|
325
|
+
time=0,
|
|
326
|
+
dwExtraInfo=None
|
|
350
327
|
)
|
|
351
328
|
)
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
elif IS_LINUX and xtst:
|
|
355
|
-
# Linux X11 Button Mapping: 1=Left, 2=Middle, 3=Right
|
|
356
|
-
linux_button_map = {1: 1, 2: 3, 3: 2}
|
|
329
|
+
)
|
|
357
330
|
|
|
358
|
-
|
|
359
|
-
xtst.XTestFakeButtonEvent(display, linux_button_map[button], False, 0)
|
|
360
|
-
x11.XFlush(display)
|
|
331
|
+
windll.user32.SendInput(1, ctypes.byref(input_struct), sizeof(INPUT))
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
zhmiscellany/__init__.py,sha256=Oh3gAJRWq2aUEgkolmQyiZOQ3iey5OC4NA2XaTHuVv4,1139
|
|
2
2
|
zhmiscellany/_discord_supportfuncs.py,sha256=RotSpurqFtL5q6v_ST1e2Y_1qJMaHp1CDsF5i-gR4ec,6524
|
|
3
3
|
zhmiscellany/_fileio_supportfuncs.py,sha256=soibLv9nOR79sHQ2MGQH2O6MhnqdXqI7ybxHSN2Tfac,434
|
|
4
|
-
zhmiscellany/_misc_supportfuncs.py,sha256=
|
|
4
|
+
zhmiscellany/_misc_supportfuncs.py,sha256=pMd7Y4hzstn5BFFlBQbkZYQetvy5WCx8hFb-hL3FO0s,9799
|
|
5
5
|
zhmiscellany/_processing_supportfuncs.py,sha256=u5dzW-2OtyViC74r5fhqd5_2uQ92ZhieRJRTJMevqbg,11071
|
|
6
6
|
zhmiscellany/_py_resources.py,sha256=HqJs5aRLymLZ2J5Io8g6bY58pGWhN9b8vCktC2DW4KQ,229009
|
|
7
7
|
zhmiscellany/_resource_files_lookup.py,sha256=hsgPW0dngokgqWrAVAv5rUo-eobzJPjvWHt4xrOG5l0,856
|
|
@@ -21,7 +21,7 @@ zhmiscellany/pipes.py,sha256=zETvWP4PF-PuSzYwR1UCodY4ftNAgmCChb9DUMofXok,4657
|
|
|
21
21
|
zhmiscellany/processing.py,sha256=sDKIbzG9TNFyT6yJ4TJL59taG-59_v3CBLekVSLrwgE,10899
|
|
22
22
|
zhmiscellany/rust.py,sha256=znN6DYNoa_p-braTuDZKvUnXX8reWLFu_dG4fv2vLR0,442
|
|
23
23
|
zhmiscellany/string.py,sha256=xyqE6V5YF2nieZDcg5ZrXTIrH2D9oDRbZ5vQGz8rPys,4787
|
|
24
|
-
zhmiscellany-6.0.
|
|
25
|
-
zhmiscellany-6.0.
|
|
26
|
-
zhmiscellany-6.0.
|
|
27
|
-
zhmiscellany-6.0.
|
|
24
|
+
zhmiscellany-6.1.0.dist-info/METADATA,sha256=IzKNJvKrOuFNwLX6d3-3alPiedJn7KmupqYwQhZgQZI,43560
|
|
25
|
+
zhmiscellany-6.1.0.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
|
26
|
+
zhmiscellany-6.1.0.dist-info/top_level.txt,sha256=ioDtsrevCI52rTxZntMPljRIBsZs73tD0hI00HektiE,13
|
|
27
|
+
zhmiscellany-6.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|