Script to know when user clicks the mouse causes deadlock

119 Views Asked by At

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?

0

There are 0 best solutions below