From bc09da5a7b65b08b5d5dcd1e90173ad3b6081c23 Mon Sep 17 00:00:00 2001 From: Syndamia Date: Mon, 29 Jul 2019 11:46:36 +0300 Subject: Did some more work in Python and started officially learning Java --- .../Lib/site-packages/keyboard/_darwinmouse.py | 173 +++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 Python/venv1/Lib/site-packages/keyboard/_darwinmouse.py (limited to 'Python/venv1/Lib/site-packages/keyboard/_darwinmouse.py') diff --git a/Python/venv1/Lib/site-packages/keyboard/_darwinmouse.py b/Python/venv1/Lib/site-packages/keyboard/_darwinmouse.py new file mode 100644 index 0000000..b112dc0 --- /dev/null +++ b/Python/venv1/Lib/site-packages/keyboard/_darwinmouse.py @@ -0,0 +1,173 @@ +import os +import datetime +import threading +import Quartz +from ._mouse_event import ButtonEvent, WheelEvent, MoveEvent, LEFT, RIGHT, MIDDLE, X, X2, UP, DOWN + +_button_mapping = { + LEFT: (Quartz.kCGMouseButtonLeft, Quartz.kCGEventLeftMouseDown, Quartz.kCGEventLeftMouseUp, Quartz.kCGEventLeftMouseDragged), + RIGHT: (Quartz.kCGMouseButtonRight, Quartz.kCGEventRightMouseDown, Quartz.kCGEventRightMouseUp, Quartz.kCGEventRightMouseDragged), + MIDDLE: (Quartz.kCGMouseButtonCenter, Quartz.kCGEventOtherMouseDown, Quartz.kCGEventOtherMouseUp, Quartz.kCGEventOtherMouseDragged) +} +_button_state = { + LEFT: False, + RIGHT: False, + MIDDLE: False +} +_last_click = { + "time": None, + "button": None, + "position": None, + "click_count": 0 +} + +class MouseEventListener(object): + def __init__(self, callback, blocking=False): + self.blocking = blocking + self.callback = callback + self.listening = True + + def run(self): + """ Creates a listener and loops while waiting for an event. Intended to run as + a background thread. """ + self.tap = Quartz.CGEventTapCreate( + Quartz.kCGSessionEventTap, + Quartz.kCGHeadInsertEventTap, + Quartz.kCGEventTapOptionDefault, + Quartz.CGEventMaskBit(Quartz.kCGEventLeftMouseDown) | + Quartz.CGEventMaskBit(Quartz.kCGEventLeftMouseUp) | + Quartz.CGEventMaskBit(Quartz.kCGEventRightMouseDown) | + Quartz.CGEventMaskBit(Quartz.kCGEventRightMouseUp) | + Quartz.CGEventMaskBit(Quartz.kCGEventOtherMouseDown) | + Quartz.CGEventMaskBit(Quartz.kCGEventOtherMouseUp) | + Quartz.CGEventMaskBit(Quartz.kCGEventMouseMoved) | + Quartz.CGEventMaskBit(Quartz.kCGEventScrollWheel), + self.handler, + None) + loopsource = Quartz.CFMachPortCreateRunLoopSource(None, self.tap, 0) + loop = Quartz.CFRunLoopGetCurrent() + Quartz.CFRunLoopAddSource(loop, loopsource, Quartz.kCFRunLoopDefaultMode) + Quartz.CGEventTapEnable(self.tap, True) + + while self.listening: + Quartz.CFRunLoopRunInMode(Quartz.kCFRunLoopDefaultMode, 5, False) + + def handler(self, proxy, e_type, event, refcon): + # TODO Separate event types by button/wheel/move + scan_code = Quartz.CGEventGetIntegerValueField(event, Quartz.kCGKeyboardEventKeycode) + key_name = name_from_scancode(scan_code) + flags = Quartz.CGEventGetFlags(event) + event_type = "" + is_keypad = (flags & Quartz.kCGEventFlagMaskNumericPad) + if e_type == Quartz.kCGEventKeyDown: + event_type = "down" + elif e_type == Quartz.kCGEventKeyUp: + event_type = "up" + + if self.blocking: + return None + + self.callback(KeyboardEvent(event_type, scan_code, name=key_name, is_keypad=is_keypad)) + return event + +# Exports + +def init(): + """ Initializes mouse state """ + pass + +def listen(queue): + """ Appends events to the queue (ButtonEvent, WheelEvent, and MoveEvent). """ + if not os.geteuid() == 0: + raise OSError("Error 13 - Must be run as administrator") + listener = MouseEventListener(lambda e: queue.put(e) or is_allowed(e.name, e.event_type == KEY_UP)) + t = threading.Thread(target=listener.run, args=()) + t.daemon = True + t.start() + +def press(button=LEFT): + """ Sends a down event for the specified button, using the provided constants """ + location = get_position() + button_code, button_down, _, _ = _button_mapping[button] + e = Quartz.CGEventCreateMouseEvent( + None, + button_down, + location, + button_code) + + # Check if this is a double-click (same location within the last 300ms) + if _last_click["time"] is not None and datetime.datetime.now() - _last_click["time"] < datetime.timedelta(seconds=0.3) and _last_click["button"] == button and _last_click["position"] == location: + # Repeated Click + _last_click["click_count"] = min(3, _last_click["click_count"]+1) + else: + # Not a double-click - Reset last click + _last_click["click_count"] = 1 + Quartz.CGEventSetIntegerValueField( + e, + Quartz.kCGMouseEventClickState, + _last_click["click_count"]) + Quartz.CGEventPost(Quartz.kCGHIDEventTap, e) + _button_state[button] = True + _last_click["time"] = datetime.datetime.now() + _last_click["button"] = button + _last_click["position"] = location + +def release(button=LEFT): + """ Sends an up event for the specified button, using the provided constants """ + location = get_position() + button_code, _, button_up, _ = _button_mapping[button] + e = Quartz.CGEventCreateMouseEvent( + None, + button_up, + location, + button_code) + + if _last_click["time"] is not None and _last_click["time"] > datetime.datetime.now() - datetime.timedelta(microseconds=300000) and _last_click["button"] == button and _last_click["position"] == location: + # Repeated Click + Quartz.CGEventSetIntegerValueField( + e, + Quartz.kCGMouseEventClickState, + _last_click["click_count"]) + Quartz.CGEventPost(Quartz.kCGHIDEventTap, e) + _button_state[button] = False + +def wheel(delta=1): + """ Sends a wheel event for the provided number of clicks. May be negative to reverse + direction. """ + location = get_position() + e = Quartz.CGEventCreateMouseEvent( + None, + Quartz.kCGEventScrollWheel, + location, + Quartz.kCGMouseButtonLeft) + e2 = Quartz.CGEventCreateScrollWheelEvent( + None, + Quartz.kCGScrollEventUnitLine, + 1, + delta) + Quartz.CGEventPost(Quartz.kCGHIDEventTap, e) + Quartz.CGEventPost(Quartz.kCGHIDEventTap, e2) + +def move_to(x, y): + """ Sets the mouse's location to the specified coordinates. """ + for b in _button_state: + if _button_state[b]: + e = Quartz.CGEventCreateMouseEvent( + None, + _button_mapping[b][3], # Drag Event + (x, y), + _button_mapping[b][0]) + break + else: + e = Quartz.CGEventCreateMouseEvent( + None, + Quartz.kCGEventMouseMoved, + (x, y), + Quartz.kCGMouseButtonLeft) + Quartz.CGEventPost(Quartz.kCGHIDEventTap, e) + +def get_position(): + """ Returns the mouse's location as a tuple of (x, y). """ + e = Quartz.CGEventCreate(None) + point = Quartz.CGEventGetLocation(e) + return (point.x, point.y) \ No newline at end of file -- cgit v1.2.3