]>
Commit | Line | Data |
---|---|---|
1 | #include <X11/Xlib.h> | |
2 | #include <math.h> | |
3 | #include <X11/extensions/XTest.h> | |
4 | #include <stdio.h> | |
5 | #include <signal.h> | |
6 | #include <sys/time.h> | |
7 | #include <time.h> | |
8 | ||
9 | #define STATUS_LEFT 0x01 | |
10 | #define STATUS_RIGHT 0x02 | |
11 | #define STATUS_UP 0x04 | |
12 | #define STATUS_DOWN 0x08 | |
13 | #define STATUS_MOVING 0xF | |
14 | #define STATUS_TAPJUMP 0x100 | |
15 | #define STATUS_RUNNING 0x80000000 | |
16 | ||
17 | #define MIN(a,b) ({int __a = (a), __b = (b); (__a < __b) ? __a : __b; }) | |
18 | #define MAX(a,b) ({int __a = (a), __b = (b); (__a > __b) ? __a : __b; }) | |
19 | ||
20 | unsigned long long now() | |
21 | { | |
22 | struct timeval tv; | |
23 | gettimeofday(&tv, NULL); | |
24 | return tv.tv_sec*1000000 + tv.tv_usec; | |
25 | } | |
26 | ||
27 | int main() | |
28 | { | |
29 | int e; | |
30 | XEvent ev; | |
31 | Display *dpy; | |
32 | int status = 0, dmy; | |
33 | char key = 0, lastkey = 0; | |
34 | unsigned long long lasttime = 0; | |
35 | int repeats = 1; | |
36 | int starttime = 0; | |
37 | ||
38 | dpy = XOpenDisplay(NULL); | |
39 | if (!dpy) | |
40 | { | |
41 | printf("Display open failed; bailing out\n"); | |
42 | exit(1); | |
43 | } | |
44 | ||
45 | if (!XTestQueryExtension(dpy, &dmy, &dmy, &dmy, &dmy)) | |
46 | { | |
47 | printf("XTest NOT supported\n"); | |
48 | exit(1); | |
49 | } | |
50 | ||
51 | printf("Grabbing keyboard...\n"); | |
52 | XGrabKey(dpy, XKeysymToKeycode(dpy, XStringToKeysym("grave")), Mod1Mask, RootWindow(dpy, DefaultScreen(dpy)), True, GrabModeAsync, GrabModeAsync); | |
53 | ||
54 | while (1) | |
55 | { | |
56 | while (XCheckMaskEvent(dpy, 0xFFFFFFFF, &ev)) | |
57 | { | |
58 | switch (ev.type) | |
59 | { | |
60 | case KeyPress: | |
61 | case KeyRelease: | |
62 | { | |
63 | XKeyEvent *ke = (XKeyEvent*)&ev; | |
64 | char *keystr = XKeysymToString(XKeycodeToKeysym(dpy, ke->keycode, 0)); | |
65 | //printf("Key %s: %s\n", ev.type == KeyPress ? "PRESS " : "RELEASE", keystr); | |
66 | ||
67 | if (!strcmp(keystr, "grave")) { | |
68 | if (ev.type == KeyPress) | |
69 | { | |
70 | XGrabKey(dpy, XKeysymToKeycode(dpy, XStringToKeysym("grave")), AnyModifier, RootWindow(dpy, DefaultScreen(dpy)), True, GrabModeAsync, GrabModeAsync); | |
71 | XAutoRepeatOff(dpy); | |
72 | status |= STATUS_RUNNING; | |
73 | } else { | |
74 | XUngrabKey(dpy, XKeysymToKeycode(dpy, XStringToKeysym("grave")), AnyModifier, RootWindow(dpy, DefaultScreen(dpy))); | |
75 | XGrabKey(dpy, XKeysymToKeycode(dpy, XStringToKeysym("grave")), Mod1Mask, RootWindow(dpy, DefaultScreen(dpy)), True, GrabModeAsync, GrabModeAsync); | |
76 | XAutoRepeatOn(dpy); | |
77 | status &= ~STATUS_RUNNING; | |
78 | } | |
79 | } else if (!strcmp(keystr, "u")) | |
80 | XTestFakeButtonEvent(dpy, 1, ev.type == KeyPress, 0); | |
81 | else if (!strcmp(keystr, "o")) | |
82 | XTestFakeButtonEvent(dpy, 3, ev.type == KeyPress, 0); | |
83 | else if (!strcmp(keystr, "p")) | |
84 | XTestFakeButtonEvent(dpy, 2, ev.type == KeyPress, 0); | |
85 | else if (!strcmp(keystr, "apostrophe") || !strcmp(keystr, "semicolon")) { | |
86 | if (ev.type == KeyPress) | |
87 | status |= STATUS_TAPJUMP; | |
88 | else | |
89 | status &= ~STATUS_TAPJUMP; | |
90 | repeats = 1; | |
91 | } else if (!strcmp(keystr, "j")) { | |
92 | if (ev.type == KeyPress) | |
93 | { | |
94 | status |= STATUS_LEFT; | |
95 | if ((lastkey == 'j') && ((now() - lasttime) < 250000) && (status & STATUS_TAPJUMP)) | |
96 | XTestFakeRelativeMotionEvent(dpy, -100.0 * pow(repeats++, 0.8), 0, 0); | |
97 | } else | |
98 | status &= ~STATUS_LEFT; | |
99 | } else if (!strcmp(keystr, "l")) { | |
100 | if (ev.type == KeyPress) | |
101 | { | |
102 | status |= STATUS_RIGHT; | |
103 | if ((lastkey == 'l') && ((now() - lasttime) < 250000) && (status & STATUS_TAPJUMP)) | |
104 | XTestFakeRelativeMotionEvent(dpy, 100.0 * pow(repeats++, 0.8), 0, 0); | |
105 | } else | |
106 | status &= ~STATUS_RIGHT; | |
107 | } else if (!strcmp(keystr, "i")) { | |
108 | if (ev.type == KeyPress) | |
109 | { | |
110 | status |= STATUS_UP; | |
111 | if ((lastkey == 'i') && ((now() - lasttime) < 250000) && (status & STATUS_TAPJUMP)) | |
112 | XTestFakeRelativeMotionEvent(dpy, 0, -100.0 * pow(repeats++, 0.8), 0); | |
113 | } else | |
114 | status &= ~STATUS_UP; | |
115 | } else if (!strcmp(keystr, "k")) { | |
116 | if (ev.type == KeyPress) | |
117 | { | |
118 | status |= STATUS_DOWN; | |
119 | if (lastkey == 'k' && (now() - lasttime) < 250000 && (status & STATUS_TAPJUMP)) | |
120 | XTestFakeRelativeMotionEvent(dpy, 0, 100.0 * pow(repeats++, 0.8), 0); | |
121 | } else | |
122 | status &= ~STATUS_DOWN; | |
123 | } | |
124 | if (strlen(keystr) == 1 && ev.type == KeyPress) | |
125 | { | |
126 | lastkey = keystr[0]; | |
127 | lasttime = now(); | |
128 | } else if (ev.type == KeyPress) { | |
129 | lastkey = 0; | |
130 | repeats = 0; | |
131 | } | |
132 | break; | |
133 | } | |
134 | default: | |
135 | printf("NFI what %d is\n", ev.type); | |
136 | } | |
137 | } | |
138 | if (!(status & STATUS_RUNNING)) | |
139 | status = 0; | |
140 | if (!(status & STATUS_MOVING)) // if we haven't anything to do, don't sit and suck up cycles | |
141 | { | |
142 | XPeekEvent(dpy, &ev); | |
143 | starttime = now(); | |
144 | } | |
145 | usleep(20000 - MIN(MAX(pow(13 * (now() - starttime), 0.6), 0), 18000)); | |
146 | if (now() - lasttime > 250000) | |
147 | repeats = 1; | |
148 | if (status & STATUS_LEFT) | |
149 | XTestFakeRelativeMotionEvent(dpy, -2, 0, 0); | |
150 | if (status & STATUS_RIGHT) | |
151 | XTestFakeRelativeMotionEvent(dpy, 2, 0, 0); | |
152 | if (status & STATUS_UP) | |
153 | XTestFakeRelativeMotionEvent(dpy, 0, -2, 0); | |
154 | if (status & STATUS_DOWN) | |
155 | XTestFakeRelativeMotionEvent(dpy, 0, 2, 0); | |
156 | } | |
157 | ||
158 | exit(0); | |
159 | } |