ratpoison-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[RP] [PATCH] binding key sequences, ver2


From: Dan Aloni
Subject: [RP] [PATCH] binding key sequences, ver2
Date: Thu Jun 26 19:50:12 2003
User-agent: Mutt/1.5.4i

On Thu, Jun 26, 2003 at 09:42:52PM +0300, Dan Aloni wrote:
> On Sun, May 25, 2003 at 04:14:18AM -0700, Shawn Betts wrote:
> 
> > You'll notice that none of them have been bound to keys. I *hope* to
> > get some more-like-emacs nested keymaps going so *hopefully* all this
> > group stuff will hang off of C-t g or somethin'.
> > 
> > bind "g '" gselect
> > bind "g space" gnext
> > [...]
> 
> Emacs-like nested keymaps? i.e, key sequences, like, 
> 'bind "d a n a l o n i" echo Dan Aloni, hey ho!'?
> 
> Oh, you mean the patch below?
> Preliminary, not much complete, but actually working!
> 
> (This adds key sequence bindings support to ratpoison (CVS). It's 
> incomplete for some reasons: these sequences are not printed 
> like they should in 'help' and friends. Plus I haven't checked what 
> happens when you bind 'A B' when 'A B C' is already bound and such 
> other contradictions. Anyway, in this 3 hours work I might have
> missed some stuff, but you know the code better than I do. I hope
> this is enough to get you going on this)

I got this even more complete now. 
 
 * 'help' now prints key sequences, though I didn't check what happens
   with the really long ones (probably overflow the column).
 * hitting an unknown key sequence properly prints about it.
 * the key sequence of 'help' is properly printed in the welcome message.
 * setting colliding bindings ("A B" vs "A B C") does not alert, but the order
   determines what takes precedence.  If "A B" is bound before "A B C", then
   "A B C" is ignored and visa verse. We can reverse it if we change
   the scanning order of key_actions.
   
diff -x '*~' -urN ratpoison/src/actions.c ratpoison-mod/src/actions.c
--- ratpoison/src/actions.c     2003-06-26 04:47:59.000000000 +0300
+++ ratpoison-mod/src/actions.c 2003-06-27 05:18:57.000000000 +0300
@@ -169,19 +169,110 @@
   return NULL;
 }
 
-rp_action*
-find_keybinding (KeySym keysym, int state)
+find_keybinding_rc_t
+find_keybinding_by_number (struct rp_key *keys, int num_keys, int *action_out)
 {
   int i;
+
   for (i = 0; i < key_actions_last; i++)
     {
-      if (key_actions[i].key == keysym 
-         && key_actions[i].state == state)
-       return &key_actions[i];
+      struct rp_action *action = &key_actions[i];
+      int j = 0;
+
+      while (action->key == keys[j].sym  &&  
+            action->state == keys[j].state)
+       {
+         ++j;
+
+         if (j == num_keys) 
+           {
+             if (action->next != NULL) 
+               {
+                 return FIND_KEYBINDING_RC_NEEDS_MORE;
+               }
+
+             *action_out = i;
+             
+             return FIND_KEYBINDING_RC_FOUND;
+           }
+         else if (action->next == NULL)
+           {
+             break;
+           }
+         
+         action = action->next;
+       }
+    }
+
+  return FIND_KEYBINDING_RC_NOT_FOUND;
+}
+
+find_keybinding_rc_t
+find_keybinding_extended (struct rp_key *keys, int num_keys, rp_action 
**action_out)
+{
+  int num_action_out;
+  find_keybinding_rc_t rc;
+
+  rc = find_keybinding_by_number (keys, num_keys, &num_action_out);
+
+  if (rc == FIND_KEYBINDING_RC_FOUND)
+    {
+      *action_out = &key_actions[num_action_out];
     }
+
+  return rc;
+}
+
+rp_action*
+find_keybinding (KeySym code, unsigned int state)
+{
+  rp_action* ret;
+  find_keybinding_rc_t rc;
+  rp_key key;
+
+  key.sym = code;
+  key.state = state;
+
+  rc = find_keybinding_extended (&key, 1, &ret);
+  if (rc == FIND_KEYBINDING_RC_FOUND)
+    return ret;
+
   return NULL;
 }
 
+char *
+keybinding_to_string (rp_action *action)
+{
+  struct sbuf *s;
+  char *ret;
+  int index = 0;
+  char *keysym_name;
+
+  s = sbuf_new (0);
+
+  while (action != NULL) 
+    {
+      keysym_name = keysym_to_string (action->key, action->state);
+
+      if (index == 0)
+       sbuf_printf_concat (s, "%s", keysym_name);
+      else
+       sbuf_printf_concat (s, " %s", keysym_name);
+
+      free(keysym_name);
+
+      index += 1;
+
+      action = action->next;
+    }
+
+  ret = sbuf_get(s);
+
+  free(s);
+
+  return ret;
+}
+
 static char *
 find_command_by_keydesc (char *desc)
 {
@@ -190,7 +281,7 @@
 
   while (i < key_actions_last)
     {
-      keysym_name = keysym_to_string (key_actions[i].key, 
key_actions[i].state);
+      keysym_name = keybinding_to_string (&key_actions[i]);
       if (!strcmp (keysym_name, desc))
         {
           free (keysym_name);
@@ -235,6 +326,7 @@
 
   key_actions[key_actions_last].key = keysym;
   key_actions[key_actions_last].state = state;
+  key_actions[key_actions_last].next = NULL;
   key_actions[key_actions_last].data = xstrdup (cmd); /* free this on
                                                         shutdown, or
                                                         re/unbinding */
@@ -242,6 +334,33 @@
   ++key_actions_last;
 }
 
+
+static void
+add_keybinding_extended (rp_key *keys, int num_keys, char *cmd)
+{
+  rp_action *action;
+
+  add_keybinding(keys->sym, keys->state, cmd);
+
+  action = &key_actions[key_actions_last-1];
+  
+  while (num_keys > 1) 
+    {
+      rp_action *new_action = xmalloc(sizeof(rp_action));
+
+      keys++;
+      num_keys--;
+      
+      new_action->key = keys->sym;
+      new_action->state = keys->state;
+      new_action->next = NULL;
+      new_action->data = NULL;
+
+      action->next = new_action;
+      action = new_action;
+    }
+}
+
 static void
 replace_keybinding (rp_action *key_action, char *newcmd)
 {
@@ -252,24 +371,29 @@
 }
 
 static int
-remove_keybinding (KeySym keysym, int state)
+remove_keybinding (rp_key *keys, int num_keys)
 {
-  int i;
   int found = -1;
+  find_keybinding_rc_t rc;
 
-  for (i=0; i<key_actions_last; i++)
-    {
-      if (key_actions[i].key == keysym && key_actions[i].state == state)
-       {
-         found = i;
-         break;
-       }
-    }
+  rc = find_keybinding_by_number (keys, num_keys, &found);
 
-  if (found >= 0)
+  if (rc == FIND_KEYBINDING_RC_FOUND)
     {
-      free (key_actions[found].data);
+      rp_action *key_action_walk = &key_actions[found];
 
+      free(key_action_walk->data);
+           
+      key_action_walk = key_action_walk->next;
+      while (key_action_walk != NULL)
+       {
+         rp_action *key_action_temp;
+         
+         key_action_temp = key_action_walk->next;
+         free(key_action_walk);
+         key_action_walk = key_action_temp;
+       } 
+       
       memmove (&key_actions[found], &key_actions[found+1], 
               sizeof (rp_action) * (key_actions_last - found - 1));
       key_actions_last--;
@@ -370,6 +494,18 @@
   /* Free the data in the actions. */
   for (i=0; i<key_actions_last; i++)
     {
+      rp_action *key_action_walk = &key_actions[i];
+
+      key_action_walk = key_action_walk->next;
+      while (key_action_walk != NULL)
+       {
+         rp_action *key_action_temp;
+         
+         key_action_temp = key_action_walk->next;
+         free(key_action_walk);
+         key_action_walk = key_action_temp;
+       } 
+
       free (key_actions[i].data);
     }
 
@@ -496,6 +632,151 @@
     return p;
 }
 
+/* 
+ * From a given string extract the first paramter. For example: 
+ *    'abc defg hijk' -> 'abc'. 
+ *
+ * It handles \"'s. For example: 
+ *    '"abc dfg" hijk' -> 'abc dfg'.
+ *
+ * Optionally, if next_param is not NULL, return a pointer to 
+ * the next paramter in *next_param. *next_param will be NULL 
+ * if there are no more paramters.
+ */ 
+
+char *
+parse_single_parameter(char *data, char **next_param_out)
+{
+  char *param;
+  char *next_param;
+  char *arg_terminator;
+  int arg_len;
+  
+  /* 
+   * Test if it's a quoted paramter (i.e starts with ")
+   * maybe this should be more elaborated in the future 
+   */
+
+  if (data[0] == '"')
+    {
+      /* Look for the other " */
+      arg_terminator = strchr (&data[1], '"');
+      if (arg_terminator == NULL)
+       {
+         message (" error: missing \" ");
+         return NULL;
+       }
+
+      next_param = arg_terminator;
+      arg_len = (arg_terminator - data) - 1;
+
+      param = (char*) xmalloc (arg_len + 1);
+
+      /* Copy and NULL terminate */
+      memcpy(param, &data[1], arg_len);
+      param[arg_len] = '\0';
+
+      ++arg_terminator;
+    }
+  else 
+    {
+      /* Look for the next space */
+      arg_terminator = strchr (data, ' ');
+      if (arg_terminator == NULL) 
+       {
+         /* No space. This is the last parameter */
+
+         arg_terminator = data + strlen (data);
+       }
+
+      next_param = arg_terminator;
+      arg_len = (arg_terminator - data);
+
+      param = (char*) xmalloc (arg_len + 1);
+
+      /* Copy and NULL terminate */
+      memcpy (param, data, arg_len);
+      param[arg_len] = '\0';
+    }
+
+  while (*arg_terminator == ' ')
+    {
+      arg_terminator++;
+    }
+
+  if (next_param_out != NULL)
+    {
+      *next_param_out = arg_terminator;
+
+      if (*arg_terminator == '\0')
+       {
+         /* Tells the caller this is the last parameter */
+         *next_param_out = NULL;
+       }
+    }
+
+  return param;
+}
+
+struct rp_key*
+parse_keydesc_list (char *s, int *num_keys_out)
+{
+  static struct rp_key keys[MAX_KEY_SEQUENCE_INPUT_LENGTH];
+  char *current_arg, *next_arg = s;
+  struct rp_key *key;
+  int num_keys = 0;
+
+  if (s == NULL)
+    {
+      return NULL;
+    }
+
+  do
+    {
+      current_arg = parse_single_parameter (next_arg, &next_arg);
+      if (current_arg != NULL) 
+       {
+         if (*current_arg == '\0')
+           {
+             /* Empty string */
+             free(current_arg);
+             return NULL;
+           }
+
+         key = parse_keydesc (current_arg);
+             
+         if (key == NULL)
+           {
+             marked_message_printf (0, 0, " bind: unknown key '%s' ", 
current_arg);
+
+             free(current_arg);
+             return NULL;
+           }
+
+         if (num_keys >= MAX_KEY_SEQUENCE_INPUT_LENGTH) 
+           {
+             free(current_arg);
+             return NULL;
+           }
+         
+         keys[num_keys] = *key;
+         ++num_keys;
+
+         free(current_arg);
+       }      
+      else 
+       {
+         /* Parse error */
+         return NULL;
+       }
+    }
+  while (next_arg != NULL);
+
+  *num_keys_out = num_keys;
+
+  return keys;
+}
+
 /* Unmanage window */
 char *
 cmd_unmanage (int interactive, char *data)
@@ -530,37 +811,35 @@
       return NULL;
     }
 
-  keydesc = (char*) xmalloc (strlen (data) + 1);
-  sscanf (data, "%s", keydesc);
-  cmd = data + strlen (keydesc);
-
-  /* Gobble remaining whitespace before command starts */
-  while (*cmd == ' ')
-    {
-      cmd++;
-    }
+  keydesc = parse_single_parameter(data, &cmd);
   
-  if (!keydesc)
+  if (strlen(keydesc) == 0)
     message (" bind: at least one argument required ");
   else
     {
-      if (!cmd || !*cmd)
+      if (!cmd)
        {
          /* If no comand is specified, then unbind the key. */
-         cmd_unbind (interactive, keydesc);
+         cmd_unbind (interactive, data);
        }
       else
        {
-         struct rp_key *key = parse_keydesc (keydesc);
+         struct rp_key *keys;
+         int num_keys = 0;
+
+         keys = parse_keydesc_list (keydesc, &num_keys);
          
-         if (key)
+         if (keys)
            {
-             rp_action *key_action;
+             rp_action *key_action = NULL;
+             find_keybinding_rc_t rc;
 
-             if ((key_action = find_keybinding (key->sym, key->state)))
+             rc = find_keybinding_extended (keys, num_keys, &key_action);
+
+             if (rc == FIND_KEYBINDING_RC_FOUND)
                replace_keybinding (key_action, cmd);
              else
-               add_keybinding (key->sym, key->state, cmd);
+               add_keybinding_extended (keys, num_keys, cmd);
            }
          else
            marked_message_printf (0, 0, " bind: unknown key '%s' ", keydesc);
@@ -575,8 +854,9 @@
 char *
 cmd_unbind (int interactive, char *data)
 {
-  struct rp_key *key;
+  struct rp_key *keys;
   char *keydesc;
+  int num_keys;
 
   if (!data)
     {
@@ -584,18 +864,18 @@
       return NULL;
     }
 
-  keydesc = (char*) xmalloc (strlen (data) + 1);
-  sscanf (data, "%s", keydesc);
-  key = parse_keydesc (keydesc);
+  keydesc = parse_single_parameter(data, NULL);
 
-  if (key)
+  keys = parse_keydesc_list (keydesc, &num_keys);
+
+  if (keys)
     {
-      if (!remove_keybinding (key->sym, key->state))
-       marked_message_printf (0, 0, " unbind: unbound key '%s' ", keydesc);
+      if (!remove_keybinding (keys, num_keys))
+       marked_message_printf (0, 0, " unbind: unbound key sequence '%s' ", 
keydesc);
     }
   else
     {
-      marked_message_printf (0, 0, " unbind: unknown key '%s' ", keydesc);
+      marked_message_printf (0, 0, " unbind: unknown key sequence '%s' ", 
keydesc);
     }
 
   free (keydesc);
@@ -1807,7 +2087,7 @@
         {
           if (drawing_keys)
             {
-              keysym_name = keysym_to_string (key_actions[i].key, 
key_actions[i].state);
+              keysym_name = keybinding_to_string (&key_actions[i]);
 
               XDrawString (dpy, s->help_window, s->normal_gc,
                       x, y + defaults.font->max_bounds.ascent,
@@ -1886,7 +2166,7 @@
 
       for (i = 0; i < key_actions_last; i++)
         {
-          keysym_name = keysym_to_string (key_actions[i].key, 
key_actions[i].state);
+          keysym_name = keybinding_to_string (&key_actions[i]);
           sbuf_concat (help_list, keysym_name);
           free (keysym_name);
           sbuf_concat (help_list, " ");
diff -x '*~' -urN ratpoison/src/actions.h ratpoison-mod/src/actions.h
--- ratpoison/src/actions.h     2003-05-25 14:01:25.000000000 +0300
+++ ratpoison-mod/src/actions.h 2003-06-27 05:13:54.000000000 +0300
@@ -136,7 +136,19 @@
 void initialize_default_keybindings (void);
 void free_keybindings ();
 void free_aliases ();
-rp_action* find_keybinding (KeySym keysym, int state);
+
+typedef enum 
+{
+  FIND_KEYBINDING_RC_NOT_FOUND,
+  FIND_KEYBINDING_RC_FOUND,
+  FIND_KEYBINDING_RC_NEEDS_MORE,
+} find_keybinding_rc_t;
+
+find_keybinding_rc_t find_keybinding_extended (struct rp_key *keys, int 
num_keys, rp_action **action_out);
+rp_action* find_keybinding (KeySym code, unsigned int state);
 rp_action* find_keybinding_by_action (char *action);
+char *keybinding_to_string (rp_action *action);
+
+#define MAX_KEY_SEQUENCE_INPUT_LENGTH  (32)
 
 #endif /* ! _RATPOISON_ACTIONS_H */
diff -x '*~' -urN ratpoison/src/data.h ratpoison-mod/src/data.h
--- ratpoison/src/data.h        2003-05-27 10:51:07.000000000 +0300
+++ ratpoison-mod/src/data.h    2003-06-26 20:49:05.000000000 +0300
@@ -38,6 +38,7 @@
 typedef struct rp_window_elem rp_window_elem;
 typedef struct rp_completions rp_completions;
 typedef struct rp_input_line rp_input_line;
+typedef struct rp_key rp_key;
 
 struct rp_frame
 {
@@ -163,7 +164,8 @@
   KeySym key;
   unsigned int state;
   void *data;                  /* misc data to be passed to the function */
-/*   void (*func)(void *); */
+
+  struct rp_action *next;       /* if not NULL, it's the next keystroke */
 };
 
 struct rp_key
diff -x '*~' -urN ratpoison/src/events.c ratpoison-mod/src/events.c
--- ratpoison/src/events.c      2003-05-25 13:56:19.000000000 +0300
+++ ratpoison-mod/src/events.c  2003-06-27 05:13:00.000000000 +0300
@@ -368,53 +368,73 @@
   char *keysym_name;
   rp_action *key_action;
   int revert;                  
-  Window fwin;                 /* Window currently in focus */
-  KeySym keysym;               /* Key pressed */
-  unsigned int mod;            /* Modifiers */
+  Window fwin;                                /* Window currently in focus */ 
+  rp_key keys[MAX_KEY_SEQUENCE_INPUT_LENGTH];  /* Key sequence pressed */
+  int num_keys = 0;
+  unsigned int mod;                           /* Modifiers */
   int rat_grabbed = 0;
 
   PRINT_DEBUG (("handling key...\n"));
 
-  /* All functions hide the program bar and the frame indicator. */
-  if (defaults.bar_timeout > 0) hide_bar (s);
-  hide_frame_indicator();
+  for (;;)
+    {
+      find_keybinding_rc_t rc;
 
-  /* Disable any alarm that was going to go off. */
-  alarm (0);
-  alarm_signalled = 0;
+      /* All functions hide the program bar and the frame indicator. */
+      if (defaults.bar_timeout > 0) hide_bar (s);
+      hide_frame_indicator();
 
-  XGetInputFocus (dpy, &fwin, &revert);
-  XSetInputFocus (dpy, s->key_window, RevertToPointerRoot, CurrentTime);
+      /* Disable any alarm that was going to go off. */
+      alarm (0);
+      alarm_signalled = 0;
 
-  /* Change the mouse icon to indicate to the user we are waiting for
-     more keystrokes */
-  if (defaults.wait_for_key_cursor)
-    {
-      grab_rat();
-      rat_grabbed = 1;
-    }
+      XGetInputFocus (dpy, &fwin, &revert);
+      XSetInputFocus (dpy, s->key_window, RevertToPointerRoot, CurrentTime);
 
-  read_key (&keysym, &mod, NULL, 0);
+      /* Change the mouse icon to indicate to the user we are waiting for
+        more keystrokes */
+      if (defaults.wait_for_key_cursor)
+       {
+         grab_rat();
+         rat_grabbed = 1;
+       }
 
-  XSetInputFocus (dpy, fwin, revert, CurrentTime);
-  if (rat_grabbed)
-    ungrab_rat();
+      read_key (&keys[num_keys].sym, &mod, NULL, 0);
+      keys[num_keys].state = x11_mask_to_rp_mask (mod);
+      ++num_keys;
+
+      XSetInputFocus (dpy, fwin, revert, CurrentTime);
+      if (rat_grabbed)
+       ungrab_rat();
 
-  if ((key_action = find_keybinding (keysym, x11_mask_to_rp_mask (mod))))
-    {
-      char *result;
-      result = command (1, key_action->data);
+      rc = find_keybinding_extended (keys, num_keys, &key_action);
       
-      /* Gobble the result. */
-      if (result)
-       free (result);
-    }
-  else
-    {
-      /* No key match, notify user. */
-      keysym_name = keysym_to_string (keysym, x11_mask_to_rp_mask (mod));
-      marked_message_printf (0, 0, " %s unbound key ", keysym_name);
-      free (keysym_name);
+      if (rc == FIND_KEYBINDING_RC_NEEDS_MORE) 
+       {
+         if (num_keys >= MAX_KEY_SEQUENCE_INPUT_LENGTH) 
+           return;
+
+         continue;
+       }
+
+      if (rc == FIND_KEYBINDING_RC_NOT_FOUND)
+       {
+         /* No key match, notify user. */
+         keysym_name = keys_to_string (keys, num_keys);
+         marked_message_printf (0, 0, " '%s' unbound key sequence", 
keysym_name);
+         free (keysym_name);
+       }
+      else 
+       {
+         char *result;
+         result = command (1, key_action->data);
+         
+         /* Gobble the result. */
+         if (result)
+           free (result);
+       }
+
+      return;
     }
 }
 
diff -x '*~' -urN ratpoison/src/input.c ratpoison-mod/src/input.c
--- ratpoison/src/input.c       2003-05-27 10:51:07.000000000 +0300
+++ ratpoison-mod/src/input.c   2003-06-27 05:05:55.000000000 +0300
@@ -223,6 +223,35 @@
   return tmp;
 }
 
+char *
+keys_to_string (rp_key *keys, int num_keys)
+{
+  struct sbuf *s;
+  char *ret, *keysym_name;
+  int i;
+
+  s = sbuf_new (0);
+
+  for (i=0; i < num_keys; i++)
+    {
+      keysym_name = keysym_to_string (keys[i].sym, keys[i].state);
+
+      if (i == 0)
+       sbuf_printf_concat (s, "%s", keysym_name);
+      else
+       sbuf_printf_concat (s, " %s", keysym_name);
+
+      free(keysym_name);
+    }
+
+  ret = sbuf_get(s);
+
+  free(s);
+
+  return ret;
+}
+
+
 /* Cooks a keycode + modifier into a keysym + modifier. This should be
    used anytime meaningful key information is to be extracted from a
    KeyPress or KeyRelease event. 
diff -x '*~' -urN ratpoison/src/input.h ratpoison-mod/src/input.h
--- ratpoison/src/input.h       2003-05-27 10:51:07.000000000 +0300
+++ ratpoison-mod/src/input.h   2003-06-27 05:03:10.000000000 +0300
@@ -22,6 +22,7 @@
 #ifndef _RATPOISON_INPUT_H
 #define _RATPOISON_INPUT_H 1
 
+char *keys_to_string (rp_key *keys, int num_keys);
 char *keysym_to_string (KeySym keysym, unsigned int modifier);
 int cook_keycode (XKeyEvent *ev, KeySym *keysym, unsigned int *mod, char 
*keysym_name, int len, int ignore_bad_mods);
 char *get_input (char *prompt, completion_fn fn);
diff -x '*~' -urN ratpoison/src/main.c ratpoison-mod/src/main.c
--- ratpoison/src/main.c        2003-05-27 21:46:34.000000000 +0300
+++ ratpoison-mod/src/main.c    2003-06-27 04:56:02.000000000 +0300
@@ -405,7 +405,7 @@
   /* Find the help key binding. */
   help_action = find_keybinding_by_action ("help");
   if (help_action)
-    help = keysym_to_string (help_action->key, help_action->state);
+    help = keybinding_to_string (help_action);
   else
     help = NULL;
 



-- 
Dan Aloni
address@hidden



reply via email to

[Prev in Thread] Current Thread [Next in Thread]