Mercurial > hg > index.fcgi > keynav-9pba
comparison keynav.c @ 0:ab8a496afb67
initial hg commit of my version of keynav
author | paulo@thepaulopc |
---|---|
date | Tue, 08 Sep 2009 22:45:57 -0700 |
parents | |
children | 0d5dc7c29c78 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:7d47d1446018 |
---|---|
1 /* | |
2 * Visual user-directed binary search for something to point your mouse at. | |
3 */ | |
4 | |
5 #include "config.h" | |
6 | |
7 #include <stdio.h> | |
8 #include <stdlib.h> | |
9 #include <string.h> | |
10 | |
11 #include <X11/Xlib.h> | |
12 #include <X11/Xresource.h> | |
13 #include <X11/Xutil.h> | |
14 #include <X11/extensions/shape.h> | |
15 #include <X11/extensions/XTest.h> | |
16 | |
17 Display *dpy; | |
18 Window root; | |
19 XWindowAttributes attr; | |
20 int drag = 0; | |
21 int allmoves = 0; | |
22 int allhits = 0; | |
23 int allclicks = 0; | |
24 int ninegrid_default = 0; | |
25 int ninegrid_n_switch = 0; | |
26 int ninegrid = 0; | |
27 | |
28 struct /* undo stack */ | |
29 { | |
30 int x[N_UNDO]; | |
31 int y[N_UNDO]; | |
32 int w[N_UNDO]; | |
33 int h[N_UNDO]; | |
34 int ninegrid[N_UNDO]; | |
35 } u; | |
36 | |
37 void toggleninegrid() { | |
38 if (ninegrid) | |
39 ninegrid = 0; | |
40 else | |
41 ninegrid = 1; | |
42 } | |
43 | |
44 void stats(int hits, int moves) { | |
45 allhits += hits; | |
46 allmoves += moves; | |
47 allclicks++; | |
48 printf("hits=%d, moves=%d \n", hits, moves); | |
49 printf("total hits=%d, total moves=%d, total clicks=%d, avg. hit/click=%.2f, avg. moves/click=%.2f \n", allhits, allmoves, allclicks, (float)allhits/(float)allclicks, (float)allmoves/(float)allclicks); | |
50 fflush(stdout); fflush(stderr); // force buffers to write out | |
51 } | |
52 | |
53 void grab(char *keyname, int mods) { | |
54 int key; | |
55 | |
56 key = XKeysymToKeycode(dpy, XStringToKeysym(keyname)); | |
57 XGrabKey(dpy, key, mods, root, False, | |
58 GrabModeAsync, GrabModeAsync); | |
59 XGrabKey(dpy, key, mods | CAPSLOCKMASK, root, False, | |
60 GrabModeAsync, GrabModeAsync); | |
61 XGrabKey(dpy, key, mods | NUMLOCKMASK, root, False, | |
62 GrabModeAsync, GrabModeAsync); | |
63 XGrabKey(dpy, key, mods | CAPSLOCKMASK | NUMLOCKMASK, root, False, | |
64 GrabModeAsync, GrabModeAsync); | |
65 } | |
66 | |
67 void ungrab(char *keyname, int mod) { | |
68 int key; | |
69 | |
70 key = XKeysymToKeycode(dpy, XStringToKeysym(keyname)); | |
71 XUngrabKey(dpy, key, mod, root); | |
72 XUngrabKey(dpy, key, mod | CAPSLOCKMASK, root); | |
73 XUngrabKey(dpy, key, mod | NUMLOCKMASK, root); | |
74 XUngrabKey(dpy, key, mod | CAPSLOCKMASK | NUMLOCKMASK, root); | |
75 } | |
76 | |
77 void buttondown(int i) { | |
78 XTestFakeButtonEvent(dpy, i, True, 50); | |
79 } | |
80 | |
81 void buttonup(int i) { | |
82 XTestFakeButtonEvent(dpy, i, False, 50); | |
83 } | |
84 | |
85 void warppointer(int x, int y, int w, int h) { | |
86 XWarpPointer(dpy, None, root, 0, 0, 0, 0, x + w/2, y + h/2); | |
87 } | |
88 | |
89 /* operations for undo */ | |
90 void undo_stack_push(int x, int y, int w, int h) { | |
91 int i; | |
92 | |
93 if (x >= 0 && y >= 0 && w > 0 && h > 0) { | |
94 fprintf(stderr,"undo_stack_push success: @(%d,%d) #(%d,%d)\n", x, y, w, h); | |
95 for (i=N_UNDO-1; i>0; i--) { | |
96 u.x[i] = u.x[i-1]; | |
97 u.y[i] = u.y[i-1]; | |
98 u.w[i] = u.w[i-1]; | |
99 u.h[i] = u.h[i-1]; | |
100 u.ninegrid[i] = u.ninegrid[i-1]; | |
101 } | |
102 u.x[0] = x; | |
103 u.y[0] = y; | |
104 u.w[0] = w; | |
105 u.h[0] = h; | |
106 u.ninegrid[0] = ninegrid; | |
107 } | |
108 } | |
109 | |
110 int undo_stack_pop(int *x, int *y, int *w, int *h, int *ninegrid) { | |
111 int i; | |
112 int ret = 0; | |
113 | |
114 int rx = u.x[0]; | |
115 int ry = u.y[0]; | |
116 int rw = u.w[0]; | |
117 int rh = u.h[0]; | |
118 int rninegrid = u.ninegrid[0]; | |
119 | |
120 if (rx >= 0 && ry >= 0 && rw > 0 && rh > 0) { | |
121 fprintf(stderr,"undo_stack_pop success: @(%d,%d) #(%d,%d)\n", *x, *y, *w, *h); | |
122 ret = 1; // return success | |
123 for (i=0; i<N_UNDO-1; i++) { | |
124 u.x[i] = u.x[i+1]; | |
125 u.y[i] = u.y[i+1]; | |
126 u.w[i] = u.w[i+1]; | |
127 u.h[i] = u.h[i+1]; | |
128 u.ninegrid[i] = u.ninegrid[i+1]; | |
129 } | |
130 u.x[N_UNDO-1] = -1; | |
131 u.y[N_UNDO-1] = -1; | |
132 u.w[N_UNDO-1] = -1; | |
133 u.h[N_UNDO-1] = -1; | |
134 u.ninegrid[N_UNDO-1] = -1; | |
135 *x = rx; | |
136 *y = ry; | |
137 *w = rw; | |
138 *h = rh; | |
139 *ninegrid = rninegrid; | |
140 } | |
141 return ret; | |
142 } | |
143 | |
144 void undo_stack_reset() { | |
145 int i; | |
146 | |
147 for (i=0; i<N_UNDO; i++) { | |
148 u.x[i] = -1; | |
149 u.y[i] = -1; | |
150 u.w[i] = -1; | |
151 u.h[i] = -1; | |
152 } | |
153 } | |
154 /* =-=-=-=-=-= */ | |
155 | |
156 int chk_keysym(int keysym, char *str) { | |
157 int ret = 0; | |
158 | |
159 if (XStringToKeysym(str) == keysym) | |
160 ret = 1; | |
161 | |
162 return ret; | |
163 } | |
164 | |
165 GC creategc(Window win) { | |
166 GC gc; | |
167 XGCValues values; | |
168 | |
169 gc = XCreateGC(dpy, win, 0, &values); | |
170 XSetForeground(dpy, gc, BlackPixel(dpy, 0)); | |
171 XSetBackground(dpy, gc, WhitePixel(dpy, 0)); | |
172 XSetLineAttributes(dpy, gc, LINEWIDTH, LineSolid, CapButt, JoinBevel); | |
173 XSetFillStyle(dpy, gc, FillSolid); | |
174 | |
175 return gc; | |
176 } | |
177 | |
178 void drawquadrants(Window win, int w, int h) { | |
179 GC gc; | |
180 XRectangle clip[20]; | |
181 int idx = 0; | |
182 Colormap colormap; | |
183 XColor color; | |
184 | |
185 gc = creategc(win); | |
186 colormap = DefaultColormap(dpy, 0); | |
187 | |
188 if (drag) | |
189 XAllocNamedColor(dpy, colormap, DRAG_COLOR, &color, &color); | |
190 else | |
191 XAllocNamedColor(dpy, colormap, NORM_COLOR, &color, &color); | |
192 | |
193 /*left*/ clip[idx].x = 0; clip[idx].y = 0; clip[idx].width = BORDER; clip[idx].height = h; | |
194 idx++; | |
195 /*right*/ clip[idx].x = w-BORDER; clip[idx].y = 0; clip[idx].width = BORDER; clip[idx].height = h; | |
196 idx++; | |
197 /*top*/ clip[idx].x = 0; clip[idx].y = 0; clip[idx].width = w; clip[idx].height = BORDER; | |
198 idx++; | |
199 /*bottom*/ clip[idx].x = 0; clip[idx].y = h-BORDER; clip[idx].width = w; clip[idx].height = BORDER; | |
200 idx++; | |
201 | |
202 if (ninegrid) { | |
203 /*1st horiz*/ | |
204 clip[idx].x = 0; clip[idx].y = h/3 - BORDER/2; | |
205 clip[idx].width = w; clip[idx].height = BORDER; | |
206 idx++; | |
207 /*2nd horiz*/ | |
208 clip[idx].x = 0; clip[idx].y = h*2/3 - BORDER/2; | |
209 clip[idx].width = w; clip[idx].height = BORDER; | |
210 idx++; | |
211 /*1st vert*/ | |
212 clip[idx].x = w/3 - BORDER/2; clip[idx].y = 0; | |
213 clip[idx].width = BORDER; clip[idx].height = h; | |
214 idx++; | |
215 /*2nd vert*/ | |
216 clip[idx].x = w*2/3 - BORDER/2; clip[idx].y = 0; | |
217 clip[idx].width = BORDER; clip[idx].height = h; | |
218 idx++; | |
219 } else { | |
220 /*horiz*/ | |
221 clip[idx].x = 0; clip[idx].y = h/2 - BORDER/2; | |
222 clip[idx].width = w; clip[idx].height = BORDER; | |
223 idx++; | |
224 /*vert*/ | |
225 clip[idx].x = w/2 - BORDER/2; clip[idx].y = 0; | |
226 clip[idx].width = BORDER; clip[idx].height = h; | |
227 idx++; | |
228 } | |
229 | |
230 XShapeCombineRectangles(dpy, win, ShapeBounding, 0, 0, clip, idx, ShapeSet, 0); | |
231 | |
232 XFillRectangle(dpy, win, gc, 0, 0, w, h); | |
233 | |
234 XSetForeground(dpy, gc, color.pixel); | |
235 XDrawLine(dpy, win, gc, BORDER - PEN, BORDER - PEN, w - PEN, BORDER - PEN); //top line | |
236 XDrawLine(dpy, win, gc, BORDER - PEN, h - PEN, w - PEN, h - PEN); //bottom line | |
237 XDrawLine(dpy, win, gc, BORDER - PEN, BORDER - PEN, BORDER - PEN, h - PEN); //left line | |
238 XDrawLine(dpy, win, gc, w - PEN, BORDER - PEN, w - PEN, h - PEN); //left line | |
239 | |
240 if (ninegrid) { | |
241 XDrawLine(dpy, win, gc, w/3, 0, w/3, h); // 1st vert line | |
242 XDrawLine(dpy, win, gc, w*2/3, 0, w*2/3, h); // 2nd vert line | |
243 XDrawLine(dpy, win, gc, 0, h/3, w, h/3); // 1st horiz line | |
244 XDrawLine(dpy, win, gc, 0, h*2/3, w, h*2/3); // 2nd horiz line | |
245 } else { | |
246 XDrawLine(dpy, win, gc, w/2, 0, w/2, h); // vert line | |
247 XDrawLine(dpy, win, gc, 0, h/2, w, h/2); // horiz line | |
248 } | |
249 | |
250 XFlush(dpy); | |
251 } | |
252 | |
253 int handlekey(int keysym, int mod, int *moves, int *x, int *y, int *w, int *h) { | |
254 int ret = 1; | |
255 int ox = *x; | |
256 int oy = *y; | |
257 int ow = *w; | |
258 int oh = *h;; | |
259 | |
260 if (mod & SHIFTMASK || mod & SHIFTMASK & NUMLOCKMASK || mod & SHIFTMASK & CAPSLOCKMASK || mod & SHIFTMASK & CAPSLOCKMASK & NUMLOCKMASK) { | |
261 if (chk_keysym(keysym, KEY_CENTER)) | |
262 toggleninegrid(); | |
263 if (chk_keysym(keysym, KEY_LEFT) || chk_keysym(keysym, KEY_UPLEFT) || chk_keysym(keysym, KEY_DOWNLEFT)) { | |
264 if (*x > 0) *x -= *w; // shift left | |
265 } | |
266 if (chk_keysym(keysym, KEY_DOWN) || chk_keysym(keysym, KEY_DOWNLEFT) || chk_keysym(keysym, KEY_DOWNRIGHT)) { | |
267 if ((*y + *h) < attr.height) *y += *h; // shift down | |
268 } | |
269 if (chk_keysym(keysym, KEY_UP) || chk_keysym(keysym, KEY_UPLEFT) || chk_keysym(keysym, KEY_UPRIGHT)) { | |
270 if (*y > 0) *y -= *h; // shift up | |
271 } | |
272 if (chk_keysym(keysym, KEY_RIGHT) || chk_keysym(keysym, KEY_UPRIGHT) || chk_keysym(keysym, KEY_DOWNRIGHT)) { | |
273 if ((*x + *w) < attr.width) *x += *w; // shift right | |
274 } | |
275 } else { | |
276 if (ninegrid) { | |
277 if (chk_keysym(keysym, KEY_UPLEFT)) { | |
278 *w /= 3; *h /= 3; // split upper left | |
279 } else if (chk_keysym(keysym, KEY_UP)) { | |
280 *w /= 3; *h /= 3; // split up | |
281 *x += *w; | |
282 } else if (chk_keysym(keysym, KEY_UPRIGHT)) { | |
283 *w /= 3; *h /= 3; // split upper right | |
284 *x += *w * 2; | |
285 } else if (chk_keysym(keysym, KEY_LEFT)) { | |
286 *w /= 3; *h /= 3; // split left | |
287 *y += *h; | |
288 } else if (chk_keysym(keysym, KEY_CENTER)) { | |
289 *w /= 3; *h /= 3; // split center | |
290 *x += *w; *y += *h; | |
291 } else if (chk_keysym(keysym, KEY_RIGHT)) { | |
292 *w /= 3; *h /= 3; // split right | |
293 *x += *w * 2; *y += *h; | |
294 } else if (chk_keysym(keysym, KEY_DOWNLEFT)) { | |
295 *w /= 3; *h /= 3; // split bottom left | |
296 *y += *h * 2; | |
297 } else if (chk_keysym(keysym, KEY_DOWN)) { | |
298 *w /= 3; *h /= 3; // split down | |
299 *x += *w; *y += *h * 2; | |
300 } else if (chk_keysym(keysym, KEY_DOWNRIGHT)) { | |
301 *w /= 3; *h /= 3; // split bottom right | |
302 *x += *w * 2; *y += *h * 2; | |
303 } | |
304 } else { | |
305 if (chk_keysym(keysym, KEY_LEFT) || chk_keysym(keysym, KEY_UPLEFT) || chk_keysym(keysym, KEY_DOWNLEFT)) { | |
306 *w /= 2; // split left | |
307 } | |
308 if (chk_keysym(keysym, KEY_DOWN) || chk_keysym(keysym, KEY_DOWNLEFT) || chk_keysym(keysym, KEY_DOWNRIGHT)) { | |
309 *h /= 2; // split down | |
310 *y += *h; | |
311 } | |
312 if (chk_keysym(keysym, KEY_UP) || chk_keysym(keysym, KEY_UPLEFT) || chk_keysym(keysym, KEY_UPRIGHT)) { | |
313 *h /= 2; // split up | |
314 } | |
315 if (chk_keysym(keysym, KEY_RIGHT) || chk_keysym(keysym, KEY_UPRIGHT) || chk_keysym(keysym, KEY_DOWNRIGHT)) { | |
316 *w /= 2; // split right | |
317 *x += *w; | |
318 } | |
319 } | |
320 } | |
321 | |
322 if (ox != *x || oy != *y || ow != *w || oh != *h) { | |
323 undo_stack_push(ox, oy, ow, oh); | |
324 *moves += 1; | |
325 } | |
326 else if (chk_keysym(keysym, KEY_UNDO)) { | |
327 int i, j; | |
328 | |
329 if (mod & SHIFTMASK) j = 0; | |
330 else j = SHIFT_N_UNDO-1; | |
331 | |
332 for (i=j; i<SHIFT_N_UNDO; i++) { | |
333 undo_stack_pop(x, y, w, h, &ninegrid); | |
334 *moves -= 1; | |
335 } | |
336 ret = 2; | |
337 } | |
338 | |
339 if (*w < 1 || *h < 1) { | |
340 fprintf(stderr,"OOPS. Area too small. Giving up :(\n"); | |
341 return 0; | |
342 } | |
343 | |
344 if (drag && !(mod & WARPMASK) || !drag && (mod & WARPMASK)) | |
345 warppointer(*x, *y, *w, *h); | |
346 | |
347 fprintf(stderr,"Box: @(%d,%d) #(%d,%d)\n", *x, *y, *w, *h); | |
348 | |
349 return ret; | |
350 } | |
351 | |
352 void startmousekey(int from_undo) { | |
353 int keysym; | |
354 int done = 0; | |
355 int x,y,w,h; | |
356 int warp = 0; | |
357 int click = 0; | |
358 int k, hits, moves; | |
359 int mod; | |
360 | |
361 Window zone; | |
362 | |
363 // disable trigger keygrab | |
364 ungrab(KEY_TRIGGER, TRIGGERMASK); | |
365 ungrab(KEY_UNDO, TRIGGERMASK); | |
366 | |
367 // grab keyboard | |
368 XGrabKeyboard(dpy, root, False, GrabModeAsync, GrabModeAsync, CurrentTime); | |
369 | |
370 // init stuff | |
371 hits = moves = 0; | |
372 if (from_undo && undo_stack_pop(&x, &y, &w, &h, &ninegrid)); | |
373 /* null -- only check for undo_stack_pop success */ | |
374 else { | |
375 undo_stack_reset(); | |
376 if (ninegrid_default) | |
377 ninegrid = 1; | |
378 x = y = 0; | |
379 w = attr.width; | |
380 h = attr.height; | |
381 } | |
382 | |
383 zone = XCreateSimpleWindow(dpy, root, x, y, w, h, 1, BlackPixel(dpy, 0), WhitePixel(dpy, 0)); | |
384 | |
385 { /* Tell the window manager not to manage us */ | |
386 unsigned long valuemask; | |
387 XSetWindowAttributes winattr; | |
388 winattr.override_redirect = 1; | |
389 XChangeWindowAttributes(dpy, zone, CWOverrideRedirect, &winattr); | |
390 } | |
391 | |
392 drawquadrants(zone, w, h); | |
393 XMapWindow(dpy, zone); | |
394 drawquadrants(zone, w, h); | |
395 | |
396 fprintf(stderr,"Starting quadrants...\n"); | |
397 while (!done) { | |
398 XEvent e; | |
399 XNextEvent(dpy, &e); | |
400 if (e.type == KeyPress) { | |
401 keysym = XKeycodeToKeysym(dpy, e.xkey.keycode, 0); | |
402 mod = e.xkey.state; | |
403 if (chk_keysym(keysym, KEY_Q_CLICK)) { | |
404 done++; | |
405 click = 1; | |
406 } else if (chk_keysym(keysym, KEY_Q_RCLICK)) { | |
407 done++; | |
408 click = 3; | |
409 } else if (chk_keysym(keysym, KEY_QW_CLICK)) { | |
410 done++; | |
411 warp = 1; | |
412 click = 1; | |
413 } else if (chk_keysym(keysym, KEY_Q_MCLICK)) { | |
414 done++; | |
415 click = 2; | |
416 } else if (chk_keysym(keysym, KEY_QW_RCLICK)) { | |
417 done++; | |
418 warp = 1; | |
419 click = 3; | |
420 } else if (chk_keysym(keysym, KEY_HIDE)) { | |
421 done++; | |
422 warp = 1; | |
423 x = w; y = h; | |
424 } else if (chk_keysym(keysym, KEY_ESCAPE)) { | |
425 warp = 0; | |
426 done++; | |
427 } else if (!drag && chk_keysym(keysym, KEY_ONE)) { | |
428 buttondown(1); | |
429 buttonup(1); | |
430 } else if (!drag && chk_keysym(keysym, KEY_TWO)) { | |
431 buttondown(2); | |
432 buttonup(2); | |
433 } else if (!drag && chk_keysym(keysym, KEY_THREE)) { | |
434 buttondown(3); | |
435 buttonup(3); | |
436 } else { | |
437 if (k = handlekey(keysym, mod, &moves, &x, &y, &w, &h)) { | |
438 if (ninegrid_default && ninegrid_n_switch && k < 2 && moves >= ninegrid_n_switch) | |
439 ninegrid = 0; | |
440 hits++; | |
441 XMoveResizeWindow(dpy, zone, x, y, w, h); | |
442 drawquadrants(zone, w, h); | |
443 } else | |
444 done++; | |
445 } | |
446 } | |
447 } | |
448 | |
449 // end mouse key | |
450 XUngrabKeyboard(dpy, CurrentTime); | |
451 XDestroyWindow(dpy, zone); | |
452 undo_stack_push(x, y, w, h); | |
453 | |
454 if (warp) | |
455 warppointer(x, y, w, h); | |
456 if (click == 1) { | |
457 if (!drag) | |
458 buttondown(1); | |
459 if (mod & DRAGMASK && !drag) | |
460 drag = 1; | |
461 else if (mod & DRAGMASK) { | |
462 buttonup(1); | |
463 buttondown(1); | |
464 } else { | |
465 buttonup(1); | |
466 drag = 0; | |
467 } | |
468 } else if (click) { | |
469 buttondown(click); | |
470 buttonup(click); | |
471 } | |
472 | |
473 if (click) stats(hits, moves); | |
474 | |
475 // loop back if in drag mode | |
476 if (drag) { | |
477 usleep(50); | |
478 startmousekey(0); | |
479 } | |
480 | |
481 // re-enable trigger keygrabs | |
482 grab(KEY_TRIGGER, TRIGGERMASK); | |
483 grab(KEY_UNDO, TRIGGERMASK); | |
484 } | |
485 | |
486 int main(int argc, char **argv) { | |
487 char *pcDisplay; | |
488 | |
489 if ((dpy = XOpenDisplay(pcDisplay = getenv("DISPLAY"))) == NULL) { | |
490 fprintf(stderr,"Error: Can't open display: %s", pcDisplay); | |
491 exit(1); | |
492 } | |
493 fprintf(stderr,"Display: %s\n", pcDisplay); | |
494 | |
495 // parse options | |
496 if (argc > 1) | |
497 if (argv[1][0] && argv[1][0] == '-') | |
498 if (argv[1][1] && argv[1][1] == 'n') { | |
499 ninegrid_default = 1; | |
500 fprintf(stderr,"nine-grid default mode \n"); | |
501 if (argv[2]) { | |
502 ninegrid_n_switch = atoi(argv[2]); | |
503 fprintf(stderr,"nine-grid autoswitch num: %d \n", ninegrid_n_switch); | |
504 } | |
505 } | |
506 | |
507 root = XDefaultRootWindow(dpy); | |
508 XGetWindowAttributes(dpy, root, &attr); | |
509 | |
510 // these are the trigger keygrabs | |
511 grab(KEY_TRIGGER, TRIGGERMASK); | |
512 grab(KEY_UNDO, TRIGGERMASK); | |
513 | |
514 while (1) { | |
515 XEvent e; | |
516 XNextEvent(dpy, &e); | |
517 if (e.type == KeyPress) | |
518 if (chk_keysym(XKeycodeToKeysym(dpy, e.xkey.keycode, 0), KEY_UNDO)) | |
519 startmousekey(1); | |
520 else | |
521 startmousekey(0); | |
522 } | |
523 } |