Find interpretation of modifier masks through the X server's modifier
authorTaylor R. Campbell <net/mumble/campbell>
Sat, 21 Oct 2006 16:05:58 +0000 (16:05 +0000)
committerTaylor R. Campbell <net/mumble/campbell>
Sat, 21 Oct 2006 16:05:58 +0000 (16:05 +0000)
mapping and keyboard mapping by finding which modifier masks are
associated with keycodes mapped to the keysyms for modifier keys.  This
now follows the ICCCM, rather than assuming that Mod1 means Meta, Mod2
means Super, and so on.  Edwin also now recognizes Super and Hyper in
key presses as a result; before it would ignore any modifier masks
other than Control and Mod1 (Meta).

v7/src/microcode/object.h
v7/src/microcode/x11.h
v7/src/microcode/x11base.c

index ed57eb64b7c8aa64dc258aae095e951a7c2c17ba..cfccb9871890f3541cddea5e7a90684b6f6a699c 100644 (file)
@@ -1,6 +1,6 @@
 /* -*-C-*-
 
-$Id: object.h,v 9.60 2006/09/16 11:19:09 gjr Exp $
+$Id: object.h,v 9.61 2006/10/21 16:05:58 riastradh Exp $
 
 Copyright 1986,1987,1988,1989,1990,1992 Massachusetts Institute of Technology
 Copyright 1993,1995,1997,1998,2000,2001 Massachusetts Institute of Technology
@@ -352,7 +352,8 @@ extern SCHEME_OBJECT * memory_base;
 
 #define CHAR_BITS_META                 0x1
 #define CHAR_BITS_CONTROL      0x2
-#define CHAR_BITS_CONTROL_META 0x3
+#define CHAR_BITS_SUPER                0x4
+#define CHAR_BITS_HYPER                0x8
 
 #define MAX_ASCII (1L << ASCII_LENGTH)
 #define MAX_CODE (1L << CODE_LENGTH)
index a8e8d9e36a23b3eadaffad942c400883c301612c..0f18b6654e36b5c4b43a31c8f36b70f367d6a5f8 100644 (file)
@@ -1,9 +1,9 @@
 /* -*-C-*-
 
-$Id: x11.h,v 1.20 2005/11/12 22:53:29 cph Exp $
+$Id: x11.h,v 1.21 2006/10/21 16:05:58 riastradh Exp $
 
 Copyright 1989,1990,1991,1992,1993,2000 Massachusetts Institute of Technology
-Copyright 2005 Massachusetts Institute of Technology
+Copyright 2005,2006 Massachusetts Institute of Technology
 
 This file is part of MIT/GNU Scheme.
 
@@ -42,6 +42,22 @@ struct xdisplay
   XEvent cached_event;
   char cached_event_p;
 
+  /* X key events have 8-bit modifier masks, three bits of which are
+     defined to be Shift, Lock, and Control, identified with ShiftMask,
+     LockMask, and ControlMask; and five bits of which are unspecified
+     named only mod1 to mod5.  Which ones mean Meta, Super, Hyper, &c.,
+     vary from system to system, however, so, on initializing the display
+     record, we grovel through some tables (XGetKeyboardMapping and
+     XGetModifierMapping) to find which ones the various modifier
+     keysyms are assigned to, and cache them here.
+
+     Scheme knows about Shift, Control, Meta, Super, and Hyper.  Of
+     these, only Meta, Super, and Hyper are identified by numbered
+     modifier masks.  All other modifiers are ignored. */
+  int modifier_mask_meta;
+  int modifier_mask_super;
+  int modifier_mask_hyper;
+
   /* The type of window manager we have.  If we move FRAME_OUTER_WINDOW
      to x/y 0/0, some window managers (type A) puts the window manager
      decorations outside the screen and FRAME_OUTER_WINDOW exactly at 0/0.
@@ -64,9 +80,28 @@ struct xdisplay
 #define XD_WM_TAKE_FOCUS(xd) ((xd) -> wm_take_focus)
 #define XD_CACHED_EVENT(xd) ((xd) -> cached_event)
 #define XD_CACHED_EVENT_P(xd) ((xd) -> cached_event_p)
+#define XD_MODIFIER_MASK_SHIFT(xd) (ShiftMask)
+#define XD_MODIFIER_MASK_CONTROL(xd) (ControlMask)
+#define XD_MODIFIER_MASK_LOCK(xd) (LockMask)
+#define XD_MODIFIER_MASK_META(xd) ((xd) -> modifier_mask_meta)
+#define XD_MODIFIER_MASK_SUPER(xd) ((xd) -> modifier_mask_super)
+#define XD_MODIFIER_MASK_HYPER(xd) ((xd) -> modifier_mask_hyper)
 #define XD_WM_TYPE(xd) ((xd) -> wm_type)
 #define XD_TO_OBJECT(xd) (LONG_TO_UNSIGNED_FIXNUM (XD_ALLOCATION_INDEX (xd)))
 
+#define X_MODIFIER_MASK_SHIFT_P(modifier_mask, xd) \
+  ((modifier_mask) & (XD_MODIFIER_MASK_SHIFT (xd)))
+#define X_MODIFIER_MASK_CONTROL_P(modifier_mask, xd) \
+  ((modifier_mask) & (XD_MODIFIER_MASK_CONTROL (xd)))
+#define X_MODIFIER_MASK_LOCK_P(modifier_mask, xd) \
+  ((modifier_mask) & (XD_MODIFIER_MASK_LOCK (xd)))
+#define X_MODIFIER_MASK_META_P(modifier_mask, xd) \
+  ((modifier_mask) & (XD_MODIFIER_MASK_META (xd)))
+#define X_MODIFIER_MASK_SUPER_P(modifier_mask, xd) \
+  ((modifier_mask) & (XD_MODIFIER_MASK_SUPER (xd)))
+#define X_MODIFIER_MASK_HYPER_P(modifier_mask, xd) \
+  ((modifier_mask) & (XD_MODIFIER_MASK_HYPER (xd)))
+
 extern struct xdisplay * EXFUN (x_display_arg, (unsigned int arg));
 
 struct drawing_attributes
index b5bcf686c183eeef1b05fbf0cba1c6450ca4ee35..7650d056d53eafe583384ee810432dff1e7c93ef 100644 (file)
@@ -1,10 +1,10 @@
 /* -*-C-*-
 
-$Id: x11base.c,v 1.85 2005/11/16 04:00:16 cph Exp $
+$Id: x11base.c,v 1.86 2006/10/21 16:05:58 riastradh Exp $
 
 Copyright 1989,1990,1991,1992,1993,1994 Massachusetts Institute of Technology
 Copyright 1995,1996,1997,1998,2000,2001 Massachusetts Institute of Technology
-Copyright 2003,2004,2005 Massachusetts Institute of Technology
+Copyright 2003,2004,2005,2006 Massachusetts Institute of Technology
 
 This file is part of MIT/GNU Scheme.
 
@@ -565,7 +565,7 @@ get_wm_decor_geometry (struct xwindow * xw)
   return (decor != (XW_WINDOW (xw)));
 }
 \f
-/* Open/Close Windows and Displays */
+/* Open/Close Windows */
 
 #define MAKE_GC(gc, fore, back)                                                \
 {                                                                      \
@@ -669,6 +669,121 @@ DEFUN (x_close_window, (xw), struct xwindow * xw)
   XSetIOErrorHandler (x_io_error_handler);
   free (xw);
 }
+\f
+/* Initialize/Close Displays */
+
+#define MODIFIER_INDEX_TO_MASK(N) (1 << (N))
+
+/* Grovel through the X server's keycode and modifier mappings to find
+   out what we ought to interpret as Meta, Hyper, and Super, based on
+   what modifiers are associated with keycodes that are associated with
+   keysyms Meta_L, Meta_R, Alt_L, Alt_R, Hyper_L, &c.
+
+   Adapted from GNU Emacs. */
+
+static void
+DEFUN (x_initialize_display_modifier_masks, (xd), struct xdisplay * xd)
+{
+  int min_keycode;
+  int max_keycode;
+  XModifierKeymap * modifier_keymap;
+  KeyCode * modifier_to_keycodes_table;
+  int keycodes_per_modifier;
+  KeySym * keycode_to_keysyms_table;
+  int keysyms_per_keycode;
+
+  (XD_MODIFIER_MASK_META (xd)) = 0;
+  (XD_MODIFIER_MASK_SUPER (xd)) = 0;
+  (XD_MODIFIER_MASK_HYPER (xd)) = 0;
+
+  modifier_keymap = (XGetModifierMapping ((XD_DISPLAY (xd))));
+  modifier_to_keycodes_table = (modifier_keymap -> modifiermap);
+  keycodes_per_modifier = (modifier_keymap -> max_keypermod);
+
+  XDisplayKeycodes ((XD_DISPLAY (xd)), (& min_keycode), (& max_keycode));
+
+  keycode_to_keysyms_table =
+    (XGetKeyboardMapping ((XD_DISPLAY (xd)),
+                          min_keycode,
+                          (max_keycode - min_keycode + 1),
+                          (& keysyms_per_keycode)));
+
+  /* Go through each of the 8 non-preassigned modifiers, which start at
+     3 (Mod1), after Shift, Control, and Lock.  For each modifier, go
+     through all of the (non-zero) keycodes attached to it; for each
+     keycode, go through all of the keysyms attached to it; check each
+     keysym for the modifiers that we're interested in (Meta, Hyper,
+     and Super). */
+
+  {
+    int modifier_index;
+
+    for (modifier_index = 3; (modifier_index < 8); modifier_index += 1)
+      {
+        int modifier_mask = (MODIFIER_INDEX_TO_MASK (modifier_index));
+        KeyCode * keycodes =
+          (& (modifier_to_keycodes_table
+              [modifier_index * keycodes_per_modifier]));
+
+        /* This is a flag specifying whether the modifier has already
+           been identified as Meta, which takes precedence over Hyper
+           and Super.  (What about precedence between Hyper and
+           Super...?  This is GNU Emacs's behaviour.) */
+        int modifier_is_meta_p = 0;
+
+        int keycode_index;
+
+        for (keycode_index = 0;
+             (keycode_index < keycodes_per_modifier);
+             keycode_index += 1)
+          {
+            KeyCode keycode = (keycodes [keycode_index]);
+
+            if (keycode == 0)
+              continue;
+
+            {
+              int keysym_index;
+              KeySym * keysyms =
+                (& (keycode_to_keysyms_table
+                    [(keycode - min_keycode) * keysyms_per_keycode]));
+
+              for (keysym_index = 0;
+                   (keysym_index < keysyms_per_keycode);
+                   keysym_index += 1)
+                switch (keysyms [keysym_index])
+                  {
+                  case XK_Meta_L:
+                  case XK_Meta_R:
+                  case XK_Alt_L:
+                  case XK_Alt_R:
+                    modifier_is_meta_p = 1;
+                    (XD_MODIFIER_MASK_META (xd)) |= modifier_mask;
+                    break;
+
+                  case XK_Hyper_L:
+                  case XK_Hyper_R:
+                    if (! modifier_is_meta_p)
+                      (XD_MODIFIER_MASK_HYPER (xd)) |= modifier_mask;
+                    goto next_modifier;
+
+                  case XK_Super_L:
+                  case XK_Super_R:
+                    if (! modifier_is_meta_p)
+                      (XD_MODIFIER_MASK_SUPER (xd)) |= modifier_mask;
+                    goto next_modifier;
+                  }
+            }
+          }
+
+      next_modifier:
+        continue;
+      }
+  }
+
+  XFree (((char *) keycode_to_keysyms_table));
+  XFreeModifiermap (modifier_keymap);
+}
 
 static void
 DEFUN (x_close_display, (xd), struct xdisplay * xd)
@@ -1030,14 +1145,15 @@ DEFUN (button_event, (xw, event, type),
       default: button_number = 0; break;
       }
     if (button_number) {
+      struct xdisplay * xd = (XW_XD (xw));
       --button_number;
-      if ((event -> state) & ShiftMask) {
+      if (X_MODIFIER_MASK_SHIFT_P ((event -> state), xd)) {
        button_number += 5;
       }
-      if ((event -> state) & ControlMask) {
+      if (X_MODIFIER_MASK_CONTROL_P ((event -> state), xd)) {
        button_number += 10;
       }
-      if ((event -> state) & Mod1Mask) {
+      if (X_MODIFIER_MASK_META_P ((event -> state), xd)) {
        button_number += 20;
       }
       conversion = (LONG_TO_UNSIGNED_FIXNUM (button_number));
@@ -1050,29 +1166,47 @@ DEFUN (button_event, (xw, event, type),
   return (result);
 }
 
+/* This handles only the modifier bits that Scheme supports.
+   At the moment, these are Control, Meta, Super, and Hyper.
+   This might want to change if the character abstraction were ever to
+   change, or if the X11 interface were to be changed to use something
+   other than Scheme characters to convey key presses. */
+
 static SCHEME_OBJECT
-DEFUN (convert_bucky_bits, (state, allp), unsigned int state AND int allp)
+DEFUN (x_modifier_mask_to_bucky_bits, (mask, xd),
+       unsigned int mask AND
+       struct xdisplay * xd)
 {
   long bucky = 0;
-  if (state & Mod1Mask)    bucky |= 0x0001; /* meta */
-  if (state & ControlMask) bucky |= 0x0002; /* control */
-  if (allp)
-    {
-      if (state & Mod2Mask)    bucky |= 0x0004; /* super */
-      if (state & Mod3Mask)    bucky |= 0x0008; /* hyper */
-      if (state & ShiftMask)   bucky |= 0x0010;
-      if (state & LockMask)    bucky |= 0x0020;
-      if (state & Mod4Mask)    bucky |= 0x0040;
-      if (state & Mod5Mask)    bucky |= 0x0080;
-      if (state & Button1Mask) bucky |= 0x0100;
-      if (state & Button2Mask) bucky |= 0x0200;
-      if (state & Button3Mask) bucky |= 0x0400;
-      if (state & Button4Mask) bucky |= 0x0800;
-      if (state & Button5Mask) bucky |= 0x1000;
-    }
+  if (X_MODIFIER_MASK_CONTROL_P (mask, xd)) bucky |= CHAR_BITS_CONTROL;
+  if (X_MODIFIER_MASK_META_P    (mask, xd)) bucky |= CHAR_BITS_META;
+  if (X_MODIFIER_MASK_SUPER_P   (mask, xd)) bucky |= CHAR_BITS_SUPER;
+  if (X_MODIFIER_MASK_HYPER_P   (mask, xd)) bucky |= CHAR_BITS_HYPER;
   return (LONG_TO_UNSIGNED_FIXNUM (bucky));
 }
 
+/* I'm not sure why we have a function for this. */
+
+static SCHEME_OBJECT
+DEFUN (x_key_button_mask_to_scheme, (x_state), unsigned int x_state)
+{
+  long scheme_state = 0;
+  if (x_state & ControlMask) scheme_state |= 0x0001;
+  if (x_state & Mod1Mask)    scheme_state |= 0x0002;
+  if (x_state & Mod2Mask)    scheme_state |= 0x0004;
+  if (x_state & Mod3Mask)    scheme_state |= 0x0008;
+  if (x_state & ShiftMask)   scheme_state |= 0x0010;
+  if (x_state & LockMask)    scheme_state |= 0x0020;
+  if (x_state & Mod4Mask)    scheme_state |= 0x0040;
+  if (x_state & Mod5Mask)    scheme_state |= 0x0080;
+  if (x_state & Button1Mask) scheme_state |= 0x0100;
+  if (x_state & Button2Mask) scheme_state |= 0x0200;
+  if (x_state & Button3Mask) scheme_state |= 0x0400;
+  if (x_state & Button4Mask) scheme_state |= 0x0800;
+  if (x_state & Button5Mask) scheme_state |= 0x1000;
+  return (LONG_TO_UNSIGNED_FIXNUM (scheme_state));
+}
+
 static XComposeStatus compose_status;
 
 static SCHEME_OBJECT
@@ -1117,7 +1251,9 @@ DEFUN (key_event, (xw, event, type),
       /* Create Scheme bucky bits (kept independent of the character).
         X has already controlified, so Scheme may choose to ignore
         the control bucky bit.  */
-      VECTOR_SET (result, EVENT_1, (convert_bucky_bits ((event -> state), 0)));
+      VECTOR_SET (result, EVENT_1,
+                  (x_modifier_mask_to_bucky_bits ((event -> state),
+                                                  (XW_XD (xw)))));
       /* Move vendor-specific bit from bit 28 (zero-based) to bit 23
         so that all keysym values will fit in Scheme fixnums.  */
       VECTOR_SET
@@ -1165,7 +1301,8 @@ DEFUN (x_event_to_object, (event), XEvent * event)
          EVENT_INTEGER (result, EVENT_0, ((event -> xmotion) . x));
          EVENT_INTEGER (result, EVENT_1, ((event -> xmotion) . y));
          VECTOR_SET (result, EVENT_2,
-                     (convert_bucky_bits (((event -> xmotion) . state), 1)));
+                      (x_key_button_mask_to_scheme
+                       (((event -> xmotion) . state))));
        }
       break;
     case ConfigureNotify:
@@ -1547,6 +1684,7 @@ DEFINE_PRIMITIVE ("X-OPEN-DISPLAY", Prim_x_open_display, 1, 1, 0)
     (XD_WM_TAKE_FOCUS (xd)) =
       (XInternAtom ((XD_DISPLAY (xd)), "WM_TAKE_FOCUS", False));
     (XD_CACHED_EVENT_P (xd)) = 0;
+    x_initialize_display_modifier_masks (xd);
     XRebindKeysym ((XD_DISPLAY (xd)), XK_BackSpace, 0, 0, "\177", 1);
     PRIMITIVE_RETURN (XD_TO_OBJECT (xd));
   }
@@ -1859,7 +1997,7 @@ DEFINE_PRIMITIVE ("X-WINDOW-QUERY-POINTER", Prim_x_window_query_pointer, 1, 1, 0
     VECTOR_SET (result, 1, (long_to_integer (root_y)));
     VECTOR_SET (result, 2, (long_to_integer (win_x)));
     VECTOR_SET (result, 3, (long_to_integer (win_y)));
-    VECTOR_SET (result, 4, (convert_bucky_bits (keys_buttons, 1)));
+    VECTOR_SET (result, 4, (x_key_button_mask_to_scheme (keys_buttons)));
     PRIMITIVE_RETURN (result);
   }
 }