static int tcurses_vt100_getkeycode()

in system/termcurses/tcurses_vt100.c [1131:1416]


static int tcurses_vt100_getkeycode(FAR struct termcurses_s *dev,
                                    FAR int *specialkey,
                                    FAR int *keymodifiers)
{
  FAR struct tcurses_vt100_s  *priv;
  FAR const struct keycodes_s *pkeycodes;
  int                          ret;
  int                          fd;
  bool                         esc_seq = false;
  bool                         ctrl_seq = false;
  bool                         ismodifier;
  int                          keycode;
  int                          k;
  int                          x;
  int                          start;
  fd_set                       rfds;
  struct timeval               tv;
  char                         ch;
  char                         buildkey[16];
  int                          keybuildcount;
  static const char            modtable[7] =
  {
    PDC_KEY_MODIFIER_SHIFT,
    PDC_KEY_MODIFIER_ALT,
    PDC_KEY_MODIFIER_ALT | PDC_KEY_MODIFIER_SHIFT,
    PDC_KEY_MODIFIER_CONTROL,
    PDC_KEY_MODIFIER_CONTROL | PDC_KEY_MODIFIER_SHIFT,
    PDC_KEY_MODIFIER_ALT | PDC_KEY_MODIFIER_CONTROL,
    PDC_KEY_MODIFIER_ALT | PDC_KEY_MODIFIER_CONTROL | PDC_KEY_MODIFIER_SHIFT
  };

  priv = (FAR struct tcurses_vt100_s *)dev;
  fd   = priv->in_fd;

  /* Watch stdin (fd 0) to see when it has input. */

  FD_ZERO(&rfds);
  FD_SET(0, &rfds);

  /* Wait up to 1000us for next character after ESC */

  tv.tv_sec     = 0;
  tv.tv_usec    = 1000;

  /* Loop until we have a valid key code, taking escape sequences
   * into account
   */

  keycode       = -1;
  *keymodifiers = 0;
  *specialkey   = 0;
  ismodifier    = false;
  keybuildcount = 0;

  while (keycode == -1)
    {
      /* Test if keybuf has unprocessed bytes */

      if (priv->keycount == 0)
        {
          /* Get next bytes from input stream */

          priv->keycount = read(fd, priv->keybuf, sizeof(priv->keybuf)-1);
          if (priv->keycount <= 0)
            {
              return -1;
            }

          priv->keybuf[priv->keycount] = 0;
        }

      /* Loop for all bytes received */

      for (x = 0; x < priv->keycount && keycode == -1; x++)
        {
          ch = priv->keybuf[x];
          if (ch == 0 && x + 1 == priv->keycount)
            {
              priv->keycount = 0;
              return -1;
            }

          /* Test for escape sequence */

          if (ch == '\x1b')
            {
#ifdef CONFIG_SYSTEM_TERMCURSES_DEBUG_KEYCODES
              printf("<0x%02X>", ch);
              fflush(stdout);
#endif

              /* Mark as ESC sequence */

              esc_seq       = true;
              ctrl_seq      = false;
              buildkey[0]   = ch;
              keybuildcount = 1;

              if (x + 1 != priv->keycount)
                {
                  continue;
                }

              /* Test for other characters in the queue.  If there are more
               * characters waiting, then simply continue the loop to process
               * them.
               */

              priv->keycount = 0;
              ret = select(1, &rfds, NULL, NULL, &tv);
              if (ret > 0)
                {
                  continue;
                }

              /* No more characters waiting in the queue.  Must be ESC key. */

              return '\x1b';
            }

          else if (esc_seq || ctrl_seq)
            {
#ifdef CONFIG_SYSTEM_TERMCURSES_DEBUG_KEYCODES
              if (ch >= 33 && ch <= '~')
                {
                  printf("%c", ch);
                }
              else
                {
                  printf("<0x%02X>", ch);
                }

              fflush(stdout);
#endif

              /* Check if this is a key modifier code */

              if (ismodifier)
                {
                  /* Test if previous char was '1' */

                  if (buildkey[keybuildcount - 1] == '1')
                    {
                      buildkey[--keybuildcount] = 0;
                    }

                  /* Clear ismodifier attribute */

                  ismodifier = false;
                  if (ch < '2' || ch > '8')
                    {
                      continue;
                    }

                  /* Get the key modifier code */

                  *keymodifiers = modtable[ch - '2'];
                  continue;
                }
              else if (ch == ';')
                {
                  /* Mark next character as key modifier code */

                  ismodifier = true;
                  continue;
                }

              /* Add character to the buildkey */

              buildkey[keybuildcount++] = ch;
              buildkey[keybuildcount]   = 0;

              /* If there are more bytes in the sequence, then
               * process them prior to searching the keycode
               * array to save time.
               */

              if (x + 1 != priv->keycount && priv->keybuf[x + 1] != '\x1b')
                {
                  continue;
                }

              /* Search either ESC or CTRL keycode table */

              pkeycodes = g_esc_keycodes;
              start = 1;

#ifdef CONFIG_SYSTEM_TERMCURSES_VT100_OSX_ALT_CODES
              if (buildkey[0] != '\x1b')
                {
                  pkeycodes = g_ctrl_keycodes;
                  start = 0;
                }
#endif

              /* Test for match with known key definitions */

              for (k = 0; pkeycodes[k].def != NULL; k++)
                {
                  if (strcmp(&buildkey[start], pkeycodes[k].def) == 0)
                    {
                      /* Special key code found! */

                      keycode = pkeycodes[k].keycode;
                      break;
                    }
                }

              /* If we found a keymodifier, then check for key code
               * substitutions.
               */

              if (pkeycodes[k].def != NULL && *keymodifiers != 0)
                {
                  uint16_t searchval = (*keymodifiers << 12) | keycode;

                  /* Search the modifier table */

                  for (k = 0; k < g_key_modifier_count; k++)
                    {
                      /* Test next entry in table for match */

                      if (searchval == g_key_modifiers[k][0])
                        {
                          /* Update to new keycode */

                          keycode = g_key_modifiers[k][1];
                          break;
                        }
                    }
                }
            }

          else if (ch == 127)
            {
              /* Return as CTRL-H */

              keycode = 8;
            }

          else if ((ch >= ' ' && ch <= '~') || (ch >= 1 && ch <= 26))
            {
              /* Return normal ASCII or CTRL-x key */

              keycode = ch;

#ifdef CONFIG_SYSTEM_TERMCURSES_DEBUG_KEYCODES
              printf("%c", ch);
              fflush(stdout);
#endif
            }
          else
            {
              /* It is a special control code */

              buildkey[0]   = ch;
              keybuildcount = 1;
              ctrl_seq      = 1;

#ifdef CONFIG_SYSTEM_TERMCURSES_DEBUG_KEYCODES
              /* Print all bytes in ctrl seq */

              printf("<0x%02X>", ch);
              fflush(stdout);
#endif
            }
        }

      /* Update keycount and keybuf */

      priv->keycount -= x;
      if (priv->keycount < 0)
        {
          /* Hmm, some bug.  Better to simply ignore than to crash */

          priv->keycount = 0;
        }

      if (priv->keycount != 0)
        {
          memmove(priv->keybuf, &priv->keybuf[x], priv->keycount);
        }
    }

  return keycode;
}