202 lines
5.7 KiB
Python
202 lines
5.7 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
import ctypes
|
||
|
import time
|
||
|
from ctypes import c_short, c_char, c_uint8, c_int32, c_int, c_uint, c_uint32, c_long, byref, Structure, CFUNCTYPE, POINTER
|
||
|
from ctypes.wintypes import DWORD, BOOL, HHOOK, MSG, LPWSTR, WCHAR, WPARAM, LPARAM
|
||
|
LPMSG = POINTER(MSG)
|
||
|
|
||
|
import atexit
|
||
|
|
||
|
from ._mouse_event import ButtonEvent, WheelEvent, MoveEvent, LEFT, RIGHT, MIDDLE, X, X2, UP, DOWN, DOUBLE, WHEEL, HORIZONTAL, VERTICAL
|
||
|
|
||
|
#https://github.com/boppreh/mouse/issues/1
|
||
|
#user32 = ctypes.windll.user32
|
||
|
user32 = ctypes.WinDLL('user32', use_last_error = True)
|
||
|
|
||
|
class MSLLHOOKSTRUCT(Structure):
|
||
|
_fields_ = [("x", c_long),
|
||
|
("y", c_long),
|
||
|
('data', c_int32),
|
||
|
('reserved', c_int32),
|
||
|
("flags", DWORD),
|
||
|
("time", c_int),
|
||
|
]
|
||
|
|
||
|
LowLevelMouseProc = CFUNCTYPE(c_int, WPARAM, LPARAM, POINTER(MSLLHOOKSTRUCT))
|
||
|
|
||
|
SetWindowsHookEx = user32.SetWindowsHookExA
|
||
|
#SetWindowsHookEx.argtypes = [c_int, LowLevelMouseProc, c_int, c_int]
|
||
|
SetWindowsHookEx.restype = HHOOK
|
||
|
|
||
|
CallNextHookEx = user32.CallNextHookEx
|
||
|
#CallNextHookEx.argtypes = [c_int , c_int, c_int, POINTER(MSLLHOOKSTRUCT)]
|
||
|
CallNextHookEx.restype = c_int
|
||
|
|
||
|
UnhookWindowsHookEx = user32.UnhookWindowsHookEx
|
||
|
UnhookWindowsHookEx.argtypes = [HHOOK]
|
||
|
UnhookWindowsHookEx.restype = BOOL
|
||
|
|
||
|
GetMessage = user32.GetMessageW
|
||
|
GetMessage.argtypes = [LPMSG, c_int, c_int, c_int]
|
||
|
GetMessage.restype = BOOL
|
||
|
|
||
|
TranslateMessage = user32.TranslateMessage
|
||
|
TranslateMessage.argtypes = [LPMSG]
|
||
|
TranslateMessage.restype = BOOL
|
||
|
|
||
|
DispatchMessage = user32.DispatchMessageA
|
||
|
DispatchMessage.argtypes = [LPMSG]
|
||
|
|
||
|
# Beware, as of 2016-01-30 the official docs have a very incomplete list.
|
||
|
# This one was compiled from experience and may be incomplete.
|
||
|
WM_MOUSEMOVE = 0x200
|
||
|
WM_LBUTTONDOWN = 0x201
|
||
|
WM_LBUTTONUP = 0x202
|
||
|
WM_LBUTTONDBLCLK = 0x203
|
||
|
WM_RBUTTONDOWN = 0x204
|
||
|
WM_RBUTTONUP = 0x205
|
||
|
WM_RBUTTONDBLCLK = 0x206
|
||
|
WM_MBUTTONDOWN = 0x207
|
||
|
WM_MBUTTONUP = 0x208
|
||
|
WM_MBUTTONDBLCLK = 0x209
|
||
|
WM_MOUSEWHEEL = 0x20A
|
||
|
WM_XBUTTONDOWN = 0x20B
|
||
|
WM_XBUTTONUP = 0x20C
|
||
|
WM_XBUTTONDBLCLK = 0x20D
|
||
|
WM_NCXBUTTONDOWN = 0x00AB
|
||
|
WM_NCXBUTTONUP = 0x00AC
|
||
|
WM_NCXBUTTONDBLCLK = 0x00AD
|
||
|
WM_MOUSEHWHEEL = 0x20E
|
||
|
WM_LBUTTONDOWN = 0x0201
|
||
|
WM_LBUTTONUP = 0x0202
|
||
|
WM_MOUSEMOVE = 0x0200
|
||
|
WM_MOUSEWHEEL = 0x020A
|
||
|
WM_MOUSEHWHEEL = 0x020E
|
||
|
WM_RBUTTONDOWN = 0x0204
|
||
|
WM_RBUTTONUP = 0x0205
|
||
|
|
||
|
buttons_by_wm_code = {
|
||
|
WM_LBUTTONDOWN: (DOWN, LEFT),
|
||
|
WM_LBUTTONUP: (UP, LEFT),
|
||
|
WM_LBUTTONDBLCLK: (DOUBLE, LEFT),
|
||
|
|
||
|
WM_RBUTTONDOWN: (DOWN, RIGHT),
|
||
|
WM_RBUTTONUP: (UP, RIGHT),
|
||
|
WM_RBUTTONDBLCLK: (DOUBLE, RIGHT),
|
||
|
|
||
|
WM_MBUTTONDOWN: (DOWN, MIDDLE),
|
||
|
WM_MBUTTONUP: (UP, MIDDLE),
|
||
|
WM_MBUTTONDBLCLK: (DOUBLE, MIDDLE),
|
||
|
|
||
|
WM_XBUTTONDOWN: (DOWN, X),
|
||
|
WM_XBUTTONUP: (UP, X),
|
||
|
WM_XBUTTONDBLCLK: (DOUBLE, X),
|
||
|
}
|
||
|
|
||
|
MOUSEEVENTF_ABSOLUTE = 0x8000
|
||
|
MOUSEEVENTF_MOVE = 0x1
|
||
|
MOUSEEVENTF_WHEEL = 0x800
|
||
|
MOUSEEVENTF_HWHEEL = 0x1000
|
||
|
MOUSEEVENTF_LEFTDOWN = 0x2
|
||
|
MOUSEEVENTF_LEFTUP = 0x4
|
||
|
MOUSEEVENTF_RIGHTDOWN = 0x8
|
||
|
MOUSEEVENTF_RIGHTUP = 0x10
|
||
|
MOUSEEVENTF_MIDDLEDOWN = 0x20
|
||
|
MOUSEEVENTF_MIDDLEUP = 0x40
|
||
|
MOUSEEVENTF_XDOWN = 0x0080
|
||
|
MOUSEEVENTF_XUP = 0x0100
|
||
|
|
||
|
simulated_mouse_codes = {
|
||
|
(WHEEL, HORIZONTAL): MOUSEEVENTF_HWHEEL,
|
||
|
(WHEEL, VERTICAL): MOUSEEVENTF_WHEEL,
|
||
|
|
||
|
(DOWN, LEFT): MOUSEEVENTF_LEFTDOWN,
|
||
|
(UP, LEFT): MOUSEEVENTF_LEFTUP,
|
||
|
|
||
|
(DOWN, RIGHT): MOUSEEVENTF_RIGHTDOWN,
|
||
|
(UP, RIGHT): MOUSEEVENTF_RIGHTUP,
|
||
|
|
||
|
(DOWN, MIDDLE): MOUSEEVENTF_MIDDLEDOWN,
|
||
|
(UP, MIDDLE): MOUSEEVENTF_MIDDLEUP,
|
||
|
|
||
|
(DOWN, X): MOUSEEVENTF_XDOWN,
|
||
|
(UP, X): MOUSEEVENTF_XUP,
|
||
|
}
|
||
|
|
||
|
NULL = c_int(0)
|
||
|
|
||
|
WHEEL_DELTA = 120
|
||
|
|
||
|
init = lambda: None
|
||
|
|
||
|
def listen(queue):
|
||
|
def low_level_mouse_handler(nCode, wParam, lParam):
|
||
|
struct = lParam.contents
|
||
|
# Can't use struct.time because it's usually zero.
|
||
|
t = time.time()
|
||
|
|
||
|
if wParam == WM_MOUSEMOVE:
|
||
|
event = MoveEvent(struct.x, struct.y, t)
|
||
|
elif wParam == WM_MOUSEWHEEL:
|
||
|
event = WheelEvent(struct.data / (WHEEL_DELTA * (2<<15)), t)
|
||
|
elif wParam in buttons_by_wm_code:
|
||
|
type, button = buttons_by_wm_code.get(wParam, ('?', '?'))
|
||
|
if wParam >= WM_XBUTTONDOWN:
|
||
|
button = {0x10000: X, 0x20000: X2}[struct.data]
|
||
|
event = ButtonEvent(type, button, t)
|
||
|
|
||
|
queue.put(event)
|
||
|
return CallNextHookEx(NULL, nCode, wParam, lParam)
|
||
|
|
||
|
WH_MOUSE_LL = c_int(14)
|
||
|
mouse_callback = LowLevelMouseProc(low_level_mouse_handler)
|
||
|
mouse_hook = SetWindowsHookEx(WH_MOUSE_LL, mouse_callback, NULL, NULL)
|
||
|
|
||
|
# Register to remove the hook when the interpreter exits. Unfortunately a
|
||
|
# try/finally block doesn't seem to work here.
|
||
|
atexit.register(UnhookWindowsHookEx, mouse_hook)
|
||
|
|
||
|
msg = LPMSG()
|
||
|
while not GetMessage(msg, NULL, NULL, NULL):
|
||
|
TranslateMessage(msg)
|
||
|
DispatchMessage(msg)
|
||
|
|
||
|
def _translate_button(button):
|
||
|
if button == X or button == X2:
|
||
|
return X, {X: 0x10000, X2: 0x20000}[button]
|
||
|
else:
|
||
|
return button, 0
|
||
|
|
||
|
def press(button=LEFT):
|
||
|
button, data = _translate_button(button)
|
||
|
code = simulated_mouse_codes[(DOWN, button)]
|
||
|
user32.mouse_event(code, 0, 0, data, 0)
|
||
|
|
||
|
def release(button=LEFT):
|
||
|
button, data = _translate_button(button)
|
||
|
code = simulated_mouse_codes[(UP, button)]
|
||
|
user32.mouse_event(code, 0, 0, data, 0)
|
||
|
|
||
|
def wheel(delta=1):
|
||
|
code = simulated_mouse_codes[(WHEEL, VERTICAL)]
|
||
|
user32.mouse_event(code, 0, 0, int(delta * WHEEL_DELTA), 0)
|
||
|
|
||
|
def move_to(x, y):
|
||
|
user32.SetCursorPos(int(x), int(y))
|
||
|
|
||
|
def move_relative(x, y):
|
||
|
user32.mouse_event(MOUSEEVENTF_MOVE, int(x), int(y), 0, 0)
|
||
|
|
||
|
class POINT(Structure):
|
||
|
_fields_ = [("x", c_long), ("y", c_long)]
|
||
|
|
||
|
def get_position():
|
||
|
point = POINT()
|
||
|
user32.GetCursorPos(byref(point))
|
||
|
return (point.x, point.y)
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
def p(e):
|
||
|
print(e)
|
||
|
listen(p)
|