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.
@@ -92,19 +92,19 @@ def patch_cpp():
92
92
  if WIN32_AVAILABLE:
93
93
  patch_cpp()
94
94
 
95
- import ctypes
96
- import os
97
- import math
98
- import sys
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
- # --- Windows Imports & Structures ---
105
- if WIN32_AVAILABLE:
106
- windll = ctypes.windll
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
- # Windows Constants
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
- if WIN32_AVAILABLE:
163
- hdc = windll.user32.GetDC(0)
164
- width = windll.gdi32.GetDeviceCaps(hdc, 118) # HORZRES
165
- height = windll.gdi32.GetDeviceCaps(hdc, 117) # VERTRES
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
- return 1920, 1080
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
- if WIN32_AVAILABLE:
191
- if not relative:
192
- # Convert coordinates to normalized coordinates (0-65535)
193
- normalized_x = int(x * (65535 / SCREEN_WIDTH))
194
- normalized_y = int(y * (65535 / SCREEN_HEIGHT))
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
- calibrate()
198
- if calibrated:
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
- elif IS_LINUX and x11:
222
- # XWarpPointer(display, src_w, dest_w, src_x, src_y, src_width, src_height, dest_x, dest_y)
223
- if relative:
224
- # Move relative to current position (None as dest_window usually means relative move,
225
- # but standard practice is None src, None dest = move relative to current position)
226
- x11.XWarpPointer(display, None, None, 0, 0, 0, 0, int(x), int(y))
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
- # Absolute move relative to Root Window
229
- x11.XWarpPointer(display, None, root_window, 0, 0, 0, 0, int(x), int(y))
188
+ normalized_x = x
189
+ normalized_y = y
230
190
 
231
- x11.XFlush(display)
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
- def calibrate():
262
- global calibration_multiplier_x, calibration_multiplier_y, calibrated, calipass
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
- # Avoid division by zero
297
- if moved_pos[0] != 0:
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
- if WIN32_AVAILABLE:
312
- flags = {
313
- 1: MOUSEEVENTF_LEFTDOWN,
314
- 2: MOUSEEVENTF_RIGHTDOWN,
315
- 3: MOUSEEVENTF_MIDDLEDOWN
316
- }
317
- input_struct = INPUT(
318
- type=0,
319
- union=INPUT_UNION(
320
- mi=MOUSEINPUT(dx=0, dy=0, mouseData=0, dwFlags=flags[button], time=0, dwExtraInfo=None)
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
- windll.user32.SendInput(1, ctypes.byref(input_struct), sizeof(INPUT))
294
+ )
324
295
 
325
- elif IS_LINUX and xtst:
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
- if WIN32_AVAILABLE:
341
- flags = {
342
- 1: MOUSEEVENTF_LEFTUP,
343
- 2: MOUSEEVENTF_RIGHTUP,
344
- 3: MOUSEEVENTF_MIDDLEUP
345
- }
346
- input_struct = INPUT(
347
- type=0,
348
- union=INPUT_UNION(
349
- mi=MOUSEINPUT(dx=0, dy=0, mouseData=0, dwFlags=flags[button], time=0, dwExtraInfo=None)
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
- windll.user32.SendInput(1, ctypes.byref(input_struct), sizeof(INPUT))
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
- # XTestFakeButtonEvent(display, button, is_press, delay)
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zhmiscellany
3
- Version: 6.0.9
3
+ Version: 6.1.0
4
4
  Summary: A collection of useful/interesting python libraries made by zh.
5
5
  Home-page: https://discord.gg/ThBBAuueVJ
6
6
  Author: zh
@@ -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=zD7LjbRCUC3wMuCS90jum8m_mNw1ZmW7ejPl7IZ9Nbg,11486
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.9.dist-info/METADATA,sha256=6XEuRXVoVBvCorsRSFU8Rm2ZVr81aYO8jVn8ZIAnwGY,43560
25
- zhmiscellany-6.0.9.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
26
- zhmiscellany-6.0.9.dist-info/top_level.txt,sha256=ioDtsrevCI52rTxZntMPljRIBsZs73tD0hI00HektiE,13
27
- zhmiscellany-6.0.9.dist-info/RECORD,,
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,,