0

I am trying to code a script that would adjust the view in a game after user clicks the mouse, and stop adjusting if mouse was released. To enable/disable my script I used a keyboard.Listener from pynput. To know when user clicks the mouse I tried using mouse.Listener, but it caused me some problems, so I switched to pyWinhook.Hookmanager and I can hook/unhook it with key bindings.

def left_down(event):
    move(10, -10, 10, 0.15)
    return True

hm = pyWinhook.HookManager() 
hm.SubscribeMouseLeftDown(left_down)

def on_key_press(key):

    if (key==keyboard.Key.f8):
        try:
            global hm
            hm.HookMouse()
        except MyException as e:
            print('Exception: '.format(e.args[0]))

    elif (key==keyboard.Key.f9):
        hm.UnhookMouse()
        
    elif (key==keyboard.Key.f10):
        hm.UnhookMouse() 
        key_listener.stop()                  


with keyboard.Listener(on_press=on_key_press) as key_listener:
    key_listener.join() 
  

Everything worked fine, untill I tried to implement it with the code which simulates mouse movements in the game. To my understanding hook manager creates a deadlock when it calls move function which works using win32api

PUL = ctypes.POINTER(ctypes.c_ulong)


class KeyBdInput(ctypes.Structure):
    _fields_ = [("wVk", ctypes.c_ushort),
                ("wScan", ctypes.c_ushort),
                ("dwFlags", ctypes.c_ulong),
                ("time", ctypes.c_ulong),
                ("dwExtraInfo", PUL)]


class HardwareInput(ctypes.Structure):
    _fields_ = [("uMsg", ctypes.c_ulong),
                ("wParamL", ctypes.c_short),
                ("wParamH", ctypes.c_ushort)]


class MouseInput(ctypes.Structure):
    _fields_ = [("dx", ctypes.c_long),
                ("dy", ctypes.c_long),
                ("mouseData", ctypes.c_ulong),
                ("dwFlags", ctypes.c_ulong),
                ("time", ctypes.c_ulong),
                ("dwExtraInfo", PUL)]

class Input_I(ctypes.Union):
    _fields_ = [("ki", KeyBdInput),
                ("mi", MouseInput),
                ("hi", HardwareInput)]

class Input(ctypes.Structure):
    _fields_ = [("type", ctypes.c_ulong),
                ("ii", Input_I)]


def move(steps, x=None, y=None, duration=0.25, vertical=False):
    x = int(x)
    y = int(y)

    coordinates = _interpolate_mouse_movement(
        start_windows_coordinates=(0, 0),
        end_windows_coordinates=(x, y)
    )

    
    for x, y in coordinates:
        if (vertical):
            x = 0
        extra = ctypes.c_ulong(0)
        ii_ = Input_I()
        ii_.mi = MouseInput(x, y, 0, 0x0001, 0, ctypes.pointer(extra))
        x = Input(ctypes.c_ulong(0), ii_)
        ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))

        time.sleep(duration / len(coordinates))


def _to_windows_coordinates(x=0, y=0):
    display_width = win32api.GetSystemMetrics(0)
    display_height = win32api.GetSystemMetrics(1)

    windows_x = (x * 65535) // display_width
    windows_y = (y * 65535) // display_height

    return windows_x, windows_y
    
def _interpolate_mouse_movement(start_windows_coordinates, end_windows_coordinates, steps=10):
    x_coordinates = [start_windows_coordinates[0], end_windows_coordinates[0]]
    y_coordinates = [start_windows_coordinates[1], end_windows_coordinates[1]]

    if x_coordinates[0] == x_coordinates[1]:
        x_coordinates[1] += 1

    if y_coordinates[0] == y_coordinates[1]:
        y_coordinates[1] += 1

    interpolation_func = scipy.interpolate.interp1d(x_coordinates, y_coordinates)    
    intermediate_x_coordinates = np.linspace(start_windows_coordinates[0], end_windows_coordinates[0], steps + 1)[1:]
    
    coordinates = list(map(lambda x: (int(round(x)), int(interpolation_func(x))), intermediate_x_coordinates))
    
    return coordinates

Another problem is that I have no idea how to create a functionality, when given series of movements are applied only while mouse is pressed and when its released, the whole cycle should reset.

Could You please help me solve these issues, or maybe offer a different way of approach based on the whole idea?

mikuszefski
  • 3,943
  • 1
  • 25
  • 38
  • Could you please show a [minimal, reproducible sample](https://stackoverflow.com/help/minimal-reproducible-example) without private information to help me reproduce your problem? – Zeus Jan 22 '21 at 06:54
  • If i click the mouse twice the bug happens, after that the system is stuck with unkillable python.exe process – Edvinas Deksnys Jan 24 '21 at 19:42
  • I believe You just need to add these inports and it should work `import pyautogui, time, pydirectinput, win32api, ctypes, scipy.interpolate, pyWinhook` `import numpy as np` – Edvinas Deksnys Jan 25 '21 at 08:17
  • I have tried this both with hooks and with mouse listener, the problem persists – Edvinas Deksnys Jan 25 '21 at 08:25

0 Answers0