moses-palmer / pynput

Sends virtual input commands
GNU Lesser General Public License v3.0
1.73k stars 243 forks source link

mouse listener example for win32_event_filter #585

Closed kimkun07 closed 5 months ago

kimkun07 commented 5 months ago

Description It was not easy for me to implement win32_event_filter mouse function. So I thought it would be nice to share an example. If you need to suppress specific event, you might want to check code below. win32_event_filter can completely replace on_move, on_click, on_scroll handlers.

from ctypes import wintypes
from pynput import mouse
from pynput.mouse._win32 import Listener, WHEEL_DELTA

class InputMouse(Listener):
    def __init__(self):
        super().__init__(
            win32_event_filter=self.win32_event_filter,
        )

    def win32_event_filter(self, msg, data: Listener._MSLLHOOKSTRUCT):
        # return False: event passed to system, but not passed to the listener
        # self.suppress_event(): event not passed to the system and listener
        #   NOTE: self.suppress_event() raises exception, and you should not catch it
        # raise self.StopException(): stop the listener (same as return False in normal on_click listener)

        x: int
        y: int
        button: mouse.Button
        pressed: bool

        if msg == self.WM_MOUSEMOVE:
            x, y = data.pt.x, data.pt.y
            # self.on_move(x, y)

        elif msg in self.CLICK_BUTTONS:
            button, pressed = self.CLICK_BUTTONS[msg]
            assert isinstance(button, mouse.Button)
            assert isinstance(pressed, bool)
            x, y = data.pt.x, data.pt.y
            # self.on_click(x, y, button, pressed)

            if pressed:
                self.pos_press = (x, y)
                self.suppress_event()
            else:
                self.pos_release = (x, y)
                # NOTE: you cannot suppress event when you want to stop listener
                # self.suppress_event()
                raise self.StopException()

        elif msg in self.X_BUTTONS:
            button, pressed = self.X_BUTTONS[msg][data.mouseData >> 16]
            assert isinstance(button, mouse.Button)
            assert isinstance(pressed, bool)
            x, y = data.pt.x, data.pt.y
            # self.on_click(x, y, button, pressed)

        elif msg in self.SCROLL_BUTTONS:
            mx, my = self.SCROLL_BUTTONS[msg]
            assert isinstance(mx, int)
            assert isinstance(my, int)
            dd = wintypes.SHORT(data.mouseData >> 16).value // WHEEL_DELTA
            x, y = data.pt.x, data.pt.y
            dx: int = dd * mx
            dy: int = dd * my
            # self.on_scroll(x, y, dx, dy)
kimkun07 commented 5 months ago

Made PR #586