#include #include #include #include #include #include #include #include #include #include #include /* The typical communication model for a recording client is to open two connections to the server and use one for RC control and the other for reading protocol data. https://www.x.org/releases/X11R7.6/doc/recordproto/record.html#Record_Client_Connections */ Display *data_disp = NULL; Display *ctrl_disp = NULL; int stop = 0; void event_callback (XPointer, XRecordInterceptData*); int main (int argc, char **argv) { ctrl_disp = XOpenDisplay (NULL); data_disp = XOpenDisplay (NULL); if (!ctrl_disp || !data_disp) { fprintf (stderr, "Error to open local display!\n"); exit (1); } /* without this the record context is sent before we have the display */ /* there must be a better solution to this */ XSynchronize(ctrl_disp,True); int major, minor; if (!XRecordQueryVersion (ctrl_disp, &major, &minor)) { fprintf (stderr, "RECORD extension not supported on this X server!\n"); exit (2); } printf ("RECORD extension for local server is version is %d.%d\n", major, minor); XRecordRange *rr; XRecordClientSpec rcs; XRecordContext rc; rr = XRecordAllocRange (); if (!rr) { fprintf (stderr, "Could not alloc record range object!\n"); exit (3); } rr->core_requests.first = X_GrabPointer; rr->core_requests.last = X_UngrabKey; rcs = XRecordAllClients; rc = XRecordCreateContext (ctrl_disp, 0, &rcs, 1, &rr, 1); if (!rc) { fprintf (stderr, "Could not create a record context!\n"); exit (4); } if (!XRecordEnableContext (data_disp, rc, event_callback, NULL)) { fprintf (stderr, "Cound not enable the record context!\n"); exit (5); } while (stop != 1) { XRecordProcessReplies (data_disp); } XRecordDisableContext (ctrl_disp, rc); XRecordFreeContext (ctrl_disp, rc); XFree (rr); XCloseDisplay (data_disp); XCloseDisplay (ctrl_disp); return 0; } void event_callback(XPointer priv, XRecordInterceptData *hook) { if (hook->category != XRecordFromServer && hook->category != XRecordFromClient) { XRecordFreeData(hook); return; } xReq *request = (xReq *)hook->data; switch (request->reqType) { case X_GrabPointer: printf("GrabPointer request detected\n"); break; case X_UngrabPointer: printf("UngrabPointer request detected\n"); break; /* these occur way too frequently on my system */ /* case X_GrabButton: */ /* printf("GrabButton request detected\n"); */ /* break; */ /* case X_UngrabButton: */ /* printf("UngrabButton request detected\n"); */ /* break; */ case X_ChangeActivePointerGrab: printf("ChangeActivePointerGrab request detected\n"); break; case X_GrabKeyboard: { xGrabKeyboardReq *grab_req = (xGrabKeyboardReq *)hook->data; printf("GrabKeyboard request detected for Window ID: %i\n", grab_req->grabWindow); break; } case X_UngrabKeyboard: printf("UngrabKeyboard request detected\n"); break; case X_GrabKey: printf("GrabKey request detected\n"); break; case X_UngrabKey: printf("UngrabKey request detected\n"); break; default: break; } XRecordFreeData(hook); }