----pyrecord.pyx----
cdef extern from "X11/X.h":
ctypedef unsigned long Time
# declare the #define constants, don't have to provide their initial values
enum:
KeyPress=2
KeyRelease
ButtonPress
ButtonRelease
MotionNotify
FocusIn
FocusOut
cdef extern from "X11/Xproto.h":
# only need to declare the members you need to access
# and no nested ctypedef/cdef supported so far
ctypedef struct S1:
unsigned char type
unsigned char detail
ctypedef struct S2:
unsigned short rootX
unsigned short rootY
ctypedef union U:
S1 u
S2 keyButtonPointer
ctypedef struct xEvent:
U u
cdef extern from "X11/Xlib.h":
enum Bool:
False=0
True=1
ctypedef void *XPointer
ctypedef int Status
# opaque struct which you only use its pointer type
ctypedef struct Display
Display *XOpenDisplay(char* display_name)
int XCloseDisplay(Display *display)
int XFlush(Display* display)
int XFree (void *data)
int (*XSynchronize(Display *display, Bool onoff)) ()
ctypedef unsigned int XID
ctypedef XID KeySym
ctypedef unsigned char KeyCode
KeyCode XKeysymToKeycode(Display* display, KeySym keysym)
KeySym XStringToKeysym(char* string)
KeySym XKeycodeToKeysym(Display *display, KeyCode keycode, int index)
char *XKeysymToString(KeySym keysym)
cdef extern from "X11/extensions/record.h":
enum:
XRecordCurrentClients=1
XRecordFutureClients
XRecordAllClients
enum:
XRecordFromServer=0
XRecordFromClient
ctypedef struct XRecordRange8:
unsigned char first
unsigned char last
ctypedef struct XRecordRange:
XRecordRange8 device_events
ctypedef struct XRecordInterceptData:
Time server_time
int category
unsigned char *data
ctypedef unsigned long XRecordClientSpec
ctypedef unsigned long XRecordContext
XRecordRange *XRecordAllocRange()
XRecordContext XRecordCreateContext(Display*, int, XRecordClientSpec*, int, XRecordRange**, int)
# typedef function pointer
ctypedef void (*XRecordInterceptProc) (XPointer, XRecordInterceptData*)
Status XRecordEnableContextAsync(Display*, XRecordContext, XRecordInterceptProc, XPointer)
void XRecordProcessReplies(Display*)
Status XRecordDisableContext(Display*, XRecordContext)
Status XRecordFreeContext(Display*, XRecordContext)
void XRecordFreeData(XRecordInterceptData*)
cdef void _event_callback_wrapper (XPointer py_fn, XRecordInterceptData *hook):
cdef xEvent *data
# casting
data = <xEvent*> hook.data
if hook.category != XRecordFromServer:
XRecordFreeData (hook)
return
type = data.u.u.type
event_spec = {'type':type}
if type in (KeyPress, KeyRelease):
event_spec['keysym'] = XKeysymToString(XKeycodeToKeysym(g_disp, data.u.u.detail, 0))
elif type in (ButtonPress, ButtonRelease):
event_spec['button'] = data.u.u.detail
elif type == MotionNotify:
event_spec['rootX'] = data.u.keyButtonPointer.rootX
event_spec['rootY'] = data.u.keyButtonPointer.rootY
# delegate to python callback method
(<object>py_fn) (event_spec)
cdef Display *g_disp
cdef class XRecord:
cdef Display *data_disp
cdef Display *ctrl_disp
cdef XRecordRange *rr
cdef XRecordClientSpec rcs
cdef XRecordContext rc
# __new__ is also effective for constructor
def __init__(self):
self.ctrl_disp = XOpenDisplay (NULL)
self.data_disp = XOpenDisplay (NULL)
global g_disp
g_disp = self.ctrl_disp
XSynchronize(self.ctrl_disp, True)
self.rr = XRecordAllocRange ()
# use '.' instead of '->' to access the data member
self.rr.device_events.first = KeyPress
self.rr.device_events.last = MotionNotify
self.r
cs = XRecordAllClients
self.rc = XRecordCreateContext (self.ctrl_disp, 0, &(self.rcs), 1, &(self.rr), 1)
def set_callback(self, cb):
XRecordEnableContextAsync (self.data_disp, self.rc, _event_callback_wrapper, <void*>cb) #casting
# __del__ does not work for pyrex extension type
def __dealloc__(self):
XRecordDisableContext (self.ctrl_disp, self.rc)
XRecordFreeContext (self.ctrl_disp, self.rc)
XFree (self.rr)
XCloseDisplay (self.data_disp)
XCloseDisplay (self.ctrl_disp)
def replies(self):
XRecordProcessReplies (self.data_disp)
----test.py----
from pyrecord import *
stop = 0
def test_cb(event):
if event['type'] in (2, 3):
print event['type'], event['keysym']
if event['keysym'] == 'Escape':
global stop
stop = 1
elif event['type'] in (4, 5):
print event['type'], event['button']
elif event['type'] == 6:
print event['rootX'], event['rootY']
r=XRecord()
r.set_callback(test_cb)
while not stop:
r.replies()
del r