Re: keyboard handling (specifically mutt_getch)
On Tue, Dec 16, 2003 at 05:29:06AM -0500, David Yitzchak Cohen wrote:
> On Tue, Dec 16, 2003 at 01:38:13AM -0500, Allister MacLeod wrote:
> > if (tmp.op)
> > As far as I can tell, tmp is only ever set by assignment from the
> > mutt_getch() function. Upon reading that function, it looks like
> > maybe the .op field of the return value is only ever set when there is
> > an error (and then its value is OP_NULL). I'm not sure, though. Can
> > anyone tell me whether there are other cases where tmp.op would be
> > nonzero?
> Well, I'm affraid to say anything here, since I'll probably be wrong,
> and you-know-who's gonna be gunnin' for my next wrong statement ;-P
> (I've never even looked at this part of the code before.) However,
> what I can reliably say is that (a) that if is in a loop, (b) there are
> multiple levels of recursion involved here, and (c) my head hurts just
> looking at it.
> HTH (though I doubt it will),
Heh.. thanks anyway for giving a second viewpoint. I'd like to point
some things out, in light of the ones that you brought up. Even
though there's a fair amount of recursion involved in km_dokey() and
retry_generic(), the event_t tmp is a local variable. So, barring
some total weirdness, its value is just taken from settings within the
function.
Now that I've (I think) learned that OP_NULL == 0, at least according
to Thomas's example, I wonder if the body of the if(tmp.op) block ever
gets executed. As far as I could tell, the return value of mutt_getch
has its op member set to 0 directly (in a normal key case) or OP_NULL
(in an error case). Weird. I guess I can try running it through the
debugger and see if that block of code is reached in "normal use".
Attached the bodies of a few of the relevant functions, for reference.
Ciao,
Allister
--
Allister MacLeod <amacleod@xxxxxxxx> | http://amacleod.is-a-geek.org/
Elen síla lúmenn'omentielvo.
static int retry_generic (int menu, keycode_t *keys, int keyslen, int lastkey)
{
if (menu != MENU_EDITOR && menu != MENU_GENERIC && menu != MENU_PAGER)
{
if (lastkey)
mutt_ungetch (lastkey, 0);
for (; keyslen; keyslen--)
mutt_ungetch (keys[keyslen - 1], 0);
return (km_dokey (MENU_GENERIC));
}
if (menu != MENU_EDITOR)
{
/* probably a good idea to flush input here so we can abort macros */
mutt_flushinp ();
}
return OP_NULL;
}
/* return values:
* >0 function to execute
* OP_NULL no function bound to key sequence
* -1 error occured while reading input
*/
int km_dokey (int menu)
{
event_t tmp;
struct keymap_t *map = Keymaps[menu];
int pos = 0;
int n = 0;
int i;
if (!map)
return (retry_generic (menu, NULL, 0, 0));
FOREVER
{
/* ncurses doesn't return on resized screen when timeout is set to zero */
if (menu != MENU_EDITOR)
timeout ((Timeout > 0 ? Timeout : 60) * 1000);
tmp = mutt_getch();
if (menu != MENU_EDITOR)
timeout (-1); /* restore blocking operation */
LastKey = tmp.ch;
if (LastKey == -1)
return -1;
/* do we have an op already? */
if (tmp.op)
{
char *func = NULL;
struct binding_t *bindings;
/* is this a valid op for this menu? */
if ((bindings = km_get_table (menu)) &&
(func = get_func (bindings, tmp.op)))
return tmp.op;
if (menu == MENU_EDITOR && get_func (OpEditor, tmp.op))
return tmp.op;
if (menu != MENU_EDITOR && menu != MENU_PAGER)
{
/* check generic menu */
bindings = OpGeneric;
if ((func = get_func (bindings, tmp.op)))
return tmp.op;
}
/* Sigh. Valid function but not in this context.
* Find the literal string and push it back */
for (i = 0; Menus[i].name; i++)
{
bindings = km_get_table (Menus[i].value);
if (bindings)
{
func = get_func (bindings, tmp.op);
if (func)
{
/* careful not to feed the <..> as one token. otherwise
* push_string() will push the bogus op right back! */
mutt_ungetch ('>', 0);
push_string (func);
mutt_ungetch ('<', 0);
break;
}
}
}
/* continue to chew */
if (func)
continue;
}
/* Nope. Business as usual */
while (LastKey > map->keys[pos])
{
if (pos > map->eq || !map->next)
return (retry_generic (menu, map->keys, pos, LastKey));
map = map->next;
}
if (LastKey != map->keys[pos])
return (retry_generic (menu, map->keys, pos, LastKey));
if (++pos == map->len)
{
if (map->op != OP_MACRO)
return map->op;
if (n++ == 10)
{
mutt_flushinp ();
mutt_error _("Macro loop detected.");
return -1;
}
push_string (map->macro);
map = Keymaps[menu];
pos = 0;
}
}
/* not reached */
}
event_t mutt_getch (void)
{
int ch;
event_t err = {-1, OP_NULL }, ret;
if (!option(OPTUNBUFFEREDINPUT) && UngetCount)
return (KeyEvent[--UngetCount]);
SigInt = 0;
mutt_allow_interrupt (1);
#ifdef KEY_RESIZE
/* ncurses 4.2 sends this when the screen is resized */
ch = KEY_RESIZE;
while (ch == KEY_RESIZE)
#endif /* KEY_RESIZE */
ch = getch ();
mutt_allow_interrupt (0);
if (SigInt)
mutt_query_exit ();
if(ch == ERR)
return err;
if ((ch & 0x80) && option (OPTMETAKEY))
{
/* send ALT-x as ESC-x */
ch &= ~0x80;
mutt_ungetch (ch, 0);
ret.ch = '\033';
ret.op = 0;
return ret;
}
ret.ch = ch;
ret.op = 0;
return (ch == ctrl ('G') ? err : ret);
}