Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Something like pyHook on OS X

I am actually working with pyHook, but I'd like to write my program for OS X too. If someone know such a module ... I've been looking on the internet for a while, but nothing really relevant.

-> The idea is to be able to record keystrokes outside the python app. My application is a community statistics builder, so it would be great to have statistics from OS X too.

Thanks in advance ;)

Edit: PyHook : Record keystrokes and other things outside the python app http://sourceforge.net/apps/mediawiki/pyhook/index.php?title=PyHook_Tutorial http://pyhook.sourceforge.net/doc_1.5.0/ http://sourceforge.net/apps/mediawiki/pyhook/index.php?title=Main_Page

like image 912
Carto_ Avatar asked Jan 17 '23 10:01

Carto_


1 Answers

As far as I know, there is no Python library for this, so you're going to be calling native APIs. The good news is that PyObjC (which comes with the built-in Python on recent OS releases) often makes that easy.

There are two major options. For either of these to work, your app has to have a Cocoa/CoreFoundation runloop (just as in Windows, a lot of things require you to be a "Windows GUI executable" rather than a "command line executable"), which I won't explain how to do here. (Find a good tutorial for building GUI apps in Python, if you don't know how, because that's the simplest way.)

The easy option is the Cocoa global event monitor API. However, it has some major limitations. You only get events that are going to another app--which means media keys, global hotkeys, and keys that are for whatever reason ignored will not show up. Also, you need to be "trusted for accessibility". (The simplest way to do that is to ask the user to turn it on globally, in the Universal Access panel of System Preferences.)

The hard option is the Quartz event tap API. It's a lot more flexible, and it only requires exactly the appropriate rights (which, depending on the settings you use, may include being trusted for accessibility and/or running as root), and it's a lot more powerful, but it takes a lot more work to get started, and it's possible to screw up your system if you get it wrong (e.g., by eating all keystrokes and mouse events so they never get to the OS and you can't reboot except with the power button).

For references on all of the relevant functions, see https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/nsevent_Class/Reference/Reference.html (for NSEvent) and https://developer.apple.com/library/mac/#documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html (for Quartz events). A bit of googling should turn up lots of sample code out there in Objective C (for NSEvent) or C (for CGEventTap), but little or nothing in Python, so I'll show some little fragments that illustrate how you'd port the samples to Python:

import Cocoa
def evthandler(event):
  pass # this is where you do stuff; see NSEvent documentation for event
observer = Cocoa.NSEvent.addGlobalMonitorForEventsMatchingMask_handler_(NSKeyDown, evthandler)
# when you're done
Cocoa.NSEvent.removeMonitor_(observer)

import Quartz
def evthandler(proxy, type, event, refcon):
    pass # Here's where you do your stuff; see CGEventTapCallback
    return event
source = Quartz.CGEventSourceCreate(Quartz.kCGEventSourceStateHIDSystemState)
tap = Quartz.CGEventTapCreate(Quartz.kCGSessionEventTap,
                              Quartz.kCGHeadInsertEventTap,
                              Quartz.kCGEventTapOptionListenOnly,
                              (Quartz.CGEventMaskBit(Quartz.kCGEventKeyDown) |
                               Quartz.CGEventMaskBit(Quartz.kCGEventKeyUp)),
                              handler,
                              refcon)

Another option, at about the same level as Quartz events, is Carbon events (starting with InstallEventHandler). However, Carbon is obsolete, and on top of that, it's harder to get at from Python, so unless you have some specific reason to go this way, don't.

There are some other ways to get to the same point—e.g., use DYLD_INSERT_LIBRARIES or SIMBL to get some code inserted into each app—but I can't think of anything else that can be done in pure Python.

like image 137
abarnert Avatar answered Jan 21 '23 15:01

abarnert