ratpoison-devel
[Top][All Lists]
Advanced

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

[RP] [PATCH] binding key sequences


From: Dan Aloni
Subject: [RP] [PATCH] binding key sequences
Date: Thu Aug 28 01:13:04 2003
User-agent: Mutt/1.5.4i

Here is the latest version of my key sequences patch that applys
to the latest CVS version of ratpoison.

Here's a short reminder for what this patch is about: 

Binding multi-key commands, for example:

#
# Frame splitting
#

unbind s
bind "s w" split
bind "s x" split
bind "s d" vsplit
bind "s a" vsplit
bind "s Up" split
bind "s Down" split
bind "s Right" vsplit
bind "s Left" vsplit
bind "s s" only

#
# Execution
#

unbind e
bind "e s h" exec rxvt
bind "e m a i l" exec rxvt -e "mutt" 
bind "e o f f" exec xset dpms force off
bind "e x e c" colon exec 


--- ratpoison/src/actions.c     2003-08-28 10:32:08.000000000 +0300
+++ ratpoison-temp1/src/actions.c       2003-08-28 10:36:56.000000000 +0300
@@ -173,19 +173,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)
 {
@@ -194,7 +285,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);
@@ -239,6 +330,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 */
@@ -246,6 +338,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)
 {
@@ -256,24 +375,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--;
@@ -374,6 +498,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);
     }
 
@@ -500,6 +636,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)
@@ -534,37 +815,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);
@@ -579,8 +858,9 @@
 char *
 cmd_unbind (int interactive, char *data)
 {
-  struct rp_key *key;
+  struct rp_key *keys;
   char *keydesc;
+  int num_keys;
 
   if (!data)
     {
@@ -588,18 +868,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);
@@ -1817,7 +2097,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,
@@ -1896,7 +2176,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, " ");
--- ratpoison/src/actions.h     2003-08-28 10:32:08.000000000 +0300
+++ ratpoison-temp1/src/actions.h       2003-08-28 10:36:56.000000000 +0300
@@ -140,7 +140,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 */
--- ratpoison/src/data.h        2003-08-28 10:32:12.000000000 +0300
+++ ratpoison-temp1/src/data.h  2003-08-28 10:36:56.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
--- ratpoison/src/events.c      2003-08-28 10:32:14.000000000 +0300
+++ ratpoison-temp1/src/events.c        2003-08-28 10:44:05.000000000 +0300
@@ -368,56 +368,76 @@
   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();
-
-  /* Disable any alarm that was going to go off. */
-  alarm (0);
-  alarm_signalled = 0;
-
-  XGetInputFocus (dpy, &fwin, &revert);
-  XSetInputFocus (dpy, s->key_window, RevertToPointerRoot, CurrentTime);
-
-  /* Change the mouse icon to indicate to the user we are waiting for
-     more keystrokes */
-  if (defaults.wait_for_key_cursor)
+  for (;;)
     {
-      grab_rat();
-      rat_grabbed = 1;
-    }
+      find_keybinding_rc_t rc;
+  
+      /* All functions hide the program bar and the frame indicator. */
+      if (defaults.bar_timeout > 0) hide_bar (s);
+        hide_frame_indicator();
+  
+      /* Disable any alarm that was going to go off. */
+      alarm (0);
+      alarm_signalled = 0;
+  
+      XGetInputFocus (dpy, &fwin, &revert);
+      XSetInputFocus (dpy, s->key_window, RevertToPointerRoot, CurrentTime);
+  
+      /* 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;
+       }
 
-  /* Call the prefix hook. */
-  hook_run (&rp_prefix_hook);
+      if (num_keys == 0)
+        hook_run (&rp_prefix_hook);
+  
+      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();
+  
+      rc = find_keybinding_extended (keys, num_keys, &key_action);
+        
+      if (rc == FIND_KEYBINDING_RC_NEEDS_MORE) 
+       {
+         if (num_keys >= MAX_KEY_SEQUENCE_INPUT_LENGTH) 
+           return;
 
-  read_key (&keysym, &mod, NULL, 0);
+         continue;
+       }
 
-  XSetInputFocus (dpy, fwin, revert, CurrentTime);
-  if (rat_grabbed)
-    ungrab_rat();
+      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);
+       }
 
-  if ((key_action = find_keybinding (keysym, x11_mask_to_rp_mask (mod))))
-    {
-      char *result;
-      result = command (1, key_action->data);
-      
-      /* 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);
+      return;
     }
 }
 
--- ratpoison/src/input.c       2003-05-27 10:51:07.000000000 +0300
+++ ratpoison-temp1/src/input.c 2003-08-28 10:36:56.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. 
--- ratpoison/src/input.h       2003-05-27 10:51:07.000000000 +0300
+++ ratpoison-temp1/src/input.h 2003-08-28 10:36:56.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);
--- ratpoison/src/main.c        2003-05-27 21:46:34.000000000 +0300
+++ ratpoison-temp1/src/main.c  2003-08-28 10:36:56.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]