def run_event_loop(): LOG.info("try to load mac hotkey event loop") import Quartz from AppKit import NSSystemDefined # Set up a tap, with type of tap, location, options and event mask tap = Quartz.CGEventTapCreate( Quartz.kCGSessionEventTap, # Session level is enough for our needs Quartz.kCGHeadInsertEventTap, # Insert wherever, we do not filter Quartz.kCGEventTapOptionDefault, # NSSystemDefined for media keys Quartz.CGEventMaskBit(NSSystemDefined), keyboard_tap_callback, None ) run_loop_source = Quartz.CFMachPortCreateRunLoopSource( None, tap, 0) Quartz.CFRunLoopAddSource( Quartz.CFRunLoopGetCurrent(), run_loop_source, Quartz.kCFRunLoopDefaultMode ) # Enable the tap Quartz.CGEventTapEnable(tap, True) # and run! This won't return until we exit or are terminated. Quartz.CFRunLoopRun() LOG.error('Mac hotkey event loop exit') return []
def filter_input(): logging.info('Filtering all keys and mouse movements...') typed_keys = '' def keyboard_cb(proxy, type_, event, refcon): nonlocal typed_keys if not event: return None # Convert the Quartz CGEvent into something more useful ns_event = AppKit.NSEvent.eventWithCGEvent_(event) if not ns_event: return None if ns_event.type() == Quartz.kCGEventKeyDown: typed_keys += ns_event.characters() if typed_keys.endswith(EXIT_STRING): logging.info('Emergency exit!') Quartz.CFRunLoopStop(Quartz.CFRunLoopGetCurrent()) # Filter every event return None logging.info('Creating tap...') # Set up a tap, with type of tap, location, options and event mask tap = Quartz.CGEventTapCreate( Quartz.kCGSessionEventTap, # Insert at the head so we can filter Quartz.kCGHeadInsertEventTap, # Enable filtering Quartz.kCGEventTapOptionDefault, # Act on all key and mouse events Quartz.kCGAnyInputEventType, # Our callback function keyboard_cb, None) runLoopSource = Quartz.CFMachPortCreateRunLoopSource(None, tap, 0) Quartz.CFRunLoopAddSource(Quartz.CFRunLoopGetCurrent(), runLoopSource, Quartz.kCFRunLoopDefaultMode) Quartz.CGEventTapEnable(tap, True) logging.info('Starting run loop...') Quartz.CFRunLoopRun() logging.info('Run loop finished.')
def run_event_loop(player): LOG.info("try to load mac hotkey event loop") import Quartz from AppKit import NSKeyUp, NSSystemDefined, NSEvent def keyboard_tap_callback(proxy, type_, event, refcon): if type_ < 0 or type_ > 0x7fffffff: LOG.error('Unkown mac event') Quartz.CFRunLoopRun() return run_event_loop(ControllerApi.player) try: key_event = NSEvent.eventWithCGEvent_(event) except: LOG.info("mac event cast error") return event if key_event.subtype() == 8: key_code = (key_event.data1() & 0xFFFF0000) >> 16 key_state = (key_event.data1() & 0xFF00) >> 8 if key_code is 16 or key_code is 19 or key_code is 20: # 16 for play-pause, 19 for next, 20 for previous if key_state == NSKeyUp: if key_code is 19: player.play_next() elif key_code is 20: player.play_last() elif key_code is 16: player.play_or_pause() return None return event # Set up a tap, with type of tap, location, options and event mask tap = Quartz.CGEventTapCreate( Quartz.kCGSessionEventTap, # Session level is enough for our needs Quartz.kCGHeadInsertEventTap, # Insert wherever, we do not filter Quartz.kCGEventTapOptionDefault, # NSSystemDefined for media keys Quartz.CGEventMaskBit(NSSystemDefined), keyboard_tap_callback, None ) run_loop_source = Quartz.CFMachPortCreateRunLoopSource( Quartz.kCFAllocatorDefault, tap, 0) Quartz.CFRunLoopAddSource( Quartz.CFRunLoopGetCurrent(), run_loop_source, Quartz.kCFRunLoopDefaultMode ) # Enable the tap Quartz.CGEventTapEnable(tap, True) # and run! This won't return until we exit or are terminated. Quartz.CFRunLoopRun() LOG.error('Mac hotkey exit event ')
def run_event_loop(): logger.info('try to load mac hotkey event loop') # Set up a tap, with type of tap, location, options and event mask def create_tap(): return Quartz.CGEventTapCreate( Quartz.kCGSessionEventTap, # Session level is enough for our needs Quartz.kCGHeadInsertEventTap, # Insert wherever, we do not filter Quartz.kCGEventTapOptionDefault, # NSSystemDefined for media keys Quartz.CGEventMaskBit(NSSystemDefined), keyboard_tap_callback, None) tap = create_tap() if tap is None: logger.error('Error occurred when trying to listen global hotkey. ' 'trying to popup a prompt dialog to ask for permission.') # we do not use pyobjc-framework-ApplicationServices directly, since it # causes segfault when call AXIsProcessTrustedWithOptions function import objc AS = objc.loadBundle( 'CoreServices', globals(), '/System/Library/Frameworks/ApplicationServices.framework') objc.loadBundleFunctions(AS, globals(), [('AXIsProcessTrustedWithOptions', b'Z@')]) objc.loadBundleVariables(AS, globals(), [('kAXTrustedCheckOptionPrompt', b'@')]) trusted = AXIsProcessTrustedWithOptions( {kAXTrustedCheckOptionPrompt: True}) # noqa if not trusted: logger.info( 'Have popuped a prompt dialog to ask for accessibility.' 'You can restart feeluown after you grant access to it.') else: logger.warning('Have already grant accessibility, ' 'but we still can not listen global hotkey,' 'theoretically, this should not happen.') return run_loop_source = Quartz.CFMachPortCreateRunLoopSource(None, tap, 0) Quartz.CFRunLoopAddSource(Quartz.CFRunLoopGetCurrent(), run_loop_source, Quartz.kCFRunLoopDefaultMode) # Enable the tap Quartz.CGEventTapEnable(tap, True) # and run! This won't return until we exit or are terminated. Quartz.CFRunLoopRun() logger.error('mac hotkey event loop exit') return []
def start_event_loop(self): """ Start event loop. This method will not return until the event loop is stopped by \ calling :paramref:`stop_event_loop`. :return: None. """ # Enable the tap Quartz.CGEventTapEnable(self._tap, True) # Run the current run loop Quartz.CFRunLoopRun()
def on(self): mask = Quartz.CGEventMaskBit(Quartz.kCGEventKeyDown) tap = Quartz.CGEventTapCreate(Quartz.kCGSessionEventTap, Quartz.kCGHeadInsertEventTap, 0, mask, self._event_call_back, None) assert tap, "failed to create event tap" run_loop_source = Quartz.CFMachPortCreateRunLoopSource( Quartz.kCFAllocatorDefault, tap, 0) Quartz.CFRunLoopAddSource(Quartz.CFRunLoopGetCurrent(), run_loop_source, Quartz.kCFRunLoopCommonModes) Quartz.CGEventTapEnable(tap, True) Quartz.CFRunLoopRun()
def runEventsCapture(self): import AppKit pool = AppKit.NSAutoreleasePool.alloc().init() from AppKit import NSSystemDefined import Quartz self.runLoopRef = Quartz.CFRunLoopGetCurrent() while True: # https://developer.apple.com/library/mac/#documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html try: tap = Quartz.CGEventTapCreate( Quartz. kCGSessionEventTap, # Quartz.kCGSessionEventTap or kCGHIDEventTap Quartz. kCGHeadInsertEventTap, # Insert wherever, we do not filter Quartz. kCGEventTapOptionDefault, # we can't listen-only because we want to consume it Quartz.CGEventMaskBit( NSSystemDefined), # NSSystemDefined for media keys self.eventTap, None) except TypeError: # wrong number of args? I got this on MacOSX 10.6, # where it expected 5 args (instead of 6). # however, it crashes when I call it without the last arg! assert False, "CGEventTapCreate has an invalid signature on your system. This is fixed in later MacOSX versions. It should work in at least >=MacOSX 10.8. It is known to be broken in MacOSX 10.6." assert tap # Create a runloop source and add it to the current loop runLoopSource = Quartz.CFMachPortCreateRunLoopSource(None, tap, 0) Quartz.CFRunLoopAddSource(self.runLoopRef, runLoopSource, Quartz.kCFRunLoopDefaultMode) # Enable the tap Quartz.CGEventTapEnable(tap, True) try: # and run! This won't return until we exit or are terminated. Quartz.CFRunLoopRun() except Exception: sys.excepthook(*sys.exc_info()) continue # rerun # this is a regular quit break del pool
def keyHook(): tap = Quartz.CGEventTapCreate(Quartz.kCGHIDEventTap, Quartz.kCGHeadInsertEventTap, Quartz.kCGEventTapOptionDefault, Quartz.kCGEventMaskForAllEvents, keyboardTapCallback, None) if tap is None: print('failed to create event tap!') sys.exit(1) runLoopSource = Quartz.CFMachPortCreateRunLoopSource(None, tap, 0) Quartz.CFRunLoopAddSource(Quartz.CFRunLoopGetCurrent(), runLoopSource, Quartz.kCFRunLoopDefaultMode) Quartz.CGEventTapEnable(tap, True) Quartz.CFRunLoopRun()
def runEventsCapture(cls, controlPath): tapHandler = cls(controlPath) tap = Quartz.CGEventTapCreate( Quartz.kCGSessionEventTap, # Session level is enough for our needs Quartz.kCGHeadInsertEventTap, # Insert wherever, we do not filter Quartz.kCGEventTapOptionListenOnly, # Listening is enough # NSSystemDefined for media keys Quartz.CGEventMaskBit(NSSystemDefined), tapHandler.eventTap, None) # Create a runloop source and add it to the current loop runLoopSource = Quartz.CFMachPortCreateRunLoopSource(None, tap, 0) Quartz.CFRunLoopAddSource(Quartz.CFRunLoopGetCurrent(), runLoopSource, Quartz.kCFRunLoopDefaultMode) # Enable the tap Quartz.CGEventTapEnable(tap, True) # and run! This won't return until we exit or are terminated. Quartz.CFRunLoopRun()
class EventTap(object): """ wraps a Quartz keyboard event tap """ def __init__(self, cb, block_cb): self.runner = EventTapRunner.alloc().init() self.setup_event_tap(cb, block_cb) def setup_event_tap(self, callback, should_block): def keyboardTapCallback(proxy, type_, event, refcon): if type_ == Quartz.kCGEventTapDisabledByTimeout & 0xffffffff: # event tap timed out, re-enable. Quartz.CGEventTapEnable(self.tap, True) NSLog('event tap timed out, re-enabling') return None # Convert the Quartz CGEvent into something more useful keyEvent = NSEvent.eventWithCGEvent_(event) try: selector = objc.selector(self.runner.call_, signature='v@:@') self.runner.performSelectorOnMainThread_withObject_waitUntilDone_( selector, (callback, keyEvent), False) if should_block(keyEvent): return None except Exception, e: tb = sys.exc_info() print ''.join(traceback.format_exception(*tb)) print 'Exception: ' + e.message return event self.tap = Quartz.CGEventTapCreate( Quartz.kCGSessionEventTap, Quartz.kCGHeadInsertEventTap, Quartz.kCGEventTapOptionDefault, Quartz.CGEventMaskBit(Quartz.kCGEventKeyDown), keyboardTapCallback, None) runLoopSource = Quartz.CFMachPortCreateRunLoopSource(None, self.tap, 0) Quartz.CFRunLoopAddSource(Quartz.CFRunLoopGetCurrent(), runLoopSource, Quartz.kCFRunLoopDefaultMode) # Enable the tap Quartz.CGEventTapEnable(self.tap, True) Quartz.CFRunLoopRun()
def runEventsCapture(self): import AppKit, Quartz from AppKit import NSSystemDefined pool = AppKit.NSAutoreleasePool.alloc().init() self.runLoopRef = Quartz.CFRunLoopGetCurrent() while True: # https://developer.apple.com/library/mac/#documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html tap = Quartz.CGEventTapCreate( Quartz. kCGSessionEventTap, # Quartz.kCGSessionEventTap or kCGHIDEventTap Quartz. kCGHeadInsertEventTap, # Insert wherever, we do not filter Quartz. kCGEventTapOptionDefault, #Quartz.kCGEventTapOptionListenOnly, Quartz.CGEventMaskBit( NSSystemDefined), # NSSystemDefined for media keys self.eventTap, None) assert tap # Create a runloop source and add it to the current loop runLoopSource = Quartz.CFMachPortCreateRunLoopSource(None, tap, 0) Quartz.CFRunLoopAddSource(self.runLoopRef, runLoopSource, Quartz.kCFRunLoopDefaultMode) # Enable the tap Quartz.CGEventTapEnable(tap, True) try: # and run! This won't return until we exit or are terminated. Quartz.CFRunLoopRun() except Exception: # I got this one here once: # error: NSInternalInconsistencyException - Invalid parameter not satisfying: cgsEvent.type > 0 && cgsEvent.type <= kCGSLastEventType sys.excepthook(*sys.exc_info()) continue # rerun # this is a regular quit break del pool
def run(self): self._tap = Quartz.CGEventTapCreate( Quartz.kCGSessionEventTap, # Session level is enough for our needs Quartz.kCGHeadInsertEventTap, # Insert wherever, we do not filter # Active, to swallow the play/pause event is enough Quartz.kCGEventTapOptionDefault, # NSSystemDefined for media keys Quartz.CGEventMaskBit(NSSystemDefined), self._event_tap, None ) # the above can fail if self._tap is None: self._event.set() return self._loop = Quartz.CFRunLoopGetCurrent() # add an observer so we know when we can stop it # without a race condition self._observ = Quartz.CFRunLoopObserverCreate( None, Quartz.kCFRunLoopEntry, False, 0, self._loop_start, None) Quartz.CFRunLoopAddObserver( self._loop, self._observ, Quartz.kCFRunLoopCommonModes) # Create a runloop source and add it to the current loop self._runLoopSource = Quartz.CFMachPortCreateRunLoopSource( None, self._tap, 0) Quartz.CFRunLoopAddSource( self._loop, self._runLoopSource, Quartz.kCFRunLoopDefaultMode ) # Enable the tap Quartz.CGEventTapEnable(self._tap, True) # runrunrun Quartz.CFRunLoopRun()
def keyboard_tap_callback(proxy, type_, event, refcon): if type_ < 0 or type_ > 0x7fffffff: LOG.error('Unkown mac event') Quartz.CFRunLoopRun() return run_event_loop(ControllerApi.player) try: key_event = NSEvent.eventWithCGEvent_(event) except: LOG.info("mac event cast error") return event if key_event.subtype() == 8: key_code = (key_event.data1() & 0xFFFF0000) >> 16 key_state = (key_event.data1() & 0xFF00) >> 8 if key_code is 16 or key_code is 19 or key_code is 20: # 16 for play-pause, 19 for next, 20 for previous if key_state == NSKeyUp: if key_code is 19: player.play_next() elif key_code is 20: player.play_last() elif key_code is 16: player.play_or_pause() return None return event
import Quartz def MyFunction(p, t, e, c): print e tap = Quartz.CGEventTapCreate( Quartz.kCGHIDEventTap, Quartz.kCGHeadInsertEventTap, Quartz.kCGEventTapOptionListenOnly, Quartz.CGEventMaskBit(Quartz.kCGEventLeftMouseDown), MyFunction, None) runLoopSource = Quartz.CFMachPortCreateRunLoopSource(None, tap, 0) Quartz.CFRunLoopAddSource(Quartz.CFRunLoopGetCurrent(), runLoopSource, Quartz.kCFRunLoopDefaultMode) Quartz.CGEventTapEnable(tap, True) Quartz.CFRunLoopRun()