/* X11::GUITest ($Id: record.c 221 2013-02-01 18:31:48Z pecastro $)
*
* Copyright (c) 2003-2011 Dennis K. Paulsen, All Rights Reserved.
* Email: ctrondlp@cpan.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses>.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <libintl.h>
#include <sys/time.h>
#include <X11/X.h>
#include <X11/XKBlib.h>
#include <X11/Xproto.h>
#include <X11/extensions/record.h>
#include <X11/keysym.h>
#include <X11/Xutil.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include "record_event.h"
#include "record.h"
#include "Common.h"
static BOOL shouldExit = FALSE;
static struct timeval lastTime = {0};
static struct timeval currentTime = {0};
static void (*HandleEvent)(struct record_event ev);
static XRecordContext rcon;
static Display *otherDisp = NULL;
static Display *disp = NULL;
int RecordEvents(void (*handleEvent)(struct record_event ev))
{
XRecordClientSpec rcspec = {0};
XRecordRange *xrr = NULL;
int major = 0, minor = 0;
HandleEvent = handleEvent; // Register it
signal(SIGINT, sigint_handler);
SetLastTime();
disp = XOpenDisplay(NULL);
if (disp == NULL) {
fprintf(stderr, _("Unable to open display connection.\n"));
return 1;
}
XSynchronize(disp, False);
// Ensure extension available
if (!XRecordQueryVersion(disp, &major, &minor)) {
fprintf(stderr, _("The record extension is unavailable.\n"));
return 1;
}
xrr = XRecordAllocRange();
if (xrr == NULL) {
fprintf(stderr, _("Range allocation failed.\n"));
return 1;
}
xrr->device_events.first = KeyPress;
xrr->device_events.last = MotionNotify;
rcspec = XRecordAllClients;
rcon = XRecordCreateContext(disp, 0, &rcspec, 1, &xrr, 1);
if (!rcon) {
fprintf(stderr, _("Unable to create context.\n"));
return 1;
}
otherDisp = XOpenDisplay(NULL);
if (otherDisp == NULL) {
fprintf(stderr, _("Unable to open other display connection.\n"));
return 1;
}
// Clean out X events in progress
XFlush(disp);
XFlush(otherDisp);
// Record...
if (!XRecordEnableContext(otherDisp, rcon, EventCallback, (XPointer)disp)) {
fprintf(stderr, _("Enable context failed\n"));
return 1;
}
// ...until StopRecording() is called.
XFree(xrr);
return 0;
}
void StopRecording(void)
{
shouldExit = TRUE;
XRecordDisableContext(disp, rcon);
XRecordFreeContext(disp, rcon);
//XCloseDisplay(otherDisp); // Note: N/A, blocks indefinitely
XCloseDisplay(disp);
}
void SetLastTime(void)
{
if (gettimeofday(&lastTime, NULL) != 0) {
fprintf(stderr, _("unable to get time\n"));
}
}
void SetCurrentTime(void)
{
if (gettimeofday(¤tTime, NULL) != 0) {
fprintf(stderr, _("unable to get time\n"));
}
}
long GetDelay(void)
{
long secDiff = 0;
long usecDiff = 0;
long final = 0;
SetCurrentTime();
/* Get delay between the previous event and this one */
secDiff = (currentTime.tv_sec - lastTime.tv_sec);
usecDiff = ((currentTime.tv_usec - lastTime.tv_usec) / 1000);
final = ((secDiff * 1000) + usecDiff);
SetLastTime();
return final;
}
void EventCallback(XPointer p, XRecordInterceptData *idata)
{
if (shouldExit) {
return;
}
if (XRecordFromServer == idata->category) {
Display *disp = (Display *)p;
xEvent *xev = (xEvent *)idata->data;
int type = xev->u.u.type;
int keyPress = 0;
struct record_event re = {0};
re.delay = GetDelay();
re.type = NOTYPE;
re.state = NOSTATE;
re.dataname = NULL;
re.data = 0;
switch (type) {
case ButtonPress:
re.type = MOUSEBUTTON;
re.state = DOWN;
re.data = xev->u.u.detail;
break;
case ButtonRelease:
re.type = MOUSEBUTTON;
re.state = UP;
re.data = xev->u.u.detail;
break;
case KeyPress:
keyPress = 1;
case KeyRelease:
{
//printf("key code: %d\n", xev->u.u.detail);
KeyCode kc = xev->u.u.detail;
KeySym ks = XkbKeycodeToKeysym(disp, kc, 0, 0);
re.dataname = XKeysymToString(ks);
re.data = ks;
re.type = KEY;
re.state = (keyPress == 1) ? DOWN : UP;
}
break;
case MotionNotify:
{
re.type = MOUSEMOVE;
re.posX = xev->u.keyButtonPointer.rootX;
re.posY = xev->u.keyButtonPointer.rootY;
}
break;
case EnterNotify:
case LeaveNotify:
default:
break;
}
////
HandleEvent(re);
////
}
if (idata != NULL) {
XRecordFreeData(idata);
}
}
void sigint_handler(int sig)
{
StopRecording();
}