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(¤tTime, 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