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 +}