[Top][All Lists]
[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