*** empty log message ***
[dana/urxvt.git] / src / keyboard.C
index 07cdeac..a307437 100644 (file)
@@ -1,46 +1,45 @@
 #include "../config.h"
 #include "rxvt.h"
-#include "keyboard.h"
-#include "command.h"
-#include <string.h>
-#include <X11/X.h>
 
 #ifdef KEYSYM_RESOURCE
 
+#include <cstring>
+
+#include "keyboard.h"
+#include "command.h"
+
 ////////////////////////////////////////////////////////////////////////////////
 // default keycode translation map and keyevent handlers
 
 keysym_t keyboard_manager::stock_keymap[] = {
   /* examples */
-  /*        keysym,                state, range,        handler,             str */
-//{XK_ISO_Left_Tab,                    0,     1,         NORMAL,           "\033[Z"},
-//{            'a',                    0,    26,    RANGE_META8,           "a" "%c"},
-//{            'a',          ControlMask,    26,    RANGE_META8,          "\ 1" "%c"},
-//{        XK_Left,                    0,     4,           LIST,   "DACBZ" "\033[Z"},
-//{        XK_Left,            ShiftMask,     4,           LIST,   "dacbZ" "\033[Z"},
-//{        XK_Left,          ControlMask,     4,           LIST,   "dacbZ" "\033OZ"},
-//{         XK_Tab,          ControlMask,     1,         NORMAL,      "\033<C-Tab>"},
-//{  XK_apostrophe,          ControlMask,     1,         NORMAL,        "\033<C-'>"},
-//{       XK_slash,          ControlMask,     1,         NORMAL,        "\033<C-/>"},
-//{   XK_semicolon,          ControlMask,     1,         NORMAL,        "\033<C-;>"},
-//{       XK_grave,          ControlMask,     1,         NORMAL,        "\033<C-`>"},
-//{       XK_comma,          ControlMask,     1,         NORMAL,     "\033<C-\054>"},
-//{      XK_Return,          ControlMask,     1,         NORMAL,    "\033<C-Return>"},
-//{      XK_Return,            ShiftMask,     1,         NORMAL,    "\033<S-Return>"},
-//{            ' ',            ShiftMask,     1,         NORMAL,    "\033<S-Space>"},
-//{            '.',          ControlMask,     1,         NORMAL,        "\033<C-.>"},
-//{            '0',          ControlMask,    10,          RANGE,   "0" "\033<C-%c>"},
-//{            '0', MetaMask|ControlMask,    10,          RANGE, "0" "\033<M-C-%c>"},
-//{            'a', MetaMask|ControlMask,    26,          RANGE, "a" "\033<M-C-%c>"},
+  /*        keysym,                state, range,               handler,              str */
+//{XK_ISO_Left_Tab,                    0,     1,      keysym_t::NORMAL,           "\033[Z"},
+//{            'a',                    0,    26, keysym_t::RANGE_META8,           "a" "%c"},
+//{            'a',          ControlMask,    26, keysym_t::RANGE_META8,          "\ 1" "%c"},
+//{        XK_Left,                    0,     4,        keysym_t::LIST,     ".\033[.DACB."},
+//{        XK_Left,            ShiftMask,     4,        keysym_t::LIST,     ".\033[.dacb."},
+//{        XK_Left,          ControlMask,     4,        keysym_t::LIST,     ".\033O.dacb."},
+//{         XK_Tab,          ControlMask,     1,      keysym_t::NORMAL,      "\033<C-Tab>"},
+//{  XK_apostrophe,          ControlMask,     1,      keysym_t::NORMAL,        "\033<C-'>"},
+//{       XK_slash,          ControlMask,     1,      keysym_t::NORMAL,        "\033<C-/>"},
+//{   XK_semicolon,          ControlMask,     1,      keysym_t::NORMAL,        "\033<C-;>"},
+//{       XK_grave,          ControlMask,     1,      keysym_t::NORMAL,        "\033<C-`>"},
+//{       XK_comma,          ControlMask,     1,      keysym_t::NORMAL,     "\033<C-\054>"},
+//{      XK_Return,          ControlMask,     1,      keysym_t::NORMAL,   "\033<C-Return>"},
+//{      XK_Return,            ShiftMask,     1,      keysym_t::NORMAL,   "\033<S-Return>"},
+//{            ' ',            ShiftMask,     1,      keysym_t::NORMAL,    "\033<S-Space>"},
+//{            '.',          ControlMask,     1,      keysym_t::NORMAL,        "\033<C-.>"},
+//{            '0',          ControlMask,    10,       keysym_t::RANGE,   "0" "\033<C-%c>"},
+//{            '0', MetaMask|ControlMask,    10,       keysym_t::RANGE, "0" "\033<M-C-%c>"},
+//{            'a', MetaMask|ControlMask,    26,       keysym_t::RANGE, "a" "\033<M-C-%c>"},
 };
 
 static void
 output_string (rxvt_term *rt, const char *str)
 {
-  assert (rt && str);
-
-  if (strncmp (str, "proto:", 6) == 0)
-    rt->cmd_write ((unsigned char *)str + 6, strlen (str) - 6);
+  if (strncmp (str, "command:", 8) == 0)
+    rt->cmd_write ((unsigned char *)str + 8, strlen (str) - 8);
   else
     rt->tt_write ((unsigned char *)str, strlen (str));
 }
@@ -59,8 +58,7 @@ output_string_meta8 (rxvt_term *rt, unsigned int state, char *buf, int buflen)
       else if (rt->meta_char == C0_ESC)        /* escape prefix */
 #endif
         {
-          const unsigned char
-            ch = C0_ESC;
+          const unsigned char ch = C0_ESC;
           rt->tt_write (&ch, 1);
         }
     }
@@ -71,16 +69,12 @@ output_string_meta8 (rxvt_term *rt, unsigned int state, char *buf, int buflen)
 static int
 format_keyrange_string (const char *str, int keysym_offset, char *buf, int bufsize)
 {
-  int len = snprintf (buf, bufsize, str + 1, keysym_offset + str [0]);
+  size_t len = snprintf (buf, bufsize, str + 1, keysym_offset + str [0]);
 
-  if (len >= bufsize)
+  if (len >= (size_t)bufsize)
     {
-      fprintf (stderr, "buffer overflowed!\n");
-      buf[bufsize - 1] = '\0';
-    }
-  else if (len < 0)
-    {
-      perror ("keyrange_translator()");
+      rxvt_warn ("format_keyrange_string: formatting failed, ignoring key.\n");
+      *buf = 0;
     }
 
   return len;
@@ -88,23 +82,25 @@ format_keyrange_string (const char *str, int keysym_offset, char *buf, int bufsi
 
 ////////////////////////////////////////////////////////////////////////////////
 // return: #bits of '1'
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
+# define bitcount(n) (__extension__ ({ uint32_t n__ = (n); __builtin_popcount (n); }))
+#else
 static int
-bitcount (unsigned int n)
+bitcount (uint16_t n)
 {
   int i;
 
-  for (i = 0; n; ++i, n &= (n - 1))
+  for (i = 0; n; ++i, n &= n - 1)
     ;
 
   return i;
 }
+#endif
 
 // return: priority_of_a - priority_of_b
 static int
 compare_priority (keysym_t *a, keysym_t *b)
 {
-  assert (a && b);
-
   // (the more '1's in state; the less range): the greater priority
   int ca = bitcount (a->state /* & OtherModMask */);
   int cb = bitcount (b->state /* & OtherModMask */);
@@ -159,8 +155,6 @@ keyboard_manager::clear ()
 void
 keyboard_manager::register_user_translation (KeySym keysym, unsigned int state, const char *trans)
 {
-  assert (trans);
-
   keysym_t *key = new keysym_t;
   wchar_t *wc = rxvt_mbstowcs (trans);
   const char *translation = rxvt_wcstoutf8 (wc);
@@ -187,12 +181,8 @@ keyboard_manager::register_user_translation (KeySym keysym, unsigned int state,
               strcpy (translation, translation + 4);
             }
           else
-            {
-              key->range = 1;
-              rxvt_warn ("cannot parse list-type keysym '%s', treating as normal keysym.\n", translation);
-            }
+            rxvt_warn ("cannot parse list-type keysym '%s', treating as normal keysym.\n", translation);
         }
-      else
 
       user_keymap.push_back (key);
       user_translations.push_back (translation);
@@ -209,9 +199,6 @@ keyboard_manager::register_user_translation (KeySym keysym, unsigned int state,
 void
 keyboard_manager::register_keymap (keysym_t *key)
 {
-  assert (key);
-  assert (key->range >= 1);
-
   if (keymap.size () == keymap.capacity ())
     keymap.reserve (keymap.size () * 2);
 
@@ -238,11 +225,9 @@ keyboard_manager::dispatch (rxvt_term *term, KeySym keysym, unsigned int state)
 {
   assert (hash[0] == 0 && "register_done() need to be called");
 
-  if (state & term->ModMetaMask)
-    state |= MetaMask;
-
-  if (state & term->ModNumLockMask)
-    state |= NumLockMask;
+  if (state & term->ModMetaMask)    state |= MetaMask;
+  if (state & term->ModNumLockMask) state |= NumLockMask;
+  if (state & term->ModLevel3Mask)  state |= Level3Mask;
 
   if (!!(term->priv_modes & PrivMode_aplKP) != !!(state & ShiftMask))
     state |= AppKeypadMask;
@@ -251,14 +236,13 @@ keyboard_manager::dispatch (rxvt_term *term, KeySym keysym, unsigned int state)
 
   if (index >= 0)
     {
-      assert (term && keymap [index]);
       const keysym_t &key = *keymap [index];
 
       int keysym_offset = keysym - key.keysym;
 
       wchar_t *wc = rxvt_utf8towcs (key.str);
       char *str = rxvt_wcstombs (wc);
-      // TODO: do translations, unescaping etc, here (allow \u escape etc.)
+      // TODO: do (some) translations, unescaping etc, here (allow \u escape etc.)
       free (wc);
 
       switch (key.type)
@@ -311,10 +295,7 @@ keyboard_manager::dispatch (rxvt_term *term, KeySym keysym, unsigned int state)
       return true;
     }
   else
-    {
-      // fprintf(stderr,"[%x:%x]",state,keysym);
-      return false;
-    }
+    return false;
 }
 
 // purge duplicate keymap entries
@@ -334,6 +315,7 @@ void keyboard_manager::purge_duplicate_keymap ()
                   keymap[i] = keymap.back ();
                   keymap.pop_back ();
                 }
+
               break;
             }
         }
@@ -351,28 +333,13 @@ keyboard_manager::setup_hash ()
   memset (hash_budget_size, 0, sizeof (hash_budget_size));
   memset (hash_budget_counter, 0, sizeof (hash_budget_counter));
 
-  // count keysyms for corresponding hash budgets
+  // determine hash bucket size
   for (i = 0; i < keymap.size (); ++i)
-    {
-      assert (keymap [i]);
-      hashkey = (keymap [i]->keysym & KEYSYM_HASH_MASK);
-      ++hash_budget_size [hashkey];
-    }
-
-  // keysym A with range>1 is counted one more time for
-  // every keysym B lies in its range
-  for (i = 0; i < keymap.size (); ++i)
-    {
-      if (keymap[i]->range > 1)
-        {
-          for (int j = min (keymap [i]->range, KEYSYM_HASH_BUDGETS) - 1; j > 0; --j)
-            {
-              hashkey = ((keymap [i]->keysym + j) & KEYSYM_HASH_MASK);
-              if (hash_budget_size [hashkey])
-                ++hash_budget_size [hashkey];
-            }
-        }
-    }
+    for (int j = min (keymap [i]->range, KEYSYM_HASH_BUDGETS) - 1; j >= 0; --j)
+      {
+        hashkey = (keymap [i]->keysym + j) & KEYSYM_HASH_MASK;
+        ++hash_budget_size [hashkey];
+      }
 
   // now we know the size of each budget
   // compute the index of each budget
@@ -380,37 +347,31 @@ keyboard_manager::setup_hash ()
   for (index = 0, i = 1; i < KEYSYM_HASH_BUDGETS; ++i)
     {
       index += hash_budget_size [i - 1];
-      hash[i] = (hash_budget_size [i] ? index : hash [i - 1]);
+      hash [i] = index;
     }
 
   // and allocate just enough space
-  //sorted_keymap.reserve (hash[i - 1] + hash_budget_size[i - 1]);
   sorted_keymap.insert (sorted_keymap.begin (), index + hash_budget_size [i - 1], 0);
 
   // fill in sorted_keymap
   // it is sorted in each budget
   for (i = 0; i < keymap.size (); ++i)
-    {
-      for (int j = min (keymap [i]->range, KEYSYM_HASH_BUDGETS) - 1; j >= 0; --j)
-        {
-          hashkey = ((keymap [i]->keysym + j) & KEYSYM_HASH_MASK);
+    for (int j = min (keymap [i]->range, KEYSYM_HASH_BUDGETS) - 1; j >= 0; --j)
+      {
+        hashkey = (keymap [i]->keysym + j) & KEYSYM_HASH_MASK;
 
-          if (hash_budget_size [hashkey])
-            {
-              index = hash [hashkey] + hash_budget_counter [hashkey];
+        index = hash [hashkey] + hash_budget_counter [hashkey];
 
-              while (index > hash [hashkey]
-                     && compare_priority (keymap [i], sorted_keymap [index - 1]) > 0)
-                {
-                  sorted_keymap [index] = sorted_keymap [index - 1];
-                  --index;
-                }
+        while (index > hash [hashkey]
+               && compare_priority (keymap [i], sorted_keymap [index - 1]) > 0)
+          {
+            sorted_keymap [index] = sorted_keymap [index - 1];
+            --index;
+          }
 
-              sorted_keymap [index] = keymap [i];
-              ++hash_budget_counter [hashkey];
-            }
-        }
-    }
+        sorted_keymap [index] = keymap [i];
+        ++hash_budget_counter [hashkey];
+      }
 
   keymap.swap (sorted_keymap);
 
@@ -436,11 +397,12 @@ keyboard_manager::setup_hash ()
       keysym_t *a = sorted_keymap[i];
       for (int j = 0; j < a->range; ++j)
         {
-          int index = find_keysym (a->keysym + j, a->state & OtherModMask);
+          int index = find_keysym (a->keysym + j, a->state);
+
           assert (index >= 0);
           keysym_t *b = keymap [index];
           assert (i == (signed) index ||       // the normally expected result
-            (a->keysym + j) >= b->keysym && (a->keysym + j) <= (b->keysym + b->range) && compare_priority (a, b) <= 0);        // is effectively the same
+            (a->keysym + j) >= b->keysym && (a->keysym + j) <= (b->keysym + b->range) && compare_priority (a, b) <= 0);        // is effectively the same or a closer match
         }
     }
 #endif
@@ -451,18 +413,18 @@ keyboard_manager::find_keysym (KeySym keysym, unsigned int state)
 {
   int hashkey = keysym & KEYSYM_HASH_MASK;
   unsigned int index = hash [hashkey];
+  unsigned int end = hashkey < KEYSYM_HASH_BUDGETS - 1
+                     ? hash [hashkey + 1] 
+                     : keymap.size ();
 
-  for (; index < keymap.size (); ++index)
+  for (; index < end; ++index)
     {
       keysym_t *key = keymap [index];
-      assert (key);
 
-      if (key->keysym <= keysym && key->keysym + key->range > keysym
+      if (key->keysym <= keysym && keysym < key->keysym + key->range
           // match only the specified bits in state and ignore others
-          && (key->state & OtherModMask) == (key->state & state))
+          && (key->state & state) == key->state)
         return index;
-      else if (key->keysym > keysym && key->range == 1)
-        return -1;
     }
 
   return -1;