ratpoison-devel
[Top][All Lists]
Advanced

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

[PATCH] binding key sequences (was: Re: [RP] new group commands)


From: Dan Aloni
Subject: [PATCH] binding key sequences (was: Re: [RP] new group commands)
Date: Thu Jun 26 11:44:01 2003
User-agent: Mutt/1.5.4i

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)

diff -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-26 21:14:49.000000000 +0300
@@ -169,16 +169,79 @@
   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)
+           {
+             /*
+              * We need to decide what to do in this weird
+              * state. I'll leave it this way for the time
+              * being.
+              */
+             return FIND_KEYBINDING_RC_NOT_FOUND;
+           }
+         
+         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;
 }
 
@@ -235,6 +298,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 +306,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 +343,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 +466,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 +604,145 @@
     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[32]; /* Yeah I know this is ugly, but I'm lazy */
+  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;
+           }
+         
+         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 +777,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;
+
+             rc = find_keybinding_extended (keys, num_keys, &key_action);
 
-             if ((key_action = find_keybinding (key->sym, key->state)))
+             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 +820,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 +830,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);
diff -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-26 21:21:23.000000000 +0300
@@ -368,53 +368,70 @@
   char *keysym_name;
   rp_action *key_action;
   int revert;                  
-  Window fwin;                 /* Window currently in focus */
-  KeySym keysym;               /* Key pressed */
+  Window fwin;                 /* Window currently in focus */ 
+  rp_key keys[32];             /* Key sequence pressed (TODO: replace 
ugliness)*/
+  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) 
+       {
+         continue;
+       }
+
+      if (rc == FIND_KEYBINDING_RC_NOT_FOUND)
+       {
+         /* No key match, notify user. */
+         keysym_name = keysym_to_string (keys[0].sym, x11_mask_to_rp_mask 
(mod));
+         marked_message_printf (0, 0, " %s unbound key ", keysym_name);
+         free (keysym_name);
+       }
+      else 
+       {
+         char *result;
+         result = command (1, key_action->data);
+         
+         /* Gobble the result. */
+         if (result)
+           free (result);
+       }
+
+      return;
     }
 }
 
--- 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
--- ratpoison/src/actions.h     2003-05-25 14:01:25.000000000 +0300
+++ ratpoison-mod/src/actions.h 2003-06-26 20:58:58.000000000 +0300
@@ -136,7 +136,16 @@
 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);
 
 #endif /* ! _RATPOISON_ACTIONS_H */



-- 
Dan Aloni
address@hidden



reply via email to

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