From 19a818ca4cd3d4402bceb253fac540460502ef5f Mon Sep 17 00:00:00 2001
From: Nazri Ramliy <ayiehere@gmail.com>
Date: Fri, 12 Dec 2008 22:05:13 +0800
Subject: [PATCH 1/2] Lotsa holes! Alternate navigation (labeled grid).

---
 keynav.c |  188 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 168 insertions(+), 20 deletions(-)

diff --git a/keynav.c b/keynav.c
index df684fa..ed5b4ad 100644
--- a/keynav.c
+++ b/keynav.c
@@ -41,6 +41,7 @@ static char drag_modkeys[128];
 
 #define STATE_ACTIVE 0x0001 
 #define STATE_DRAGGING 0x0002
+#define STATE_MULTIKEY 0x0004
 
 typedef struct wininfo {
   int x;
@@ -69,6 +70,24 @@ static viewport_t *viewports;
 static int nviewports = 0;
 static int xinerama = 0;
 
+#define ROUNDING (.5)
+#define LABEL_WIDTH (22)
+#define LABEL_HEIGHT (20)
+#define ROW_START ('a')
+#define COLUMN_START ('a')
+static int key_sequence[3] = {
+  XK_VoidSymbol,
+  XK_VoidSymbol,
+  XK_VoidSymbol
+};
+static int key_sequence_count;
+static int old_hole_count = 0;
+static int old_clip_count = 0;
+static XRectangle *clip = NULL;
+static XRectangle *holes = NULL; // Stanley Yelnats
+void realloc_clip_if_necessary(struct wininfo *info);
+void dig_holes_as_needed(struct wininfo *info);
+
 /* history tracking */
 #define WININFO_MAXHIST (100)
 static wininfo_t wininfo_history[WININFO_MAXHIST]; /* XXX: is 100 enough? */
@@ -443,17 +462,60 @@ GC creategc(Window win) {
   return gc;
 }
 
+void realloc_clip_if_necessary(struct wininfo *info) {
+  int hole_count = (info->grid_x + 1) * (info->grid_y + 1);
+  int grid_count = (info->grid_x - 1) + (info->grid_y - 1);
+  int left_right_top_bottom = 4;
+  int clip_count = left_right_top_bottom + grid_count + hole_count;
+
+  //printf("clip count = %d\n", clip_count);
+
+  if(clip == NULL) {
+    clip = malloc(sizeof(XRectangle));
+    old_clip_count = 1;
+  }
+
+  if(clip_count != old_clip_count) {
+    clip = realloc(clip, clip_count * sizeof(XRectangle));
+    old_clip_count = clip_count;
+  }
+
+  if(clip == NULL) {
+    perror("Could not dig more memory for clipping");
+    exit(1);
+  }
+}
+
+void dig_holes_as_needed(struct wininfo *info) {
+  int hole_count = (info->grid_x + 1) * (info->grid_y + 1);
+  // printf("hole count = %d\n", hole_count);
+
+  if(holes == NULL) {
+    holes = malloc(sizeof(XRectangle));
+    old_hole_count = 1;
+  }
+
+  if(hole_count != old_hole_count) {
+    holes = realloc(holes, hole_count * sizeof(XRectangle));
+    old_hole_count = hole_count;
+  }
+
+  if(holes == NULL) {
+    perror("Could not dig more holes.");
+    exit(1);
+  }
+}
+
 void drawgrid(Window win, struct wininfo *info, int apply_clip) {
   GC gc;
-  XRectangle clip[30];
-  int idx = 0;
+  int idx = 0, row, col;
   Colormap colormap;
   XColor red, unused_xcolor;
   XColor white;
   int w = info->w;
   int h = info->h;
-  int cell_width;
-  int cell_height;
+  double cell_width;
+  double cell_height;
   int i;
 
   gc = creategc(win);
@@ -469,6 +531,7 @@ void drawgrid(Window win, struct wininfo *info, int apply_clip) {
   // Draw white lines.
   XSetForeground(dpy, gc, WhitePixel(dpy, 0));
 
+  realloc_clip_if_necessary(info);
 
   /*left*/ 
   clip[idx].x = 0;
@@ -498,12 +561,12 @@ void drawgrid(Window win, struct wininfo *info, int apply_clip) {
   clip[idx].height = info->border_thickness;
   idx++;
   
-  cell_width = (w / info->grid_y);
-  cell_height = (h / info->grid_x);
+  cell_width = ((double) w / info->grid_y);
+  cell_height = ((double) h / info->grid_x);
 
   /* clip vertically */
   for (i = 1; i < info->grid_y; i++) {
-    clip[idx].x = cell_width * i - (info->border_thickness / 2); 
+    clip[idx].x = cell_width * i - (info->border_thickness / 2) + ROUNDING;
     clip[idx].y = 0;
     clip[idx].width = info->border_thickness;
     clip[idx].height = h;
@@ -513,26 +576,49 @@ void drawgrid(Window win, struct wininfo *info, int apply_clip) {
   /* clip horizontally */
   for (i = 1; i < info->grid_x; i++) {
     clip[idx].x = 0;
-    clip[idx].y = cell_height * i - (info->border_thickness / 2);
+    clip[idx].y = cell_height * i - (info->border_thickness / 2) + ROUNDING;
     clip[idx].width = w;
     clip[idx].height = info->border_thickness;
     idx++;
   }
 
-  if (apply_clip) {
-    XShapeCombineRectangles(dpy, win, ShapeBounding, 0, 0, clip, idx, ShapeSet, 0);
+  int border_idx = idx;
+  char row_label, col_label;
+  int holes_idx = 0;
+  dig_holes_as_needed(info);
+  for (row = 0; row <= info->grid_x; row++) {
+    row_label = ROW_START + row;
+    for (col = 0; col <= info->grid_y; col++) {
+      col_label = COLUMN_START + col;
+      clip[idx].x = cell_width * col - LABEL_WIDTH/2 + ROUNDING;
+      clip[idx].y = cell_height * row - LABEL_HEIGHT + ROUNDING;
+      if(clip[idx].x < 0) clip[idx].x = 0;
+      if(clip[idx].y < 0) clip[idx].y = 0;
+      if(clip[idx].x + LABEL_WIDTH > info->w) clip[idx].x = info->w - LABEL_WIDTH;
+      //if(clip[idx].y < 0) clip[idx].y = -clip[idx].y;
+
+      clip[idx].width = LABEL_WIDTH;
+      clip[idx].height = LABEL_HEIGHT;
+      XDrawString(dpy, win, gc, clip[idx].x+4, clip[idx].y+13, &row_label, 1);
+      XDrawString(dpy, win, gc, clip[idx].x+14, clip[idx].y+13, &col_label, 1);
+      idx++;
+
+      holes[holes_idx].x = cell_width * col - info->center_cut_size/2 + ROUNDING;
+      holes[holes_idx].y = cell_height * row - info->center_cut_size/2 + ROUNDING;
+      holes[holes_idx].width = info->center_cut_size;
+      holes[holes_idx].height = info->center_cut_size;
+      holes_idx++;
+    }
+  }
 
-    /* Cut out a hole in the center */
-    clip[idx].x = (w/2 - (info->center_cut_size/2));
-    clip[idx].y = (h/2 - (info->center_cut_size/2));
-    clip[idx].width = info->center_cut_size;
-    clip[idx].height = info->center_cut_size;
-    idx++;
 
-    XShapeCombineRectangles(dpy, win, ShapeBounding, 0, 0, clip + idx - 1, 1, ShapeSubtract, 0);
+  if (apply_clip) {
+    XShapeCombineRectangles(dpy, win, ShapeBounding, 0, 0, clip, idx, ShapeSet, 0);
+    XShapeCombineRectangles(dpy, win, ShapeBounding, 0, 0, holes, holes_idx, ShapeSubtract, 0);
   }
+  //printf("idx=%d\n", idx);
 
-  for (i = 0; i < idx; i++) {
+  for (i = 0; i < border_idx; i++) {
     drawborderline(info, win, gc, &(clip[i]));
   }
 }
@@ -713,8 +799,43 @@ void cmd_windowzoom(char *args) {
 void cmd_warp(char *args) {
   if (appstate & STATE_ACTIVE == 0)
     return;
-  //printf("Warp\n");
-  xdo_mousemove(xdo, wininfo.x + wininfo.w / 2, wininfo.y + wininfo.h / 2);
+  //printf("warp\n");
+  if(key_sequence[0] == XK_VoidSymbol) {
+    xdo_mousemove(xdo, wininfo.x + wininfo.w / 2, wininfo.y + wininfo.h / 2);
+  } else {
+    char *flag_str = XKeysymToString(XKeycodeToKeysym(xdo->xdpy, key_sequence[0], 0));
+    char *row_str = XKeysymToString(XKeycodeToKeysym(xdo->xdpy, key_sequence[1], 0));
+    char *col_str = XKeysymToString(XKeycodeToKeysym(xdo->xdpy, key_sequence[2], 0));
+    char row = row_str[0];
+    char col = col_str[0];
+    int w = wininfo.w;
+    int h = wininfo.h;
+
+    double cell_width = ((double) w / wininfo.grid_y);
+    double cell_height = ((double) h / wininfo.grid_x);
+    int x;
+    int y;
+
+    // comma-[a-z]-[0-9] warps to a grid crossing
+    x = wininfo.x + cell_width * (col - COLUMN_START) + ROUNDING;
+    y = wininfo.y + cell_height * (row - ROW_START) + ROUNDING;
+
+    // apostrophe-[a-z]-[0-9] warps to the center of a cell.
+    if(!strncmp(flag_str, "apostrophe", 10)) {
+      x = x + cell_width/2;
+      y = y - cell_height/2;
+    }
+
+    //printf("%c %c (%d, %d) width: %d height: %d\n", 
+    // row, col, x, y, cell_width, cell_height);
+
+    key_sequence[0] = XK_VoidSymbol;
+    if(x - wininfo.x <= w && y - wininfo.y <= h) {
+      xdo_mousemove(xdo, x, y);
+    } else {
+      fprintf(stderr, "Invalid grid/coordinate\n");
+    }
+  }
 
   /* Some apps (window managers) don't acknowledge a drag unless there's been
    * some wiggle. Let's wiggle if we're dragging. */
@@ -962,6 +1083,7 @@ void drawborderline(struct wininfo *info, Window win, GC gc, XRectangle *rect) {
 
 void handle_keypress(XKeyEvent *e) {
   int i;
+  Display *dpy = xdo->xdpy;
 
   /* If a mouse button is pressed (like, when we're dragging),
    * then the 'mods' will include values like Button1Mask. 
@@ -969,6 +1091,24 @@ void handle_keypress(XKeyEvent *e) {
   e->state &= ~(Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask);
   e->state &= ~(LockMask | Mod2Mask);
 
+  if ((appstate & STATE_MULTIKEY) == 0) {
+    if( XK_comma == XKeycodeToKeysym(dpy, e->keycode, 0) || 
+        XK_apostrophe == XKeycodeToKeysym(dpy, e->keycode, 0) ) {
+      appstate |= STATE_MULTIKEY;
+      key_sequence[key_sequence_count++] = e->keycode;
+      return;
+    }
+  }
+
+  if (appstate & STATE_MULTIKEY) {
+    key_sequence[key_sequence_count++] = e->keycode;
+    if(key_sequence_count == 3) {
+      appstate &= ~(STATE_MULTIKEY);
+      key_sequence_count = 0;
+    }
+    return;
+  } 
+
   /* Loop over known keybindings */
   for (i = nkeybindings - 1; i >= 0; i--) {
     //printf("e->state:%d bindmods:%d and:%d\n", e->state, keybindings[i].mods, e->state & keybindings[i].mods);
@@ -1235,5 +1375,13 @@ int main(int argc, char **argv) {
     }
   }
 
+  if(holes != NULL) {
+    free(holes);
+  }
+
+  if(clip != NULL) {
+    free(clip);
+  }
+
   xdo_free(xdo);
 }
-- 
1.6.6.rc0.63.g66abc

