-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathkeydouble.c
213 lines (182 loc) · 5.62 KB
/
keydouble.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#define _BSD_SOURCE
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <X11/Xlibint.h>
#include <X11/extensions/XTest.h>
#include <X11/extensions/record.h>
#include <X11/keysym.h>
#define ARTIFICIAL_TIMEOUT 600
#define SLEEP_MICROSEC 100*1000
#define MAX_CODE 256
#define CODE_UNDEF -1
#define PAIR_SEP ":"
typedef enum {
false,
true
} bool;
void setup(void);
void loop(void);
void stop(int signum);
void evtcallback(XPointer priv, XRecordInterceptData *hook);
void die(const char *errstr, ...);
int deltamsec(struct timeval t1, struct timeval t2);
Display *ctldpy, *datdpy;
XRecordContext reccontext;
XRecordRange *recrange;
XRecordClientSpec reccspec;
/* maps a natural to an artificial keycode */
int natart[MAX_CODE];
bool running = true;
/* from libxnee */
typedef union {
unsigned char type;
xEvent event;
xResourceReq req;
xGenericReply reply;
xError error;
xConnSetupPrefix setup;
} XRecordDatum;
void setup(void)
{
int event, error, major, minor;
/*
We're gonna fetch two display objects; one, we'll be stealing events
from, and the other, we'll be sending events to. This prevents an
infinite loop.
*/
if (!(ctldpy = XOpenDisplay(NULL)) || !(datdpy = XOpenDisplay(NULL)))
die("cannot open display\n");
/*
We have to synchronize the control display to ensure that the
events we *send* get sent immediately; because we're not doing
anything but sending key events, it should not result in a
significant reduction in speed.
*/
XSynchronize(ctldpy, true);
/*
Now we have to fetch the XRecord context; some sanity checking,
first, then grab a context off of the 'from' display.
*/
if (!XTestQueryExtension(ctldpy, &event, &error, &major, &minor))
die("the xtest extension is not loaded\n");
if (!XRecordQueryVersion(ctldpy, &major, &minor))
die("the record extension is not loaded\n");
if (!(recrange = XRecordAllocRange()))
die("could not alloc the record range object\n");
recrange->device_events.first = KeyPress;
recrange->device_events.last = ButtonPress;
reccspec = XRecordAllClients;
if (!(reccontext = XRecordCreateContext(datdpy, 0, &reccspec, 1, &recrange, 1)))
die("could not create a record context");
/* Finally, start listening for events. */
if (!XRecordEnableContextAsync(datdpy, reccontext, evtcallback, NULL))
die("cannot enable record context\n");
}
void loop(void)
{
while (running) {
XRecordProcessReplies(datdpy);
usleep(SLEEP_MICROSEC);
}
}
void evtcallback(XPointer priv, XRecordInterceptData *hook)
{
if (hook->category != XRecordFromServer) {
XRecordFreeData(hook);
return;
}
XRecordDatum *data = (XRecordDatum *) hook->data;
static unsigned int numnat;
static bool natdown[MAX_CODE], keycomb[MAX_CODE];
static struct timeval startwait[MAX_CODE], endwait[MAX_CODE];
int code = data->event.u.u.detail;
int evttype = data->event.u.u.type;
if (evttype == KeyPress) {
/* a natural key was pressed */
if (!natdown[code] && natart[code] != CODE_UNDEF) {
natdown[code] = true;
numnat++;
gettimeofday(&startwait[code], NULL);
} else if (numnat > 0) {
int i;
for (i = 0; i < MAX_CODE; i++)
keycomb[i] = natdown[i];
}
} else if (evttype == KeyRelease) {
/* a natural key was released */
if (natart[code] != CODE_UNDEF) {
natdown[code] = false;
numnat--;
if (!keycomb[code]) {
gettimeofday(&endwait[code], NULL);
/* if the timeout wasn't reached since natural was pressed */
if (deltamsec(endwait[code], startwait[code]) < ARTIFICIAL_TIMEOUT ) {
/* we send key Press/Release events for the artificial keycode */
XTestFakeKeyEvent(ctldpy, natart[code], true, CurrentTime);
XTestFakeKeyEvent(ctldpy, natart[code], false, CurrentTime);
}
}
keycomb[code] = false;
}
} else if (evttype == ButtonPress && numnat > 0) {
int i;
for (i = 0; i < MAX_CODE; i++)
keycomb[i] = natdown[i];
}
XRecordFreeData(hook);
}
void die(const char *errstr, ...)
{
va_list ap;
va_start(ap, errstr);
vfprintf(stderr, errstr, ap);
va_end(ap);
exit(EXIT_FAILURE);
}
int deltamsec(struct timeval t1, struct timeval t2)
{
return (((t1.tv_sec - t2.tv_sec) * 1000000)
+ (t1.tv_usec - t2.tv_usec)) / 1000;
}
void stop(int signum)
{
running = false;
}
void addpair(char *na)
{
char *natural, *artificial;
int natcode, artcode;
if (!(natural = strtok(na, PAIR_SEP)) || !(artificial = strtok(NULL, PAIR_SEP)))
die("could not parse natart pair\n");
natcode = atoi(natural);
artcode = atoi(artificial);
natart[natcode] = artcode;
}
int main(int argc, char *argv[])
{
int i;
if (argc < 2)
die("usage: %s NAT:ART ...\n", argv[0]);
for (i = 0; i < MAX_CODE; i++)
natart[i] = CODE_UNDEF;
for (i = 1; i < argc; i++)
addpair(argv[i]);
signal(SIGINT, stop);
signal(SIGTERM, stop);
signal(SIGHUP, stop);
setup();
loop();
if(!XRecordDisableContext(ctldpy, reccontext))
die("could not disable record context\n");
XRecordFreeContext(ctldpy, reccontext);
XFlush(ctldpy);
XFree(recrange);
XCloseDisplay(datdpy);
XCloseDisplay(ctldpy);
return 0;
}