1 /* X11::GUITest ($Id: record.c 231 2014-01-11 14:26:57Z ctrondlp $)
2  *
3  * Copyright (c) 2003-2014  Dennis K. Paulsen, All Rights Reserved.
4  * Email: ctrondlp@cpan.org
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses>.
18  *
19  */
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <signal.h>
24 #include <unistd.h>
25 #include <libintl.h>
26 #include <sys/time.h>
27 #include <X11/X.h>
28 #include <X11/XKBlib.h>
29 #include <X11/Xproto.h>
30 #include <X11/extensions/record.h>
31 #include <X11/keysym.h>
32 #include <X11/Xutil.h>
33 #include <X11/Intrinsic.h>
34 #include <X11/StringDefs.h>
35 #include "record_event.h"
36 #include "record.h"
37 #include "Common.h"
38 
39 static BOOL shouldExit = FALSE;
40 static struct timeval lastTime = {0};
41 static struct timeval currentTime = {0};
42 static void (*HandleEvent)(struct record_event ev);
43 static XRecordContext rcon;
44 static Display *otherDisp = NULL;
45 static Display *disp = NULL;
46 
47 
RecordEvents(void (* handleEvent)(struct record_event ev))48 int RecordEvents(void (*handleEvent)(struct record_event ev))
49 {
50 	XRecordClientSpec rcspec = {0};
51 	XRecordRange *xrr = NULL;
52   	int major = 0, minor = 0;
53 
54 	HandleEvent = handleEvent; // Register it
55 
56 	signal(SIGINT, sigint_handler);
57 
58 	SetLastTime();
59 
60 	disp = XOpenDisplay(NULL);
61  	if (disp == NULL) {
62 		fprintf(stderr, _("Unable to open display connection.\n"));
63 		return 1;
64 	}
65 	XSynchronize(disp, False);
66 
67 	// Ensure extension available
68 	if (!XRecordQueryVersion(disp, &major, &minor)) {
69 		fprintf(stderr, _("The record extension is unavailable.\n"));
70 		return 1;
71 	}
72 
73 	xrr = XRecordAllocRange();
74 	if (xrr == NULL) {
75 		fprintf(stderr, _("Range allocation failed.\n"));
76 		return 1;
77 	}
78 
79 	xrr->device_events.first = KeyPress;
80 	xrr->device_events.last = MotionNotify;
81  	rcspec = XRecordAllClients;
82 
83 	rcon = XRecordCreateContext(disp, 0, &rcspec, 1, &xrr, 1);
84 	if (!rcon) {
85 		fprintf(stderr, _("Unable to create context.\n"));
86 		return 1;
87 	}
88 
89 	otherDisp = XOpenDisplay(NULL);
90 	if (otherDisp == NULL) {
91 		fprintf(stderr, _("Unable to open other display connection.\n"));
92 		return 1;
93 	}
94 
95 	// Clean out X events in progress
96 	XFlush(disp);
97 	XFlush(otherDisp);
98 
99 	// Record...
100 	if (!XRecordEnableContext(otherDisp, rcon, EventCallback, (XPointer)disp)) {
101 		fprintf(stderr, _("Enable context failed\n"));
102 		return 1;
103 	}
104 	// ...until StopRecording() is called.
105 
106 	XFree(xrr);
107 	return 0;
108 }
109 
StopRecording(void)110 void StopRecording(void)
111 {
112 	shouldExit = TRUE;
113 	XRecordDisableContext(disp, rcon);
114 	XRecordFreeContext(disp, rcon);
115 	//XCloseDisplay(otherDisp); // Note: N/A, blocks indefinitely
116 	XCloseDisplay(disp);
117 }
118 
SetLastTime(void)119 void SetLastTime(void)
120 {
121 	if (gettimeofday(&lastTime, NULL) != 0) {
122 		fprintf(stderr, _("unable to get time\n"));
123 	}
124 }
125 
SetCurrentTime(void)126 void SetCurrentTime(void)
127 {
128 	if (gettimeofday(&currentTime, NULL) != 0) {
129   		fprintf(stderr, _("unable to get time\n"));
130 	}
131 }
132 
GetDelay(void)133 long GetDelay(void)
134 {
135 	long secDiff = 0;
136  	long usecDiff = 0;
137 	long final = 0;
138 
139 	SetCurrentTime();
140 	/* Get delay between the previous event and this one */
141 	secDiff = (currentTime.tv_sec - lastTime.tv_sec);
142 	usecDiff = ((currentTime.tv_usec - lastTime.tv_usec) / 1000);
143 	final = ((secDiff * 1000) + usecDiff);
144 	SetLastTime();
145 
146 	return final;
147 }
148 
EventCallback(XPointer p,XRecordInterceptData * idata)149 void EventCallback(XPointer p, XRecordInterceptData *idata)
150 {
151 	if (shouldExit) {
152 		return;
153 	}
154 
155 	if (XRecordFromServer == idata->category) {
156 		Display *disp = (Display *)p;
157   		xEvent *xev = (xEvent *)idata->data;
158 		int type = xev->u.u.type;
159 		int keyPress = 0;
160 		struct record_event re = {0};
161 
162 		re.delay = GetDelay();
163 		re.type = NOTYPE;
164 		re.state = NOSTATE;
165 		re.dataname = NULL;
166 		re.data = 0;
167 
168 		switch (type) {
169 		case ButtonPress:
170 			re.type = MOUSEBUTTON;
171 			re.state = DOWN;
172 			re.data = xev->u.u.detail;
173 			break;
174    		case ButtonRelease:
175 			re.type = MOUSEBUTTON;
176 			re.state = UP;
177 			re.data = xev->u.u.detail;
178 			break;
179 		case KeyPress:
180 			keyPress = 1;
181 	    case KeyRelease:
182 	 		{
183 				//printf("key code: %d\n", xev->u.u.detail);
184 			    KeyCode kc = xev->u.u.detail;
185 			    KeySym ks = XkbKeycodeToKeysym(disp, kc, 0, 0);
186 				re.dataname = XKeysymToString(ks);
187 				re.data = ks;
188 				re.type = KEY;
189 				re.state = (keyPress == 1) ? DOWN : UP;
190 			}
191 			break;
192    		case MotionNotify:
193 			{
194 				re.type = MOUSEMOVE;
195 				re.posX = xev->u.keyButtonPointer.rootX;
196 			    re.posY = xev->u.keyButtonPointer.rootY;
197 			}
198 			break;
199    		case EnterNotify:
200 	 	case LeaveNotify:
201    		default:
202 			break;
203 		}
204 		////
205 		HandleEvent(re);
206 		////
207 	}
208 	if (idata != NULL) {
209 		XRecordFreeData(idata);
210 	}
211 }
212 
sigint_handler(int sig)213 void sigint_handler(int sig)
214 {
215 	StopRecording();
216 }
217 
218