Record module for python by pyrex

----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

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

To submit your comment, click the image below where it asks you to...
Clickcha - The One-Click Captcha