<<< Date Index >>>     <<< Thread Index >>>

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);
}