Mercurial > hg > index.fcgi > keynav-9pba
diff 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 |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/keynav.c Tue Sep 08 22:45:57 2009 -0700 1.3 @@ -0,0 +1,523 @@ 1.4 +/* 1.5 + * Visual user-directed binary search for something to point your mouse at. 1.6 + */ 1.7 + 1.8 +#include "config.h" 1.9 + 1.10 +#include <stdio.h> 1.11 +#include <stdlib.h> 1.12 +#include <string.h> 1.13 + 1.14 +#include <X11/Xlib.h> 1.15 +#include <X11/Xresource.h> 1.16 +#include <X11/Xutil.h> 1.17 +#include <X11/extensions/shape.h> 1.18 +#include <X11/extensions/XTest.h> 1.19 + 1.20 +Display *dpy; 1.21 +Window root; 1.22 +XWindowAttributes attr; 1.23 +int drag = 0; 1.24 +int allmoves = 0; 1.25 +int allhits = 0; 1.26 +int allclicks = 0; 1.27 +int ninegrid_default = 0; 1.28 +int ninegrid_n_switch = 0; 1.29 +int ninegrid = 0; 1.30 + 1.31 +struct /* undo stack */ 1.32 +{ 1.33 + int x[N_UNDO]; 1.34 + int y[N_UNDO]; 1.35 + int w[N_UNDO]; 1.36 + int h[N_UNDO]; 1.37 + int ninegrid[N_UNDO]; 1.38 +} u; 1.39 + 1.40 +void toggleninegrid() { 1.41 + if (ninegrid) 1.42 + ninegrid = 0; 1.43 + else 1.44 + ninegrid = 1; 1.45 +} 1.46 + 1.47 +void stats(int hits, int moves) { 1.48 + allhits += hits; 1.49 + allmoves += moves; 1.50 + allclicks++; 1.51 + printf("hits=%d, moves=%d \n", hits, moves); 1.52 + 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); 1.53 + fflush(stdout); fflush(stderr); // force buffers to write out 1.54 +} 1.55 + 1.56 +void grab(char *keyname, int mods) { 1.57 + int key; 1.58 + 1.59 + key = XKeysymToKeycode(dpy, XStringToKeysym(keyname)); 1.60 + XGrabKey(dpy, key, mods, root, False, 1.61 + GrabModeAsync, GrabModeAsync); 1.62 + XGrabKey(dpy, key, mods | CAPSLOCKMASK, root, False, 1.63 + GrabModeAsync, GrabModeAsync); 1.64 + XGrabKey(dpy, key, mods | NUMLOCKMASK, root, False, 1.65 + GrabModeAsync, GrabModeAsync); 1.66 + XGrabKey(dpy, key, mods | CAPSLOCKMASK | NUMLOCKMASK, root, False, 1.67 + GrabModeAsync, GrabModeAsync); 1.68 +} 1.69 + 1.70 +void ungrab(char *keyname, int mod) { 1.71 + int key; 1.72 + 1.73 + key = XKeysymToKeycode(dpy, XStringToKeysym(keyname)); 1.74 + XUngrabKey(dpy, key, mod, root); 1.75 + XUngrabKey(dpy, key, mod | CAPSLOCKMASK, root); 1.76 + XUngrabKey(dpy, key, mod | NUMLOCKMASK, root); 1.77 + XUngrabKey(dpy, key, mod | CAPSLOCKMASK | NUMLOCKMASK, root); 1.78 +} 1.79 + 1.80 +void buttondown(int i) { 1.81 + XTestFakeButtonEvent(dpy, i, True, 50); 1.82 +} 1.83 + 1.84 +void buttonup(int i) { 1.85 + XTestFakeButtonEvent(dpy, i, False, 50); 1.86 +} 1.87 + 1.88 +void warppointer(int x, int y, int w, int h) { 1.89 + XWarpPointer(dpy, None, root, 0, 0, 0, 0, x + w/2, y + h/2); 1.90 +} 1.91 + 1.92 +/* operations for undo */ 1.93 +void undo_stack_push(int x, int y, int w, int h) { 1.94 + int i; 1.95 + 1.96 + if (x >= 0 && y >= 0 && w > 0 && h > 0) { 1.97 + fprintf(stderr,"undo_stack_push success: @(%d,%d) #(%d,%d)\n", x, y, w, h); 1.98 + for (i=N_UNDO-1; i>0; i--) { 1.99 + u.x[i] = u.x[i-1]; 1.100 + u.y[i] = u.y[i-1]; 1.101 + u.w[i] = u.w[i-1]; 1.102 + u.h[i] = u.h[i-1]; 1.103 + u.ninegrid[i] = u.ninegrid[i-1]; 1.104 + } 1.105 + u.x[0] = x; 1.106 + u.y[0] = y; 1.107 + u.w[0] = w; 1.108 + u.h[0] = h; 1.109 + u.ninegrid[0] = ninegrid; 1.110 + } 1.111 +} 1.112 + 1.113 +int undo_stack_pop(int *x, int *y, int *w, int *h, int *ninegrid) { 1.114 + int i; 1.115 + int ret = 0; 1.116 + 1.117 + int rx = u.x[0]; 1.118 + int ry = u.y[0]; 1.119 + int rw = u.w[0]; 1.120 + int rh = u.h[0]; 1.121 + int rninegrid = u.ninegrid[0]; 1.122 + 1.123 + if (rx >= 0 && ry >= 0 && rw > 0 && rh > 0) { 1.124 + fprintf(stderr,"undo_stack_pop success: @(%d,%d) #(%d,%d)\n", *x, *y, *w, *h); 1.125 + ret = 1; // return success 1.126 + for (i=0; i<N_UNDO-1; i++) { 1.127 + u.x[i] = u.x[i+1]; 1.128 + u.y[i] = u.y[i+1]; 1.129 + u.w[i] = u.w[i+1]; 1.130 + u.h[i] = u.h[i+1]; 1.131 + u.ninegrid[i] = u.ninegrid[i+1]; 1.132 + } 1.133 + u.x[N_UNDO-1] = -1; 1.134 + u.y[N_UNDO-1] = -1; 1.135 + u.w[N_UNDO-1] = -1; 1.136 + u.h[N_UNDO-1] = -1; 1.137 + u.ninegrid[N_UNDO-1] = -1; 1.138 + *x = rx; 1.139 + *y = ry; 1.140 + *w = rw; 1.141 + *h = rh; 1.142 + *ninegrid = rninegrid; 1.143 + } 1.144 + return ret; 1.145 +} 1.146 + 1.147 +void undo_stack_reset() { 1.148 + int i; 1.149 + 1.150 + for (i=0; i<N_UNDO; i++) { 1.151 + u.x[i] = -1; 1.152 + u.y[i] = -1; 1.153 + u.w[i] = -1; 1.154 + u.h[i] = -1; 1.155 + } 1.156 +} 1.157 +/* =-=-=-=-=-= */ 1.158 + 1.159 +int chk_keysym(int keysym, char *str) { 1.160 + int ret = 0; 1.161 + 1.162 + if (XStringToKeysym(str) == keysym) 1.163 + ret = 1; 1.164 + 1.165 + return ret; 1.166 +} 1.167 + 1.168 +GC creategc(Window win) { 1.169 + GC gc; 1.170 + XGCValues values; 1.171 + 1.172 + gc = XCreateGC(dpy, win, 0, &values); 1.173 + XSetForeground(dpy, gc, BlackPixel(dpy, 0)); 1.174 + XSetBackground(dpy, gc, WhitePixel(dpy, 0)); 1.175 + XSetLineAttributes(dpy, gc, LINEWIDTH, LineSolid, CapButt, JoinBevel); 1.176 + XSetFillStyle(dpy, gc, FillSolid); 1.177 + 1.178 + return gc; 1.179 +} 1.180 + 1.181 +void drawquadrants(Window win, int w, int h) { 1.182 + GC gc; 1.183 + XRectangle clip[20]; 1.184 + int idx = 0; 1.185 + Colormap colormap; 1.186 + XColor color; 1.187 + 1.188 + gc = creategc(win); 1.189 + colormap = DefaultColormap(dpy, 0); 1.190 + 1.191 + if (drag) 1.192 + XAllocNamedColor(dpy, colormap, DRAG_COLOR, &color, &color); 1.193 + else 1.194 + XAllocNamedColor(dpy, colormap, NORM_COLOR, &color, &color); 1.195 + 1.196 + /*left*/ clip[idx].x = 0; clip[idx].y = 0; clip[idx].width = BORDER; clip[idx].height = h; 1.197 + idx++; 1.198 + /*right*/ clip[idx].x = w-BORDER; clip[idx].y = 0; clip[idx].width = BORDER; clip[idx].height = h; 1.199 + idx++; 1.200 + /*top*/ clip[idx].x = 0; clip[idx].y = 0; clip[idx].width = w; clip[idx].height = BORDER; 1.201 + idx++; 1.202 + /*bottom*/ clip[idx].x = 0; clip[idx].y = h-BORDER; clip[idx].width = w; clip[idx].height = BORDER; 1.203 + idx++; 1.204 + 1.205 + if (ninegrid) { 1.206 + /*1st horiz*/ 1.207 + clip[idx].x = 0; clip[idx].y = h/3 - BORDER/2; 1.208 + clip[idx].width = w; clip[idx].height = BORDER; 1.209 + idx++; 1.210 + /*2nd horiz*/ 1.211 + clip[idx].x = 0; clip[idx].y = h*2/3 - BORDER/2; 1.212 + clip[idx].width = w; clip[idx].height = BORDER; 1.213 + idx++; 1.214 + /*1st vert*/ 1.215 + clip[idx].x = w/3 - BORDER/2; clip[idx].y = 0; 1.216 + clip[idx].width = BORDER; clip[idx].height = h; 1.217 + idx++; 1.218 + /*2nd vert*/ 1.219 + clip[idx].x = w*2/3 - BORDER/2; clip[idx].y = 0; 1.220 + clip[idx].width = BORDER; clip[idx].height = h; 1.221 + idx++; 1.222 + } else { 1.223 + /*horiz*/ 1.224 + clip[idx].x = 0; clip[idx].y = h/2 - BORDER/2; 1.225 + clip[idx].width = w; clip[idx].height = BORDER; 1.226 + idx++; 1.227 + /*vert*/ 1.228 + clip[idx].x = w/2 - BORDER/2; clip[idx].y = 0; 1.229 + clip[idx].width = BORDER; clip[idx].height = h; 1.230 + idx++; 1.231 + } 1.232 + 1.233 + XShapeCombineRectangles(dpy, win, ShapeBounding, 0, 0, clip, idx, ShapeSet, 0); 1.234 + 1.235 + XFillRectangle(dpy, win, gc, 0, 0, w, h); 1.236 + 1.237 + XSetForeground(dpy, gc, color.pixel); 1.238 + XDrawLine(dpy, win, gc, BORDER - PEN, BORDER - PEN, w - PEN, BORDER - PEN); //top line 1.239 + XDrawLine(dpy, win, gc, BORDER - PEN, h - PEN, w - PEN, h - PEN); //bottom line 1.240 + XDrawLine(dpy, win, gc, BORDER - PEN, BORDER - PEN, BORDER - PEN, h - PEN); //left line 1.241 + XDrawLine(dpy, win, gc, w - PEN, BORDER - PEN, w - PEN, h - PEN); //left line 1.242 + 1.243 + if (ninegrid) { 1.244 + XDrawLine(dpy, win, gc, w/3, 0, w/3, h); // 1st vert line 1.245 + XDrawLine(dpy, win, gc, w*2/3, 0, w*2/3, h); // 2nd vert line 1.246 + XDrawLine(dpy, win, gc, 0, h/3, w, h/3); // 1st horiz line 1.247 + XDrawLine(dpy, win, gc, 0, h*2/3, w, h*2/3); // 2nd horiz line 1.248 + } else { 1.249 + XDrawLine(dpy, win, gc, w/2, 0, w/2, h); // vert line 1.250 + XDrawLine(dpy, win, gc, 0, h/2, w, h/2); // horiz line 1.251 + } 1.252 + 1.253 + XFlush(dpy); 1.254 +} 1.255 + 1.256 +int handlekey(int keysym, int mod, int *moves, int *x, int *y, int *w, int *h) { 1.257 + int ret = 1; 1.258 + int ox = *x; 1.259 + int oy = *y; 1.260 + int ow = *w; 1.261 + int oh = *h;; 1.262 + 1.263 + if (mod & SHIFTMASK || mod & SHIFTMASK & NUMLOCKMASK || mod & SHIFTMASK & CAPSLOCKMASK || mod & SHIFTMASK & CAPSLOCKMASK & NUMLOCKMASK) { 1.264 + if (chk_keysym(keysym, KEY_CENTER)) 1.265 + toggleninegrid(); 1.266 + if (chk_keysym(keysym, KEY_LEFT) || chk_keysym(keysym, KEY_UPLEFT) || chk_keysym(keysym, KEY_DOWNLEFT)) { 1.267 + if (*x > 0) *x -= *w; // shift left 1.268 + } 1.269 + if (chk_keysym(keysym, KEY_DOWN) || chk_keysym(keysym, KEY_DOWNLEFT) || chk_keysym(keysym, KEY_DOWNRIGHT)) { 1.270 + if ((*y + *h) < attr.height) *y += *h; // shift down 1.271 + } 1.272 + if (chk_keysym(keysym, KEY_UP) || chk_keysym(keysym, KEY_UPLEFT) || chk_keysym(keysym, KEY_UPRIGHT)) { 1.273 + if (*y > 0) *y -= *h; // shift up 1.274 + } 1.275 + if (chk_keysym(keysym, KEY_RIGHT) || chk_keysym(keysym, KEY_UPRIGHT) || chk_keysym(keysym, KEY_DOWNRIGHT)) { 1.276 + if ((*x + *w) < attr.width) *x += *w; // shift right 1.277 + } 1.278 + } else { 1.279 + if (ninegrid) { 1.280 + if (chk_keysym(keysym, KEY_UPLEFT)) { 1.281 + *w /= 3; *h /= 3; // split upper left 1.282 + } else if (chk_keysym(keysym, KEY_UP)) { 1.283 + *w /= 3; *h /= 3; // split up 1.284 + *x += *w; 1.285 + } else if (chk_keysym(keysym, KEY_UPRIGHT)) { 1.286 + *w /= 3; *h /= 3; // split upper right 1.287 + *x += *w * 2; 1.288 + } else if (chk_keysym(keysym, KEY_LEFT)) { 1.289 + *w /= 3; *h /= 3; // split left 1.290 + *y += *h; 1.291 + } else if (chk_keysym(keysym, KEY_CENTER)) { 1.292 + *w /= 3; *h /= 3; // split center 1.293 + *x += *w; *y += *h; 1.294 + } else if (chk_keysym(keysym, KEY_RIGHT)) { 1.295 + *w /= 3; *h /= 3; // split right 1.296 + *x += *w * 2; *y += *h; 1.297 + } else if (chk_keysym(keysym, KEY_DOWNLEFT)) { 1.298 + *w /= 3; *h /= 3; // split bottom left 1.299 + *y += *h * 2; 1.300 + } else if (chk_keysym(keysym, KEY_DOWN)) { 1.301 + *w /= 3; *h /= 3; // split down 1.302 + *x += *w; *y += *h * 2; 1.303 + } else if (chk_keysym(keysym, KEY_DOWNRIGHT)) { 1.304 + *w /= 3; *h /= 3; // split bottom right 1.305 + *x += *w * 2; *y += *h * 2; 1.306 + } 1.307 + } else { 1.308 + if (chk_keysym(keysym, KEY_LEFT) || chk_keysym(keysym, KEY_UPLEFT) || chk_keysym(keysym, KEY_DOWNLEFT)) { 1.309 + *w /= 2; // split left 1.310 + } 1.311 + if (chk_keysym(keysym, KEY_DOWN) || chk_keysym(keysym, KEY_DOWNLEFT) || chk_keysym(keysym, KEY_DOWNRIGHT)) { 1.312 + *h /= 2; // split down 1.313 + *y += *h; 1.314 + } 1.315 + if (chk_keysym(keysym, KEY_UP) || chk_keysym(keysym, KEY_UPLEFT) || chk_keysym(keysym, KEY_UPRIGHT)) { 1.316 + *h /= 2; // split up 1.317 + } 1.318 + if (chk_keysym(keysym, KEY_RIGHT) || chk_keysym(keysym, KEY_UPRIGHT) || chk_keysym(keysym, KEY_DOWNRIGHT)) { 1.319 + *w /= 2; // split right 1.320 + *x += *w; 1.321 + } 1.322 + } 1.323 + } 1.324 + 1.325 + if (ox != *x || oy != *y || ow != *w || oh != *h) { 1.326 + undo_stack_push(ox, oy, ow, oh); 1.327 + *moves += 1; 1.328 + } 1.329 + else if (chk_keysym(keysym, KEY_UNDO)) { 1.330 + int i, j; 1.331 + 1.332 + if (mod & SHIFTMASK) j = 0; 1.333 + else j = SHIFT_N_UNDO-1; 1.334 + 1.335 + for (i=j; i<SHIFT_N_UNDO; i++) { 1.336 + undo_stack_pop(x, y, w, h, &ninegrid); 1.337 + *moves -= 1; 1.338 + } 1.339 + ret = 2; 1.340 + } 1.341 + 1.342 + if (*w < 1 || *h < 1) { 1.343 + fprintf(stderr,"OOPS. Area too small. Giving up :(\n"); 1.344 + return 0; 1.345 + } 1.346 + 1.347 + if (drag && !(mod & WARPMASK) || !drag && (mod & WARPMASK)) 1.348 + warppointer(*x, *y, *w, *h); 1.349 + 1.350 + fprintf(stderr,"Box: @(%d,%d) #(%d,%d)\n", *x, *y, *w, *h); 1.351 + 1.352 + return ret; 1.353 +} 1.354 + 1.355 +void startmousekey(int from_undo) { 1.356 + int keysym; 1.357 + int done = 0; 1.358 + int x,y,w,h; 1.359 + int warp = 0; 1.360 + int click = 0; 1.361 + int k, hits, moves; 1.362 + int mod; 1.363 + 1.364 + Window zone; 1.365 + 1.366 + // disable trigger keygrab 1.367 + ungrab(KEY_TRIGGER, TRIGGERMASK); 1.368 + ungrab(KEY_UNDO, TRIGGERMASK); 1.369 + 1.370 + // grab keyboard 1.371 + XGrabKeyboard(dpy, root, False, GrabModeAsync, GrabModeAsync, CurrentTime); 1.372 + 1.373 + // init stuff 1.374 + hits = moves = 0; 1.375 + if (from_undo && undo_stack_pop(&x, &y, &w, &h, &ninegrid)); 1.376 + /* null -- only check for undo_stack_pop success */ 1.377 + else { 1.378 + undo_stack_reset(); 1.379 + if (ninegrid_default) 1.380 + ninegrid = 1; 1.381 + x = y = 0; 1.382 + w = attr.width; 1.383 + h = attr.height; 1.384 + } 1.385 + 1.386 + zone = XCreateSimpleWindow(dpy, root, x, y, w, h, 1, BlackPixel(dpy, 0), WhitePixel(dpy, 0)); 1.387 + 1.388 + { /* Tell the window manager not to manage us */ 1.389 + unsigned long valuemask; 1.390 + XSetWindowAttributes winattr; 1.391 + winattr.override_redirect = 1; 1.392 + XChangeWindowAttributes(dpy, zone, CWOverrideRedirect, &winattr); 1.393 + } 1.394 + 1.395 + drawquadrants(zone, w, h); 1.396 + XMapWindow(dpy, zone); 1.397 + drawquadrants(zone, w, h); 1.398 + 1.399 + fprintf(stderr,"Starting quadrants...\n"); 1.400 + while (!done) { 1.401 + XEvent e; 1.402 + XNextEvent(dpy, &e); 1.403 + if (e.type == KeyPress) { 1.404 + keysym = XKeycodeToKeysym(dpy, e.xkey.keycode, 0); 1.405 + mod = e.xkey.state; 1.406 + if (chk_keysym(keysym, KEY_Q_CLICK)) { 1.407 + done++; 1.408 + click = 1; 1.409 + } else if (chk_keysym(keysym, KEY_Q_RCLICK)) { 1.410 + done++; 1.411 + click = 3; 1.412 + } else if (chk_keysym(keysym, KEY_QW_CLICK)) { 1.413 + done++; 1.414 + warp = 1; 1.415 + click = 1; 1.416 + } else if (chk_keysym(keysym, KEY_Q_MCLICK)) { 1.417 + done++; 1.418 + click = 2; 1.419 + } else if (chk_keysym(keysym, KEY_QW_RCLICK)) { 1.420 + done++; 1.421 + warp = 1; 1.422 + click = 3; 1.423 + } else if (chk_keysym(keysym, KEY_HIDE)) { 1.424 + done++; 1.425 + warp = 1; 1.426 + x = w; y = h; 1.427 + } else if (chk_keysym(keysym, KEY_ESCAPE)) { 1.428 + warp = 0; 1.429 + done++; 1.430 + } else if (!drag && chk_keysym(keysym, KEY_ONE)) { 1.431 + buttondown(1); 1.432 + buttonup(1); 1.433 + } else if (!drag && chk_keysym(keysym, KEY_TWO)) { 1.434 + buttondown(2); 1.435 + buttonup(2); 1.436 + } else if (!drag && chk_keysym(keysym, KEY_THREE)) { 1.437 + buttondown(3); 1.438 + buttonup(3); 1.439 + } else { 1.440 + if (k = handlekey(keysym, mod, &moves, &x, &y, &w, &h)) { 1.441 + if (ninegrid_default && ninegrid_n_switch && k < 2 && moves >= ninegrid_n_switch) 1.442 + ninegrid = 0; 1.443 + hits++; 1.444 + XMoveResizeWindow(dpy, zone, x, y, w, h); 1.445 + drawquadrants(zone, w, h); 1.446 + } else 1.447 + done++; 1.448 + } 1.449 + } 1.450 + } 1.451 + 1.452 + // end mouse key 1.453 + XUngrabKeyboard(dpy, CurrentTime); 1.454 + XDestroyWindow(dpy, zone); 1.455 + undo_stack_push(x, y, w, h); 1.456 + 1.457 + if (warp) 1.458 + warppointer(x, y, w, h); 1.459 + if (click == 1) { 1.460 + if (!drag) 1.461 + buttondown(1); 1.462 + if (mod & DRAGMASK && !drag) 1.463 + drag = 1; 1.464 + else if (mod & DRAGMASK) { 1.465 + buttonup(1); 1.466 + buttondown(1); 1.467 + } else { 1.468 + buttonup(1); 1.469 + drag = 0; 1.470 + } 1.471 + } else if (click) { 1.472 + buttondown(click); 1.473 + buttonup(click); 1.474 + } 1.475 + 1.476 + if (click) stats(hits, moves); 1.477 + 1.478 + // loop back if in drag mode 1.479 + if (drag) { 1.480 + usleep(50); 1.481 + startmousekey(0); 1.482 + } 1.483 + 1.484 + // re-enable trigger keygrabs 1.485 + grab(KEY_TRIGGER, TRIGGERMASK); 1.486 + grab(KEY_UNDO, TRIGGERMASK); 1.487 +} 1.488 + 1.489 +int main(int argc, char **argv) { 1.490 + char *pcDisplay; 1.491 + 1.492 + if ((dpy = XOpenDisplay(pcDisplay = getenv("DISPLAY"))) == NULL) { 1.493 + fprintf(stderr,"Error: Can't open display: %s", pcDisplay); 1.494 + exit(1); 1.495 + } 1.496 + fprintf(stderr,"Display: %s\n", pcDisplay); 1.497 + 1.498 + // parse options 1.499 + if (argc > 1) 1.500 + if (argv[1][0] && argv[1][0] == '-') 1.501 + if (argv[1][1] && argv[1][1] == 'n') { 1.502 + ninegrid_default = 1; 1.503 + fprintf(stderr,"nine-grid default mode \n"); 1.504 + if (argv[2]) { 1.505 + ninegrid_n_switch = atoi(argv[2]); 1.506 + fprintf(stderr,"nine-grid autoswitch num: %d \n", ninegrid_n_switch); 1.507 + } 1.508 + } 1.509 + 1.510 + root = XDefaultRootWindow(dpy); 1.511 + XGetWindowAttributes(dpy, root, &attr); 1.512 + 1.513 + // these are the trigger keygrabs 1.514 + grab(KEY_TRIGGER, TRIGGERMASK); 1.515 + grab(KEY_UNDO, TRIGGERMASK); 1.516 + 1.517 + while (1) { 1.518 + XEvent e; 1.519 + XNextEvent(dpy, &e); 1.520 + if (e.type == KeyPress) 1.521 + if (chk_keysym(XKeycodeToKeysym(dpy, e.xkey.keycode, 0), KEY_UNDO)) 1.522 + startmousekey(1); 1.523 + else 1.524 + startmousekey(0); 1.525 + } 1.526 +}