chiark / gitweb /
make shifted keys work - untested, but compiles
authorIan Jackson <ian@liberator.(none)>
Wed, 18 Nov 2009 21:10:07 +0000 (21:10 +0000)
committerIan Jackson <ian@liberator.(none)>
Wed, 18 Nov 2009 21:10:07 +0000 (21:10 +0000)
yarrg/pages.c

index 28364a0..955add9 100644 (file)
@@ -61,17 +61,132 @@ DEBUG_DEFINE_DEBUGF(pages)
    fatal("X11 operation unexpectedly failed."          \
         " %s:%d: %s\n", __FILE__,__LINE__,#what))
 
-static KeyCode keycode(KeySym sym) {
-  return XKeysymToKeycode(disp,sym);
+
+/*---------- keyboard map ----------*/
+
+#define KEYS(EACH_KEY)                         \
+  EACH_KEY(XK_slash)                           \
+  EACH_KEY(XK_w)                               \
+  EACH_KEY(XK_Return)                          \
+  EACH_KEY(XK_Prior)                           \
+  EACH_KEY(XK_Next)
+
+#define MAXMAPCOL 6
+typedef struct {
+  int len;
+  int kc[3];
+} MappedKey;
+
+#define KEY_MAP_DEF(xk) \
+static MappedKey mk_##xk;
+KEYS(KEY_MAP_DEF)
+
+typedef struct {
+  int isdef; /* 'y' if defined; otherwise char indicating why not */
+  int kc;
+} MappedModifier;
+
+static int kc_min, kc_max;
+static MappedModifier mm_shift, mm_modeswitch, mm_isol3shift;
+static KeySym *mapping;
+static int mapwidth;
+
+static void keymap_lookup_modifier(MappedModifier *mm, KeySym sym,
+                                  char ifnot) {
+  int kc;
+
+  mm->isdef= ifnot;
+  for (kc=kc_min; kc <= kc_max; kc++) {
+    if (mapping[(kc-kc_min)*mapwidth] == sym) {
+      mm->isdef= 'y';
+      mm->kc= kc;
+      return;
+    }
+  }
 }
 
-void screenshot_startup(void) {
-  progress("starting...");
-  disp= XOpenDisplay(0);
-  if (!disp) fatal("Unable to open X11 display.");
-  sysassert(! gettimeofday(&tv_startup,0) );
+static void keymap_lookup_key(MappedKey *mk, KeySym sym, const char *what) {
+  const MappedModifier *shift, *xshift;
+  int col, kc;
+  char cols[MAXMAPCOL+1];
+
+  memset(cols,'.',MAXMAPCOL);
+  cols[MAXMAPCOL]= 0;
+
+  for (col=0; col<mapwidth && col<MAXMAPCOL; col++) {
+
+#define CHECK_SHIFT(sh) do{                    \
+      if ((sh) && (sh)->isdef!='y') {          \
+       cols[col]= (sh)->isdef;                 \
+       continue;                               \
+      }                                                \
+    }while(0)
+
+    shift= col & 1 ? &mm_shift : 0;
+    CHECK_SHIFT(shift);
+
+    xshift= col >= 4 ? &mm_isol3shift :
+            col >= 2 ? &mm_modeswitch :
+            0;
+    CHECK_SHIFT(xshift);
+
+    for (kc=kc_min; kc <= kc_max; kc++) {
+      if (mapping[(kc-kc_min)*mapwidth + col] == sym)
+       goto found;
+    }
+    cols[col]= '_';
+  }
+  fprintf(stderr,"\n"
+         "Unable to find a key to press to generate %s.\n"
+         "(Technical details: cols:%s sh:%c:%d modesw:%c:%d isol3:%c:%d)\n",
+         what, cols,
+#define SH_DETAILS(sh) mm_##sh.isdef, mm_##sh.kc
+         SH_DETAILS(shift), SH_DETAILS(modeswitch), SH_DETAILS(isol3shift));
+  fatal("Keymap is unsuitable!");
+
+ found:;
+  int *fill= mk->kc;
+  
+  if (xshift) *fill++ = xshift->kc;
+  if (shift)  *fill++ = shift->kc;
+  *fill += kc;
+  mk->len= fill - mk->kc;
+}
+
+static void keymap_startup(void) {
+  xassert( XDisplayKeycodes(disp,&kc_min,&kc_max) );
+  xassert( mapping= XGetKeyboardMapping(disp, kc_min, kc_max-kc_min+1,
+                                       &mapwidth) );
+
+  XModifierKeymap *modmap;
+  xassert( modmap= XGetModifierMapping(disp) );
+
+  /* find a shift keycode */
+  mm_shift.isdef= 'x';
+  int modent;
+  for (modent=0; modent<modmap->max_keypermod; modent++) {
+    KeySym shiftsym= modmap->modifiermap[modent];
+    if (shiftsym==NoSymbol) continue;
+    keymap_lookup_modifier(&mm_shift, shiftsym, 's');
+    if (mm_shift.isdef!='y') break;
+  }
+
+  /* find keycodes for mode_switch (column+=2) and ISO L3 shift (column+=4) */
+  keymap_lookup_modifier(&mm_modeswitch, XK_Mode_switch,      0);
+  keymap_lookup_modifier(&mm_isol3shift, XK_ISO_Level3_Shift, 0);
+  
+  XFreeModifiermap(modmap);
+
+#define KEY_MAP_LOOKUP(xk) \
+  keymap_lookup_key(&mk_##xk, xk, #xk);
+  KEYS(KEY_MAP_LOOKUP)
+
+  XFree(mapping);
+  mapping= 0;
 }
 
+#define SEND_KEY(xk) (send_key(&mk_##xk))
+
 /*---------- pager ----------*/
 
 typedef RgbImage Snapshot;
@@ -176,10 +291,11 @@ static void check_pointer_not_disturbed(void) {
   }
 }
 
-static void send_key(KeySym sym) {
+static void send_key(MappedKey *mk) {
+  int i;
   check_not_disturbed();
-  XTestFakeKeyEvent(disp, keycode(sym),1, 0);
-  XTestFakeKeyEvent(disp, keycode(sym),0, 0);
+  for (i=0; i<mk->len; i++) XTestFakeKeyEvent(disp, mk->kc[i], 1, 0);
+  for (i=mk->len; --i>=0; ) XTestFakeKeyEvent(disp, mk->kc[i], 0, 0);
 }
 static void send_mouse_1_updown_here(void) {
   check_not_disturbed();
@@ -207,7 +323,7 @@ static int pgupdown;
 static void send_pgup_many(void) {
   int i;
   for (i=0; i<25; i++) {
-    send_key(XK_Prior);
+    SEND_KEY(XK_Prior);
     pgupdown--;
   }
   debugf("PAGING   PageUp x %d\n",i);
@@ -216,7 +332,7 @@ static void send_pgup_many(void) {
 static void send_pgdown_torestore(void) {
   debugf("PAGING   PageDown x %d\n", -pgupdown);
   while (pgupdown < 0) {
-    send_key(XK_Next);
+    SEND_KEY(XK_Next);
     pgupdown++;
   }
   sync_after_input();
@@ -589,9 +705,9 @@ static void prepare_ypp_client(void) {
   send_mouse_1_updown_here();
   sync_after_input();
   check_not_disturbed();
-  send_key(XK_slash);
-  send_key(XK_w);
-  send_key(XK_Return);
+  SEND_KEY(XK_slash);
+  SEND_KEY(XK_w);
+  SEND_KEY(XK_Return);
   sync_after_input();
 
   Snapshot *status=0;
@@ -793,3 +909,12 @@ void find_yppclient_window(void) {
          " Use --window-id and/or report this as a fault.\n",
          o_ocean || o_pirate ? "matching ": "");
 }
+
+void screenshot_startup(void) {
+  progress("starting...");
+  disp= XOpenDisplay(0);
+  if (!disp) fatal("Unable to open X11 display.");
+  sysassert(! gettimeofday(&tv_startup,0) );
+
+  keymap_startup();
+}