1#Copyright 2013 Paul Barton 2# 3#This program is free software: you can redistribute it and/or modify 4#it under the terms of the GNU General Public License as published by 5#the Free Software Foundation, either version 3 of the License, or 6#(at your option) any later version. 7# 8#This program is distributed in the hope that it will be useful, 9#but WITHOUT ANY WARRANTY; without even the implied warranty of 10#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11#GNU General Public License for more details. 12# 13#You should have received a copy of the GNU General Public License 14#along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16import Quartz 17from AppKit import NSEvent, NSScreen 18from .base import PyMouseMeta, PyMouseEventMeta 19 20pressID = [None, Quartz.kCGEventLeftMouseDown, 21 Quartz.kCGEventRightMouseDown, Quartz.kCGEventOtherMouseDown] 22releaseID = [None, Quartz.kCGEventLeftMouseUp, 23 Quartz.kCGEventRightMouseUp, Quartz.kCGEventOtherMouseUp] 24 25 26class PyMouse(PyMouseMeta): 27 28 def press(self, x, y, button=1): 29 event = Quartz.CGEventCreateMouseEvent(None, 30 pressID[button], 31 (x, y), 32 button - 1) 33 Quartz.CGEventPost(Quartz.kCGHIDEventTap, event) 34 35 def release(self, x, y, button=1): 36 event = Quartz.CGEventCreateMouseEvent(None, 37 releaseID[button], 38 (x, y), 39 button - 1) 40 Quartz.CGEventPost(Quartz.kCGHIDEventTap, event) 41 42 def move(self, x, y): 43 move = Quartz.CGEventCreateMouseEvent(None, Quartz.kCGEventMouseMoved, (x, y), 0) 44 Quartz.CGEventPost(Quartz.kCGHIDEventTap, move) 45 46 def drag(self, x, y): 47 drag = Quartz.CGEventCreateMouseEvent(None, Quartz.kCGEventLeftMouseDragged, (x, y), 0) 48 Quartz.CGEventPost(Quartz.kCGHIDEventTap, drag) 49 50 def position(self): 51 loc = NSEvent.mouseLocation() 52 return loc.x, Quartz.CGDisplayPixelsHigh(0) - loc.y 53 54 def screen_size(self): 55 return NSScreen.mainScreen().frame().size.width, NSScreen.mainScreen().frame().size.height 56 57 def scroll(self, vertical=None, horizontal=None, depth=None): 58 #Local submethod for generating Mac scroll events in one axis at a time 59 def scroll_event(y_move=0, x_move=0, z_move=0, n=1): 60 for _ in range(abs(n)): 61 scrollWheelEvent = Quartz.CGEventCreateScrollWheelEvent( 62 None, # No source 63 Quartz.kCGScrollEventUnitLine, # Unit of measurement is lines 64 3, # Number of wheels(dimensions) 65 y_move, 66 x_move, 67 z_move) 68 Quartz.CGEventPost(Quartz.kCGHIDEventTap, scrollWheelEvent) 69 70 #Execute vertical then horizontal then depth scrolling events 71 if vertical is not None: 72 vertical = int(vertical) 73 if vertical == 0: # Do nothing with 0 distance 74 pass 75 elif vertical > 0: # Scroll up if positive 76 scroll_event(y_move=1, n=vertical) 77 else: # Scroll down if negative 78 scroll_event(y_move=-1, n=abs(vertical)) 79 if horizontal is not None: 80 horizontal = int(horizontal) 81 if horizontal == 0: # Do nothing with 0 distance 82 pass 83 elif horizontal > 0: # Scroll right if positive 84 scroll_event(x_move=1, n=horizontal) 85 else: # Scroll left if negative 86 scroll_event(x_move=-1, n=abs(horizontal)) 87 if depth is not None: 88 depth = int(depth) 89 if depth == 0: # Do nothing with 0 distance 90 pass 91 elif vertical > 0: # Scroll "out" if positive 92 scroll_event(z_move=1, n=depth) 93 else: # Scroll "in" if negative 94 scroll_event(z_move=-1, n=abs(depth)) 95 96 97class PyMouseEvent(PyMouseEventMeta): 98 def run(self): 99 tap = Quartz.CGEventTapCreate( 100 Quartz.kCGSessionEventTap, 101 Quartz.kCGHeadInsertEventTap, 102 Quartz.kCGEventTapOptionDefault, 103 Quartz.CGEventMaskBit(Quartz.kCGEventMouseMoved) | 104 Quartz.CGEventMaskBit(Quartz.kCGEventLeftMouseDown) | 105 Quartz.CGEventMaskBit(Quartz.kCGEventLeftMouseUp) | 106 Quartz.CGEventMaskBit(Quartz.kCGEventRightMouseDown) | 107 Quartz.CGEventMaskBit(Quartz.kCGEventRightMouseUp) | 108 Quartz.CGEventMaskBit(Quartz.kCGEventOtherMouseDown) | 109 Quartz.CGEventMaskBit(Quartz.kCGEventOtherMouseUp), 110 self.handler, 111 None) 112 113 loopsource = Quartz.CFMachPortCreateRunLoopSource(None, tap, 0) 114 loop = Quartz.CFRunLoopGetCurrent() 115 Quartz.CFRunLoopAddSource(loop, loopsource, Quartz.kCFRunLoopDefaultMode) 116 Quartz.CGEventTapEnable(tap, True) 117 118 while self.state: 119 Quartz.CFRunLoopRunInMode(Quartz.kCFRunLoopDefaultMode, 5, False) 120 121 def handler(self, proxy, type, event, refcon): 122 (x, y) = Quartz.CGEventGetLocation(event) 123 if type in pressID: 124 self.click(x, y, pressID.index(type), True) 125 elif type in releaseID: 126 self.click(x, y, releaseID.index(type), False) 127 else: 128 self.move(x, y) 129 130 if self.capture: 131 Quartz.CGEventSetType(event, Quartz.kCGEventNull) 132 133 return event 134