* src/puresize.h (BASE_PURESIZE): Bump by another 1K.
[bpt/emacs.git] / src / term.c
index 5dd70ac..8661cba 100644 (file)
@@ -1,5 +1,5 @@
 /* Terminal control module for terminals described by TERMCAP
-   Copyright (C) 1985-1987, 1993-1995, 1998, 2000-2013 Free Software
+   Copyright (C) 1985-1987, 1993-1995, 1998, 2000-2014 Free Software
    Foundation, Inc.
 
 This file is part of GNU Emacs.
@@ -51,6 +51,10 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 static int been_here = -1;
 #endif
 
+#ifdef USE_X_TOOLKIT
+#include "../lwlib/lwlib.h"
+#endif
+
 #include "cm.h"
 #ifdef HAVE_X_WINDOWS
 #include "xterm.h"
@@ -216,6 +220,7 @@ tty_update_end (struct frame *f)
     tty_show_cursor (tty);
   tty_turn_off_insert (tty);
   tty_background_highlight (tty);
+  fflush (tty->output);
 }
 
 /* The implementation of set_terminal_window for termcap frames. */
@@ -304,7 +309,11 @@ tty_hide_cursor (struct tty_display_info *tty)
   if (tty->cursor_hidden == 0)
     {
       tty->cursor_hidden = 1;
+#ifdef WINDOWSNT
+      w32con_hide_cursor ();
+#else
       OUTPUT_IF (tty, tty->TS_cursor_invisible);
+#endif
     }
 }
 
@@ -317,9 +326,13 @@ tty_show_cursor (struct tty_display_info *tty)
   if (tty->cursor_hidden)
     {
       tty->cursor_hidden = 0;
+#ifdef WINDOWSNT
+      w32con_show_cursor ();
+#else
       OUTPUT_IF (tty, tty->TS_cursor_normal);
       if (visible_cursor)
         OUTPUT_IF (tty, tty->TS_cursor_visible);
+#endif
     }
 }
 
@@ -487,8 +500,12 @@ static ptrdiff_t encode_terminal_dst_size;
    Set CODING->produced to the byte-length of the resulting byte
    sequence, and return a pointer to that byte sequence.  */
 
+#ifndef DOS_NT
+static
+#endif
 unsigned char *
-encode_terminal_code (struct glyph *src, int src_len, struct coding_system *coding)
+encode_terminal_code (struct glyph *src, int src_len,
+                     struct coding_system *coding)
 {
   struct glyph *src_end = src + src_len;
   unsigned char *buf;
@@ -1405,18 +1422,6 @@ term_get_fkeys_1 (void)
       CONDITIONAL_REASSIGN ("kD", "kI", "insert");
       /* if there's no key_end keycap, map key_ll to 'end' keysym */
       CONDITIONAL_REASSIGN ("@7", "kH", "end");
-
-      /* IBM has their own non-standard dialect of terminfo.
-        If the standard name isn't found, try the IBM name.  */
-      CONDITIONAL_REASSIGN ("kB", "KO", "backtab");
-      CONDITIONAL_REASSIGN ("@4", "kJ", "execute"); /* actually "action" */
-      CONDITIONAL_REASSIGN ("@4", "kc", "execute"); /* actually "command" */
-      CONDITIONAL_REASSIGN ("%7", "ki", "menu");
-      CONDITIONAL_REASSIGN ("@7", "kw", "end");
-      CONDITIONAL_REASSIGN ("F1", "k<", "f11");
-      CONDITIONAL_REASSIGN ("F2", "k>", "f12");
-      CONDITIONAL_REASSIGN ("%1", "kq", "help");
-      CONDITIONAL_REASSIGN ("*6", "kU", "select");
 #undef CONDITIONAL_REASSIGN
   }
 
@@ -1802,27 +1807,10 @@ append_glyphless_glyph (struct it *it, int face_id, const char *str)
 static void
 produce_glyphless_glyph (struct it *it, Lisp_Object acronym)
 {
-  int face_id;
-  int len;
+  int len, face_id = merge_glyphless_glyph_face (it);
   char buf[sizeof "\\x" + max (6, (sizeof it->c * CHAR_BIT + 3) / 4)];
   char const *str = "    ";
 
-  /* Get a face ID for the glyph by utilizing a cache (the same way as
-     done for `escape-glyph' in get_next_display_element).  */
-  if (it->f == last_glyphless_glyph_frame
-      && it->face_id == last_glyphless_glyph_face_id)
-    {
-      face_id = last_glyphless_glyph_merged_face_id;
-    }
-  else
-    {
-      /* Merge the `glyphless-char' face into the current face.  */
-      face_id = merge_faces (it->f, Qglyphless_char, 0, it->face_id);
-      last_glyphless_glyph_frame = it->f;
-      last_glyphless_glyph_face_id = it->face_id;
-      last_glyphless_glyph_merged_face_id = face_id;
-    }
-
   if (it->glyphless_method == GLYPHLESS_DISPLAY_THIN_SPACE)
     {
       /* As there's no way to produce a thin space, we produce a space
@@ -1892,55 +1880,18 @@ static void
 turn_on_face (struct frame *f, int face_id)
 {
   struct face *face = FACE_FROM_ID (f, face_id);
-  long fg = face->foreground;
-  long bg = face->background;
+  unsigned long fg = face->foreground;
+  unsigned long bg = face->background;
   struct tty_display_info *tty = FRAME_TTY (f);
 
-  /* Do this first because TS_end_standout_mode may be the same
+  /* Use reverse video if the face specifies that.
+     Do this first because TS_end_standout_mode may be the same
      as TS_exit_attribute_mode, which turns all appearances off. */
-  if (MAY_USE_WITH_COLORS_P (tty, NC_REVERSE))
-    {
-      if (tty->TN_max_colors > 0)
-       {
-         if (fg >= 0 && bg >= 0)
-           {
-             /* If the terminal supports colors, we can set them
-                below without using reverse video.  The face's fg
-                and bg colors are set as they should appear on
-                the screen, i.e. they take the inverse-video'ness
-                of the face already into account.  */
-           }
-         else if (inverse_video)
-           {
-             if (fg == FACE_TTY_DEFAULT_FG_COLOR
-                 || bg == FACE_TTY_DEFAULT_BG_COLOR)
-               tty_toggle_highlight (tty);
-           }
-         else
-           {
-             if (fg == FACE_TTY_DEFAULT_BG_COLOR
-                 || bg == FACE_TTY_DEFAULT_FG_COLOR)
-               tty_toggle_highlight (tty);
-           }
-       }
-      else
-       {
-         /* If we can't display colors, use reverse video
-            if the face specifies that.  */
-         if (inverse_video)
-           {
-             if (fg == FACE_TTY_DEFAULT_FG_COLOR
-                 || bg == FACE_TTY_DEFAULT_BG_COLOR)
-               tty_toggle_highlight (tty);
-           }
-         else
-           {
-             if (fg == FACE_TTY_DEFAULT_BG_COLOR
-                 || bg == FACE_TTY_DEFAULT_FG_COLOR)
-               tty_toggle_highlight (tty);
-           }
-       }
-    }
+  if (MAY_USE_WITH_COLORS_P (tty, NC_REVERSE)
+      && (inverse_video
+         ? fg == FACE_TTY_DEFAULT_FG_COLOR || bg == FACE_TTY_DEFAULT_BG_COLOR
+         : fg == FACE_TTY_DEFAULT_BG_COLOR || bg == FACE_TTY_DEFAULT_FG_COLOR))
+    tty_toggle_highlight (tty);
 
   if (face->tty_bold_p && MAY_USE_WITH_COLORS_P (tty, NC_BOLD))
     OUTPUT1_IF (tty, tty->TS_enter_bold_mode);
@@ -1965,7 +1916,7 @@ turn_on_face (struct frame *f, int face_id)
       char *p;
 
       ts = tty->standout_mode ? tty->TS_set_background : tty->TS_set_foreground;
-      if (fg >= 0 && ts)
+      if (face_tty_specified_color (fg) && ts)
        {
           p = tparam (ts, NULL, 0, fg, 0, 0, 0);
          OUTPUT (tty, p);
@@ -1973,7 +1924,7 @@ turn_on_face (struct frame *f, int face_id)
        }
 
       ts = tty->standout_mode ? tty->TS_set_foreground : tty->TS_set_background;
-      if (bg >= 0 && ts)
+      if (face_tty_specified_color (bg) && ts)
        {
           p = tparam (ts, NULL, 0, bg, 0, 0, 0);
          OUTPUT (tty, p);
@@ -2027,12 +1978,10 @@ turn_off_face (struct frame *f, int face_id)
 
 
 /* Return true if the terminal on frame F supports all of the
-   capabilities in CAPS simultaneously, with foreground and background
-   colors FG and BG.  */
+   capabilities in CAPS simultaneously.  */
 
 bool
-tty_capable_p (struct tty_display_info *tty, unsigned int caps,
-              unsigned long fg, unsigned long bg)
+tty_capable_p (struct tty_display_info *tty, unsigned int caps)
 {
 #define TTY_CAPABLE_P_TRY(tty, cap, TS, NC_bit)                                \
   if ((caps & (cap)) && (!(TS) || !MAY_USE_WITH_COLORS_P(tty, NC_bit)))        \
@@ -2103,17 +2052,9 @@ tty_default_color_capabilities (struct tty_display_info *tty, bool save)
 
   if (save)
     {
-      xfree (default_orig_pair);
-      default_orig_pair = tty->TS_orig_pair ? xstrdup (tty->TS_orig_pair) : NULL;
-
-      xfree (default_set_foreground);
-      default_set_foreground = tty->TS_set_foreground ? xstrdup (tty->TS_set_foreground)
-                              : NULL;
-
-      xfree (default_set_background);
-      default_set_background = tty->TS_set_background ? xstrdup (tty->TS_set_background)
-                              : NULL;
-
+      dupstring (&default_orig_pair, tty->TS_orig_pair);
+      dupstring (&default_set_foreground, tty->TS_set_foreground);
+      dupstring (&default_set_background, tty->TS_set_background);
       default_max_colors = tty->TN_max_colors;
       default_max_pairs = tty->TN_max_pairs;
       default_no_color_video = tty->TN_no_color_video;
@@ -2446,7 +2387,7 @@ frame's terminal). */)
             was suspended.  */
          get_tty_size (fileno (t->display_info.tty->input), &width, &height);
          if (width != old_width || height != old_height)
-           change_frame_size (f, height, width, 0, 0, 0);
+           change_frame_size (f, width, height, 0, 0, 0, 0);
          SET_FRAME_VISIBLE (XFRAME (t->display_info.tty->top_frame), 1);
        }
 
@@ -2778,14 +2719,15 @@ DEFUN ("gpm-mouse-stop", Fgpm_mouse_stop, Sgpm_mouse_stop,
                               Menus
  ***********************************************************************/
 
-#if defined (HAVE_MENUS) && !defined (MSDOS)
+#if !defined (MSDOS)
 
 /* TTY menu implementation and main ideas are borrowed from msdos.c.
 
    However, unlike on MSDOS, where the menu text is drawn directly to
-   the screen, on a TTY we use display_string (see xdisp.c) to put the
-   glyphs produced from the menu items into the desired_matrix glyph
-   matrix, and then call update_frame to deliver the results to the
+   the display video memory, on a TTY we use display_string (see
+   display_tty_menu_item in xdisp.c) to put the glyphs produced from
+   the menu items into the frame's 'desired_matrix' glyph matrix, and
+   then call update_frame_with_menu to deliver the results to the
    glass.  The previous contents of the screen, in the form of the
    current_matrix, is stashed away, and used to restore screen
    contents when the menu selection changes or when the final
@@ -2797,6 +2739,8 @@ DEFUN ("gpm-mouse-stop", Fgpm_mouse_stop, Sgpm_mouse_stop,
 #define TTYM_SUCCESS 1
 #define TTYM_NO_SELECT 2
 #define TTYM_IA_SELECT 3
+#define TTYM_NEXT 4
+#define TTYM_PREV 5
 
 /* These hold text of the current and the previous menu help messages.  */
 static const char *menu_help_message, *prev_menu_help_message;
@@ -2804,13 +2748,19 @@ static const char *menu_help_message, *prev_menu_help_message;
    last menu help message.  */
 static int menu_help_paneno, menu_help_itemno;
 
+static Lisp_Object Qtty_menu_navigation_map, Qtty_menu_exit;
+static Lisp_Object Qtty_menu_prev_item, Qtty_menu_next_item;
+static Lisp_Object Qtty_menu_next_menu, Qtty_menu_prev_menu;
+static Lisp_Object Qtty_menu_select, Qtty_menu_ignore;
+static Lisp_Object Qtty_menu_mouse_movement;
+
 typedef struct tty_menu_struct
 {
   int count;
   char **text;
   struct tty_menu_struct **submenu;
-  int *panenumber; /* Also used as enable.  */
-  int allocated;
+  int *panenumber; /* Also used as enabled flag.  */
+  ptrdiff_t allocated;
   int panecount;
   int width;
   const char **help_text;
@@ -2821,38 +2771,27 @@ typedef struct tty_menu_struct
 static tty_menu *
 tty_menu_create (void)
 {
-  tty_menu *menu;
-
-  menu = (tty_menu *) xmalloc (sizeof (tty_menu));
-  menu->allocated = menu->count = menu->panecount = menu->width = 0;
-  return menu;
+  return xzalloc (sizeof *tty_menu_create ());
 }
 
 /* Allocate some (more) memory for MENU ensuring that there is room for one
-   for item.  */
+   more item.  */
 
 static void
 tty_menu_make_room (tty_menu *menu)
 {
-  if (menu->allocated == 0)
-    {
-      int count = menu->allocated = 10;
-      menu->text = (char **) xmalloc (count * sizeof (char *));
-      menu->submenu = (tty_menu **) xmalloc (count * sizeof (tty_menu *));
-      menu->panenumber = (int *) xmalloc (count * sizeof (int));
-      menu->help_text = (const char **) xmalloc (count * sizeof (char *));
-    }
-  else if (menu->allocated == menu->count)
+  if (menu->allocated == menu->count)
     {
-      int count = menu->allocated = menu->allocated + 10;
-      menu->text
-       = (char **) xrealloc (menu->text, count * sizeof (char *));
-      menu->submenu
-       = (tty_menu **) xrealloc (menu->submenu, count * sizeof (tty_menu *));
-      menu->panenumber
-       = (int *) xrealloc (menu->panenumber, count * sizeof (int));
-      menu->help_text
-       = (const char **) xrealloc (menu->help_text, count * sizeof (char *));
+      ptrdiff_t allocated = menu->allocated;
+      menu->text = xpalloc (menu->text, &allocated, 1, -1, sizeof *menu->text);
+      menu->text = xrealloc (menu->text, allocated * sizeof *menu->text);
+      menu->submenu = xrealloc (menu->submenu,
+                               allocated * sizeof *menu->submenu);
+      menu->panenumber = xrealloc (menu->panenumber,
+                                  allocated * sizeof *menu->panenumber);
+      menu->help_text = xrealloc (menu->help_text,
+                                 allocated * sizeof *menu->help_text);
+      menu->allocated = allocated;
     }
 }
 
@@ -2869,7 +2808,8 @@ tty_menu_search_pane (tty_menu *menu, int pane)
       {
        if (pane == menu->panenumber[i])
          return menu->submenu[i];
-       if ((try = tty_menu_search_pane (menu->submenu[i], pane)))
+       try = tty_menu_search_pane (menu->submenu[i], pane);
+       if (try)
          return try;
       }
   return (tty_menu *) 0;
@@ -2882,7 +2822,7 @@ tty_menu_calc_size (tty_menu *menu, int *width, int *height)
 {
   int i, h2, w2, maxsubwidth, maxheight;
 
-  maxsubwidth = 0;
+  maxsubwidth = menu->width;
   maxheight = menu->count;
   for (i = 0; i < menu->count; i++)
     {
@@ -2893,62 +2833,59 @@ tty_menu_calc_size (tty_menu *menu, int *width, int *height)
          if (i + h2 > maxheight) maxheight = i + h2;
        }
     }
-  *width = menu->width + maxsubwidth;
+  *width = maxsubwidth;
   *height = maxheight;
 }
 
-/* Display MENU at (X,Y) using FACES.  */
-
 static void
-tty_menu_display (tty_menu *menu, int x, int y, int pn, int *faces,
-                 int disp_help)
+mouse_get_xy (int *x, int *y)
 {
-  int i, face, width,  mx = -1, my = -1, enabled, mousehere, row, col;
   struct frame *sf = SELECTED_FRAME ();
-  struct tty_display_info *tty = FRAME_TTY (sf);
-#if defined (HAVE_MOUSE) || defined (HAVE_GPM)
-  Lisp_Object lmx, lmy, lisp_dummy;
+  Lisp_Object lmx = Qnil, lmy = Qnil, lisp_dummy;
   enum scroll_bar_part part_dummy;
   Time time_dummy;
 
   if (FRAME_TERMINAL (sf)->mouse_position_hook)
     (*FRAME_TERMINAL (sf)->mouse_position_hook) (&sf, -1,
-                                                 &lispy_dummy, &party_dummy,
+                                                 &lisp_dummy, &part_dummy,
                                                 &lmx, &lmy,
                                                 &time_dummy);
   if (!NILP (lmx))
     {
-      mx = XINT (lmx);
-      my = XINT (lmy);
+      *x = XINT (lmx);
+      *y = XINT (lmy);
     }
-  else
-    {
-      mx = x;
-      my = y;
-    }
-#else
-  /* FIXME: need to set mx and my from cursor movement commands.  */
-  mx = x;
-  my = y;
-#endif
+}
+
+/* Display MENU at (X,Y) using FACES, starting with FIRST_ITEM
+   (zero-based).  */
+
+static void
+tty_menu_display (tty_menu *menu, int x, int y, int pn, int *faces,
+                 int mx, int my, int first_item, bool disp_help)
+{
+  int i, face, width, enabled, mousehere, row, col;
+  struct frame *sf = SELECTED_FRAME ();
+  struct tty_display_info *tty = FRAME_TTY (sf);
+  /* Don't try to display more menu items than the console can display
+     using the available screen lines.  Exclude the echo area line, as
+     it will be overwritten by the help-echo anyway.  */
+  int max_items = min (menu->count - first_item, FRAME_LINES (sf) - 1 - y);
 
   menu_help_message = NULL;
 
   width = menu->width;
   col = cursorX (tty);
   row = cursorY (tty);
-#if 0
-  IT_update_begin (sf);                /* FIXME: do we need an update_begin_hook? */
-#endif
-  for (i = 0; i < menu->count; i++)
+  for (i = 0; i < max_items; i++)
     {
       int max_width = width + 2; /* +2 for padding blanks on each side */
+      int j = i + first_item;
 
-      cursor_to (sf, y + i, x);
-      if (menu->submenu[i])
+      if (menu->submenu[j])
        max_width += 2; /* for displaying " >" after the item */
       enabled
-       = (!menu->submenu[i] && menu->panenumber[i]) || (menu->submenu[i]);
+       = (!menu->submenu[j] && menu->panenumber[j]) || (menu->submenu[j]);
       mousehere = (y + i == my && x <= mx && mx < x + max_width);
       face = faces[enabled + mousehere * 2];
       /* Display the menu help string for the i-th menu item even if
@@ -2956,31 +2893,32 @@ tty_menu_display (tty_menu *menu, int x, int y, int pn, int *faces,
         code does.  */
       if (disp_help && enabled + mousehere * 2 >= 2)
        {
-         menu_help_message = menu->help_text[i];
+         menu_help_message = menu->help_text[j];
          menu_help_paneno = pn - 1;
-         menu_help_itemno = i;
+         menu_help_itemno = j;
+       }
+      /* Take note of the coordinates of the active menu item, to
+        display the cursor there.  */
+      if (mousehere)
+       {
+         row = y + i;
+         col = x;
        }
-      display_tty_menu_item (menu->text[i], max_width, face, x, y + i,
-                            menu->submenu[i] != NULL);
+      display_tty_menu_item (menu->text[j], max_width, face, x, y + i,
+                            menu->submenu[j] != NULL);
     }
-  update_frame_with_menu (sf);
-  cursor_to (sf, row, col);
+  update_frame_with_menu (sf, row, col);
 }
 
 /* --------------------------- X Menu emulation ---------------------- */
 
-/* Report availability of menus.  */
-
-int
-have_menus_p (void) {  return 1; }
-
 /* Create a new pane and place it on the outer-most level.  */
 
 static int
 tty_menu_add_pane (tty_menu *menu, const char *txt)
 {
   int len;
-  const char *p;
+  const unsigned char *p;
 
   tty_menu_make_room (menu);
   menu->submenu[menu->count] = tty_menu_create ();
@@ -2990,7 +2928,7 @@ tty_menu_add_pane (tty_menu *menu, const char *txt)
   menu->count++;
 
   /* Update the menu width, if necessary.  */
-  for (len = 0, p = txt; *p; )
+  for (len = 0, p = (unsigned char *) txt; *p; )
     {
       int ch_len;
       int ch = STRING_CHAR_AND_LENGTH (p, ch_len);
@@ -3007,16 +2945,19 @@ tty_menu_add_pane (tty_menu *menu, const char *txt)
 
 /* Create a new item in a menu pane.  */
 
-int
+static bool
 tty_menu_add_selection (tty_menu *menu, int pane,
-                       char *txt, int enable, char const *help_text)
+                       char *txt, bool enable, char const *help_text)
 {
   int len;
-  char *p;
+  unsigned char *p;
 
   if (pane)
-    if (!(menu = tty_menu_search_pane (menu, pane)))
-      return TTYM_FAILURE;
+    {
+      menu = tty_menu_search_pane (menu, pane);
+      if (! menu)
+       return 0;
+    }
   tty_menu_make_room (menu);
   menu->submenu[menu->count] = (tty_menu *) 0;
   menu->text[menu->count] = txt;
@@ -3025,7 +2966,7 @@ tty_menu_add_selection (tty_menu *menu, int pane,
   menu->count++;
 
   /* Update the menu width, if necessary.  */
-  for (len = 0, p = txt; *p; )
+  for (len = 0, p = (unsigned char *) txt; *p; )
     {
       int ch_len;
       int ch = STRING_CHAR_AND_LENGTH (p, ch_len);
@@ -3037,12 +2978,12 @@ tty_menu_add_selection (tty_menu *menu, int pane,
   if (len > menu->width)
     menu->width = len;
 
-  return TTYM_SUCCESS;
+  return 1;
 }
 
 /* Decide where the menu would be placed if requested at (X,Y).  */
 
-void
+static void
 tty_menu_locate (tty_menu *menu, int x, int y,
                 int *ulx, int *uly, int *width, int *height)
 {
@@ -3060,6 +3001,37 @@ struct tty_menu_state
   int x, y;
 };
 
+/* Save away the contents of frame F's current frame matrix, and
+   enable all its rows.  Value is a glyph matrix holding the contents
+   of F's current frame matrix with all its glyph rows enabled.  */
+
+static struct glyph_matrix *
+save_and_enable_current_matrix (struct frame *f)
+{
+  int i;
+  struct glyph_matrix *saved = xzalloc (sizeof *saved);
+  saved->nrows = f->current_matrix->nrows;
+  saved->rows = xzalloc (saved->nrows * sizeof *saved->rows);
+
+  for (i = 0; i < saved->nrows; ++i)
+    {
+      struct glyph_row *from = f->current_matrix->rows + i;
+      struct glyph_row *to = saved->rows + i;
+      ptrdiff_t nbytes = from->used[TEXT_AREA] * sizeof (struct glyph);
+
+      to->glyphs[TEXT_AREA] = xmalloc (nbytes);
+      memcpy (to->glyphs[TEXT_AREA], from->glyphs[TEXT_AREA], nbytes);
+      to->used[TEXT_AREA] = from->used[TEXT_AREA];
+      /* Make sure every row is enabled, or else update_frame will not
+        redraw them.  (Rows that are identical to what is already on
+        screen will not be redrawn anyway.)  */
+      to->enabled_p = true;
+      to->hash = from->hash;
+    }
+
+  return saved;
+}
+
 /* Restore the contents of frame F's desired frame matrix from SAVED,
    and free memory associated with SAVED.  */
 
@@ -3074,33 +3046,12 @@ restore_desired_matrix (struct frame *f, struct glyph_matrix *saved)
       struct glyph_row *to = f->desired_matrix->rows + i;
       ptrdiff_t nbytes = from->used[TEXT_AREA] * sizeof (struct glyph);
 
+      eassert (to->glyphs[TEXT_AREA] != from->glyphs[TEXT_AREA]);
       memcpy (to->glyphs[TEXT_AREA], from->glyphs[TEXT_AREA], nbytes);
       to->used[TEXT_AREA] = from->used[TEXT_AREA];
-      xfree (from->glyphs[TEXT_AREA]);
-      nbytes = from->used[LEFT_MARGIN_AREA];
-      if (nbytes)
-       {
-         memcpy (to->glyphs[LEFT_MARGIN_AREA],
-                 from->glyphs[LEFT_MARGIN_AREA], nbytes);
-         to->used[LEFT_MARGIN_AREA] = from->used[LEFT_MARGIN_AREA];
-         xfree (from->glyphs[LEFT_MARGIN_AREA]);
-       }
-      else
-       to->used[LEFT_MARGIN_AREA] = 0;
-      nbytes = from->used[RIGHT_MARGIN_AREA];
-      if (nbytes)
-       {
-         memcpy (to->glyphs[RIGHT_MARGIN_AREA],
-                 from->glyphs[RIGHT_MARGIN_AREA], nbytes);
-         to->used[RIGHT_MARGIN_AREA] = from->used[RIGHT_MARGIN_AREA];
-         xfree (from->glyphs[RIGHT_MARGIN_AREA]);
-       }
-      else
-       to->used[RIGHT_MARGIN_AREA] = 0;
+      to->enabled_p = from->enabled_p;
+      to->hash = from->hash;
     }
-
-  xfree (saved->rows);
-  xfree (saved);
 }
 
 static void
@@ -3109,20 +3060,13 @@ free_saved_screen (struct glyph_matrix *saved)
   int i;
 
   if (!saved)
-    return;    /* already freed */
+    return;    /* Already freed!  */
 
   for (i = 0; i < saved->nrows; ++i)
     {
       struct glyph_row *from = saved->rows + i;
-      short nbytes;
 
       xfree (from->glyphs[TEXT_AREA]);
-      nbytes = from->used[LEFT_MARGIN_AREA];
-      if (nbytes)
-       xfree (from->glyphs[LEFT_MARGIN_AREA]);
-      nbytes = from->used[RIGHT_MARGIN_AREA];
-      if (nbytes)
-       xfree (from->glyphs[RIGHT_MARGIN_AREA]);
     }
 
   xfree (saved->rows);
@@ -3134,118 +3078,112 @@ static void
 screen_update (struct frame *f, struct glyph_matrix *mtx)
 {
   restore_desired_matrix (f, mtx);
-  update_frame_with_menu (f);
+  update_frame_with_menu (f, -1, -1);
 }
 
+typedef enum {
+  MI_QUIT_MENU      = -1,
+  MI_CONTINUE       = 0,
+  MI_ITEM_SELECTED  = 1,
+  MI_NEXT_ITEM      = 2,
+  MI_PREV_ITEM      = 3,
+  MI_SCROLL_FORWARD = 4,
+  MI_SCROLL_BACK    = 5
+} mi_result;
+
 /* Read user input and return X and Y coordinates where that input
-   puts us.  We only consider mouse movement and click events and
+   puts us.  We only consider mouse movement and click events, and
    keyboard movement commands; the rest are ignored.  */
-static void
-read_menu_input (struct frame *sf, int *x, int *y, bool *first_time)
+static mi_result
+read_menu_input (struct frame *sf, int *x, int *y, int min_y, int max_y,
+                bool *first_time)
 {
-  Lisp_Object c;
-
   if (*first_time)
     {
       *first_time = false;
       sf->mouse_moved = 1;
-      return;
     }
-
-  while (1)
+  else
     {
+      Lisp_Object cmd;
+      bool usable_input = 1;
+      mi_result st = MI_CONTINUE;
+      struct tty_display_info *tty = FRAME_TTY (sf);
+      Lisp_Object saved_mouse_tracking = do_mouse_tracking;
+
+      /* Signal the keyboard reading routines we are displaying a menu
+        on this terminal.  */
+      tty->showing_menu = 1;
+      /* We want mouse movements be reported by read_menu_command.  */
+      do_mouse_tracking = Qt;
       do {
-       c = read_char (-2, Qnil, Qnil, NULL, NULL);
-      } while (BUFFERP (c) || (INTEGERP (c) && XINT (c) == -2));
-
-      if (INTEGERP (c))
+       cmd = read_menu_command ();
+      } while (NILP (cmd));
+      tty->showing_menu = 0;
+      do_mouse_tracking = saved_mouse_tracking;
+
+      if (EQ (cmd, Qt) || EQ (cmd, Qtty_menu_exit)
+         /* If some input switched frames under our feet, exit the
+            menu, since the menu faces are no longer valid, and the
+            menu is no longer relevant anyway.  */
+         || sf != SELECTED_FRAME ())
+       return MI_QUIT_MENU;
+      if (EQ (cmd, Qtty_menu_mouse_movement))
+       mouse_get_xy (x, y);
+      else if (EQ (cmd, Qtty_menu_next_menu))
        {
-         int ch = XINT (c);
-         int usable_input = 1;
-
-         /* FIXME: Exceedingly primitive!  Can we support arrow keys?  */
-         switch (ch && ~CHAR_MODIFIER_MASK)
-           {
-           case 6:     /* ^F */
-             *x += 1;
-             break;
-           case 2:     /* ^B */
-             *x -= 1;
-             break;
-           case 14:    /* ^N */
-             *y += 1;
-             break;
-           case 16:    /* ^P */
-             *y -= 1;
-             break;
-           default:
-             usable_input = 0;
-             break;
-           }
-         if (usable_input)
-           {
-             sf->mouse_moved = 1;
-             return;
-           }
+         usable_input = 0;
+         st = MI_NEXT_ITEM;
        }
-
-      else if (EVENT_HAS_PARAMETERS (c))
+      else if (EQ (cmd, Qtty_menu_prev_menu))
        {
-         if (EQ (EVENT_HEAD (c), Qmouse_movement))
-           {
-           }
-         else if (EQ (EVENT_HEAD_KIND (EVENT_HEAD (c)), Qmouse_click))
-           {
-           }
+         usable_input = 0;
+         st = MI_PREV_ITEM;
+       }
+      else if (EQ (cmd, Qtty_menu_next_item))
+       {
+         if (*y < max_y)
+           *y += 1;
+         else
+           st = MI_SCROLL_FORWARD;
        }
+      else if (EQ (cmd, Qtty_menu_prev_item))
+       {
+         if (*y > min_y)
+           *y -= 1;
+         else
+           st = MI_SCROLL_BACK;
+       }
+      else if (EQ (cmd, Qtty_menu_select))
+       st = MI_ITEM_SELECTED;
+      else if (!EQ (cmd, Qtty_menu_ignore))
+       usable_input = 0;
+      if (usable_input)
+       sf->mouse_moved = 1;
+      return st;
     }
-}
-
-/* FIXME */
-static bool mouse_visible;
-static void
-mouse_off (void)
-{
-  mouse_visible = false;
-}
-
-static void
-mouse_on (void)
-{
-  mouse_visible = true;
-}
-
-static bool
-mouse_pressed (int b, int *x, int *y)
-{
-  return false;
-}
-
-static bool
-mouse_button_depressed (int b, int *x, int *y)
-{
-  return false;
-}
-
-static bool
-mouse_released (int b, int *x, int *y)
-{
-  return true;
+  return MI_CONTINUE;
 }
 
 /* Display menu, wait for user's response, and return that response.  */
-int
+static int
 tty_menu_activate (tty_menu *menu, int *pane, int *selidx,
                   int x0, int y0, char **txt,
-                  void (*help_callback)(char const *, int, int))
+                  void (*help_callback)(char const *, int, int),
+                  bool kbd_navigation)
 {
   struct tty_menu_state *state;
-  int statecount, x, y, i, b, leave, result, onepane;
-  int title_faces[4];          /* face to display the menu title */
+  int statecount, x, y, i;
+  bool leave, onepane;
+  int result IF_LINT (= 0);
+  int title_faces[4];          /* Face to display the menu title.  */
   int faces[4], buffers_num_deleted = 0;
   struct frame *sf = SELECTED_FRAME ();
+  struct tty_display_info *tty = FRAME_TTY (sf);
   bool first_time;
-  Lisp_Object saved_echo_area_message, selectface;
+  Lisp_Object selectface;
+  int first_item = 0;
+  int col, row;
 
   /* Don't allow non-positive x0 and y0, lest the menu will wrap
      around the display.  */
@@ -3254,11 +3192,6 @@ tty_menu_activate (tty_menu *menu, int *pane, int *selidx,
   if (y0 <= 0)
     y0 = 1;
 
-#if 0
-  /* We will process all the mouse events directly.  */
-  mouse_preempted++;
-#endif
-
   state = alloca (menu->panecount * sizeof (struct tty_menu_state));
   memset (state, 0, sizeof (*state));
   faces[0]
@@ -3292,33 +3225,28 @@ tty_menu_activate (tty_menu *menu, int *pane, int *selidx,
       buffers_num_deleted = 1;
     }
 
-#if 0
-  /* We need to save the current echo area message, so that we could
-     restore it below, before we exit.  See the commentary below,
-     before the call to message_with_string.  */
-  saved_echo_area_message = Fcurrent_message ();
-#endif
   /* Force update of the current frame, so that the desired and the
      current matrices are identical.  */
-  update_frame_with_menu (sf);
+  update_frame_with_menu (sf, -1, -1);
   state[0].menu = menu;
-  mouse_off ();        /* FIXME */
-  state[0].screen_behind = save_current_matrix (sf);
-
-  /* Turn off the cursor.  Otherwise it shows through the menu
-     panes, which is ugly.  */
-#if 0
-  show_cursor (0);     /* FIXME: need a new hook, for w32console.  */
-#endif
+  state[0].screen_behind = save_and_enable_current_matrix (sf);
 
   /* Display the menu title.  We subtract 1 from x0 and y0 because we
      want to interpret them as zero-based column and row coordinates,
      and also because we want the first item of the menu, not its
      title, to appear at x0,y0.  */
-  tty_menu_display (menu, x0 - 1, y0 - 1, 1, title_faces, 0);
+  tty_menu_display (menu, x0 - 1, y0 - 1, 1, title_faces, x0 - 1, y0 - 1, 0, 0);
+
+  /* Turn off the cursor.  Otherwise it shows through the menu
+     panes, which is ugly.  */
+  col = cursorX (tty);
+  row = cursorY (tty);
+  tty_hide_cursor (tty);
+
   if (buffers_num_deleted)
     menu->text[0][7] = ' ';
-  if ((onepane = menu->count == 1 && menu->submenu[0]))
+  onepane = menu->count == 1 && menu->submenu[0];
+  if (onepane)
     {
       menu->width = menu->submenu[0]->width;
       state[0].menu = menu->submenu[0];
@@ -3338,18 +3266,67 @@ tty_menu_activate (tty_menu *menu, int *pane, int *selidx,
   leave = 0;
   while (!leave)
     {
-      int mouse_button_count = 3; /* FIXME */
+      mi_result input_status;
+      int min_y = state[0].y;
+      int max_y = min (min_y + state[0].menu->count, FRAME_LINES (sf) - 1) - 1;
 
-      if (!mouse_visible) mouse_on ();
-      read_menu_input (sf, &x, &y, &first_time);
-      if (sf->mouse_moved)
+      input_status = read_menu_input (sf, &x, &y, min_y, max_y, &first_time);
+      if (input_status)
+       {
+         leave = 1;
+         switch (input_status)
+           {
+           case MI_QUIT_MENU:
+             /* Remove the last help-echo, so that it doesn't
+                re-appear after "Quit".  */
+             show_help_echo (Qnil, Qnil, Qnil, Qnil);
+             result = TTYM_NO_SELECT;
+             break;
+           case MI_NEXT_ITEM:
+             if (kbd_navigation)
+               result = TTYM_NEXT;
+             else
+               leave = 0;
+             break;
+           case MI_PREV_ITEM:
+             if (kbd_navigation)
+               result = TTYM_PREV;
+             else
+               leave = 0;
+             break;
+           case MI_SCROLL_FORWARD:
+             if (y - min_y == state[0].menu->count - 1 - first_item)
+               {
+                 y = min_y;
+                 first_item = 0;
+               }
+             else
+               first_item++;
+             leave = 0;
+             break;
+           case MI_SCROLL_BACK:
+             if (first_item == 0)
+               {
+                 y = max_y;
+                 first_item = state[0].menu->count - 1 - (y - min_y);
+               }
+             else
+               first_item--;
+             leave = 0;
+             break;
+           default:
+             /* MI_ITEM_SELECTED is handled below, so nothing to do.  */
+             break;
+           }
+       }
+      if (sf->mouse_moved && input_status != MI_QUIT_MENU)
        {
          sf->mouse_moved = 0;
          result = TTYM_IA_SELECT;
          for (i = 0; i < statecount; i++)
            if (state[i].x <= x && x < state[i].x + state[i].menu->width + 2)
              {
-               int dy = y - state[i].y;
+               int dy = y - state[i].y + first_item;
                if (0 <= dy && dy < state[i].menu->count)
                  {
                    if (!state[i].menu->submenu[dy])
@@ -3365,11 +3342,10 @@ tty_menu_activate (tty_menu *menu, int *pane, int *selidx,
                       have been opened.  That does not include an open and
                       active submenu.  */
                    if (i != statecount - 2
-                       || state[i].menu->submenu[dy] != state[i+1].menu)
+                       || state[i].menu->submenu[dy] != state[i + 1].menu)
                      while (i != statecount - 1)
                        {
                          statecount--;
-                         mouse_off (); /* FIXME */
                          screen_update (sf, state[statecount].screen_behind);
                          state[statecount].screen_behind = NULL;
                        }
@@ -3379,12 +3355,11 @@ tty_menu_activate (tty_menu *menu, int *pane, int *selidx,
                                          state[i].x,
                                          state[i].y,
                                          state[i].pane,
-                                         faces, 1);
+                                         faces, x, y, first_item, 1);
                        state[statecount].menu = state[i].menu->submenu[dy];
                        state[statecount].pane = state[i].menu->panenumber[dy];
-                       mouse_off (); /* FIXME */
                        state[statecount].screen_behind
-                         = save_current_matrix (sf);
+                         = save_and_enable_current_matrix (sf);
                        state[statecount].x
                          = state[i].x + state[i].menu->width + 2;
                        state[statecount].y = y;
@@ -3396,94 +3371,55 @@ tty_menu_activate (tty_menu *menu, int *pane, int *selidx,
                            state[statecount - 1].x,
                            state[statecount - 1].y,
                            state[statecount - 1].pane,
-                           faces, 1);
+                           faces, x, y, first_item, 1);
+         /* The call to display help-echo below will move the cursor,
+            so remember its current position as computed by
+            tty_menu_display.  */
+         col = cursorX (tty);
+         row = cursorY (tty);
        }
-      else
-       {
-         if ((menu_help_message || prev_menu_help_message)
-             && menu_help_message != prev_menu_help_message)
-           {
-             help_callback (menu_help_message,
-                            menu_help_paneno, menu_help_itemno);
-#if 0
-             show_cursor (0);  /* FIXME */
-#endif
-             prev_menu_help_message = menu_help_message;
-           }
-         /* We are busy-waiting for the mouse to move, so let's be nice
-            to other Windows applications by releasing our time slice.  */
-         Sleep (20);   /* FIXME */
-       }
-      for (b = 0; b < mouse_button_count && !leave; b++)
+
+      /* Display the help-echo message for the currently-selected menu
+        item.  */
+      if ((menu_help_message || prev_menu_help_message)
+         && menu_help_message != prev_menu_help_message)
        {
-         /* Only leave if user both pressed and released the mouse, and in
-            that order.  This avoids popping down the menu pane unless
-            the user is really done with it.  */
-         if (mouse_pressed (b, &x, &y))
-           {
-             while (mouse_button_depressed (b, &x, &y))
-               Sleep (20);     /* FIXME */
-             leave = 1;
-           }
-         (void) mouse_released (b, &x, &y);
+         help_callback (menu_help_message,
+                        menu_help_paneno, menu_help_itemno);
+         /* Move the cursor to the beginning of the current menu
+            item, so that screen readers and other accessibility aids
+            know where the active region is.  */
+         cursor_to (sf, row, col);
+         prev_menu_help_message = menu_help_message;
        }
+      /* Both tty_menu_display and help_callback invoke update_end,
+        which calls tty_show_cursor.  Re-hide it, so it doesn't show
+        through the menus.  */
+      tty_hide_cursor (tty);
+      fflush (tty->output);
     }
 
-  mouse_off ();                        /* FIXME */
   sf->mouse_moved = 0;
-  /* FIXME: Since we set the fram's garbaged flag, do we need this
-     call to screen_update?  */
   screen_update (sf, state[0].screen_behind);
-  state[0].screen_behind = NULL;
-#if 0
-  /* We have a situation here.  ScreenUpdate has just restored the
-     screen contents as it was before we started drawing this menu.
-     That includes any echo area message that could have been
-     displayed back then.  (In reality, that echo area message will
-     almost always be the ``keystroke echo'' that echoes the sequence
-     of menu items chosen by the user.)  However, if the menu had some
-     help messages, then displaying those messages caused Emacs to
-     forget about the original echo area message.  So when
-     ScreenUpdate restored it, it created a discrepancy between the
-     actual screen contents and what Emacs internal data structures
-     know about it.
-
-     To avoid this conflict, we force Emacs to restore the original
-     echo area message as we found it when we entered this function.
-     The irony of this is that we then erase the restored message
-     right away, so the only purpose of restoring it is so that
-     erasing it works correctly...  */
-  if (! NILP (saved_echo_area_message))
-    message_with_string ("%s", saved_echo_area_message, 0);
-  message (0);
-#endif
   while (statecount--)
     free_saved_screen (state[statecount].screen_behind);
-#if 0
-  show_cursor (1);     /* turn cursor back on */
-#endif
-  /* Clean up any mouse events that are waiting inside Emacs event queue.
+  tty_show_cursor (tty);       /* Turn cursor back on.  */
+  fflush (tty->output);
+
+/* Clean up any mouse events that are waiting inside Emacs event queue.
      These events are likely to be generated before the menu was even
      displayed, probably because the user pressed and released the button
      (which invoked the menu) too quickly.  If we don't remove these events,
      Emacs will process them after we return and surprise the user.  */
   discard_mouse_events ();
-#if 0
-  mouse_clear_clicks ();
-#endif
   if (!kbd_buffer_events_waiting ())
     clear_input_pending ();
-#if 0
-  /* Allow mouse events generation by dos_rawgetc.  */
-  mouse_preempted--;
-#endif
-  SET_FRAME_GARBAGED (sf);
   return result;
 }
 
 /* Dispose of a menu.  */
 
-void
+static void
 tty_menu_destroy (tty_menu *menu)
 {
   int i;
@@ -3501,18 +3437,10 @@ tty_menu_destroy (tty_menu *menu)
   menu_help_message = prev_menu_help_message = NULL;
 }
 
-static struct frame *tty_menu_help_frame;
-
 /* Show help HELP_STRING, or clear help if HELP_STRING is null.
 
    PANE is the pane number, and ITEM is the menu item number in
-   the menu (currently not used).
-
-   This cannot be done with generating a HELP_EVENT because
-   XMenuActivate contains a loop that doesn't let Emacs process
-   keyboard events.
-
-   FIXME: Do we need this in TTY menus?  */
+   the menu (currently not used).  */
 
 static void
 tty_menu_help_callback (char const *help_string, int pane, int item)
@@ -3546,24 +3474,103 @@ tty_pop_down_menu (Lisp_Object arg)
   unblock_input ();
 }
 
+/* Return the zero-based index of the last menu-bar item on frame F.  */
+static int
+tty_menu_last_menubar_item (struct frame *f)
+{
+  int i = 0;
+
+  eassert (FRAME_TERMCAP_P (f) && FRAME_LIVE_P (f));
+  if (FRAME_TERMCAP_P (f) && FRAME_LIVE_P (f))
+    {
+      Lisp_Object items = FRAME_MENU_BAR_ITEMS (f);
+
+      while (i < ASIZE (items))
+       {
+         Lisp_Object str;
+
+         str = AREF (items, i + 1);
+         if (NILP (str))
+           break;
+         i += 4;
+       }
+      i -= 4;  /* Went one too far!  */
+    }
+  return i;
+}
+
+/* Find in frame F's menu bar the menu item that is next or previous
+   to the item at X/Y, and return that item's position in X/Y.  WHICH
+   says which one--next or previous--item to look for.  X and Y are
+   measured in character cells.  This should only be called on TTY
+   frames.  */
+static void
+tty_menu_new_item_coords (struct frame *f, int which, int *x, int *y)
+{
+  eassert (FRAME_TERMCAP_P (f) && FRAME_LIVE_P (f));
+  if (FRAME_TERMCAP_P (f) && FRAME_LIVE_P (f))
+    {
+      Lisp_Object items = FRAME_MENU_BAR_ITEMS (f);
+      int last_i = tty_menu_last_menubar_item (f);
+      int i, prev_x;
+
+      /* This loop assumes a single menu-bar line, and will fail to
+        find an item if it is not in the first line.  Note that
+        make_lispy_event in keyboard.c makes the same assumption.  */
+      for (i = 0, prev_x = -1; i < ASIZE (items); i += 4)
+       {
+         Lisp_Object pos, str;
+         int ix;
+
+         str = AREF (items, i + 1);
+         pos = AREF (items, i + 3);
+         if (NILP (str))
+           return;
+         ix = XINT (pos);
+         if (ix <= *x
+             /* We use <= so the blank between 2 items on a TTY is
+                considered part of the previous item.  */
+             && *x <= ix + menu_item_width (SDATA (str)))
+           {
+             /* Found current item.  Now compute the X coordinate of
+                the previous or next item.  */
+             if (which == TTYM_NEXT)
+               {
+                 if (i < last_i)
+                   *x = XINT (AREF (items, i + 4 + 3));
+                 else
+                   *x = 0;     /* Wrap around to the first item.  */
+               }
+             else if (prev_x < 0)
+               {
+                 /* Wrap around to the last item.  */
+                 *x = XINT (AREF (items, last_i + 3));
+               }
+             else
+               *x = prev_x;
+             return;
+           }
+         prev_x = ix;
+       }
+    }
+}
+
 Lisp_Object
-tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
-              Lisp_Object title, const char **error_name)
+tty_menu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
+              Lisp_Object title, bool kbd_navigation, const char **error_name)
 {
   tty_menu *menu;
   int pane, selidx, lpane, status;
   Lisp_Object entry, pane_prefix;
   char *datap;
   int ulx, uly, width, height;
+  int item_x, item_y;
   int dispwidth, dispheight;
   int i, j, lines, maxlines;
   int maxwidth;
-  int dummy_int;
-  unsigned int dummy_uint;
-  ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+  ptrdiff_t specpdl_count;
 
-  if (! FRAME_TERMCAP_P (f))
-    emacs_abort ();
+  eassert (FRAME_TERMCAP_P (f));
 
   *error_name = 0;
   if (menu_items_n_panes == 0)
@@ -3585,11 +3592,11 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
 
   /* Don't GC while we prepare and show the menu, because we give the
      menu functions pointers to the contents of strings.  */
-  inhibit_garbage_collection ();
+  specpdl_count = inhibit_garbage_collection ();
 
   /* Adjust coordinates to be root-window-relative.  */
-  x += f->left_pos;
-  y += f->top_pos;
+  item_x = x += f->left_pos;
+  item_y = y += f->top_pos;
 
   /* Create all the necessary panes and their items.  */
   maxwidth = maxlines = lines = i = 0;
@@ -3616,7 +3623,8 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
            {
              tty_menu_destroy (menu);
              *error_name = "Can't create pane";
-             return Qnil;
+             entry = Qnil;
+             goto tty_menu_end;
            }
          i += MENU_ITEMS_PANE_LENGTH;
 
@@ -3659,7 +3667,7 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
 
          if (!NILP (descrip))
            {
-             /* if alloca is fast, use that to make the space,
+             /* If alloca is fast, use that to make the space,
                 to reduce gc needs.  */
              item_data = (char *) alloca (maxwidth + SBYTES (descrip) + 1);
              memcpy (item_data, SSDATA (item_name), SBYTES (item_name));
@@ -3672,13 +3680,13 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
            item_data = SSDATA (item_name);
 
          if (lpane == TTYM_FAILURE
-             || (tty_menu_add_selection (menu, lpane, item_data,
-                                         !NILP (enable), help_string)
-                 == TTYM_FAILURE))
+             || (! tty_menu_add_selection (menu, lpane, item_data,
+                                           !NILP (enable), help_string)))
            {
              tty_menu_destroy (menu);
              *error_name = "Can't add selection to menu";
-             return Qnil;
+             entry = Qnil;
+             goto tty_menu_end;
            }
          i += MENU_ITEMS_ITEM_LENGTH;
           lines++;
@@ -3695,54 +3703,59 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
   x = max (x, 1);
   y = max (y, 1);
   tty_menu_locate (menu, x, y, &ulx, &uly, &width, &height);
-  if (ulx+width > dispwidth)
+  if (ulx + width > dispwidth)
     {
       x -= (ulx + width) - dispwidth;
       ulx = dispwidth - width;
     }
-  if (uly+height > dispheight)
+  if (uly + height > dispheight)
     {
       y -= (uly + height) - dispheight;
       uly = dispheight - height;
     }
 
-  if (FRAME_HAS_MINIBUF_P (f) && uly+height > dispheight - 1)
+  if (FRAME_HAS_MINIBUF_P (f) && uly + height > dispheight - 2)
     {
       /* Move the menu away of the echo area, to avoid overwriting the
         menu with help echo messages or vice versa.  */
       if (BUFFERP (echo_area_buffer[0]) && WINDOWP (echo_area_window))
        {
-         y -= WINDOW_TOTAL_LINES (XWINDOW (echo_area_window));
-         uly -= WINDOW_TOTAL_LINES (XWINDOW (echo_area_window));
+         y -= WINDOW_TOTAL_LINES (XWINDOW (echo_area_window)) + 1;
+         uly -= WINDOW_TOTAL_LINES (XWINDOW (echo_area_window)) + 1;
        }
       else
        {
-         y--;
-         uly--;
+         y -= 2;
+         uly -= 2;
        }
     }
 
   if (ulx < 0) x -= ulx;
   if (uly < 0) y -= uly;
 
+#if 0
+  /* This code doesn't make sense on a TTY, since it can easily annul
+     the adjustments above that carefully avoid truncation of the menu
+     items.  I think it was written to fix some problem that only
+     happens on X11.  */
   if (! for_click)
     {
       /* If position was not given by a mouse click, adjust so upper left
          corner of the menu as a whole ends up at given coordinates.  This
          is what x-popup-menu says in its documentation.  */
-      x += width/2;
-      y += 1.5*height/(maxlines+2);
+      x += width / 2;
+      y += 1.5 * height / (maxlines + 2);
     }
+#endif
 
   pane = selidx = 0;
 
   record_unwind_protect (tty_pop_down_menu, make_save_ptr (menu));
 
-  /* Help display under X won't work because XMenuActivate contains
-     a loop that doesn't give Emacs a chance to process it.  FIXME.  */
-  tty_menu_help_frame = f;
+  specbind (Qoverriding_terminal_local_map,
+           Fsymbol_value (Qtty_menu_navigation_map));
   status = tty_menu_activate (menu, &pane, &selidx, x, y, &datap,
-                             tty_menu_help_callback);
+                             tty_menu_help_callback, kbd_navigation);
   entry = pane_prefix = Qnil;
 
   switch (status)
@@ -3783,11 +3796,22 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
        }
       break;
 
+    case TTYM_NEXT:
+    case TTYM_PREV:
+      tty_menu_new_item_coords (f, status, &item_x, &item_y);
+      entry = Fcons (make_number (item_x), make_number (item_y));
+      break;
+
     case TTYM_FAILURE:
       *error_name = "Can't activate menu";
     case TTYM_IA_SELECT:
       break;
     case TTYM_NO_SELECT:
+      /* If the selected frame was changed while we displayed a menu,
+        throw to top level in order to undo any temporary settings
+        made by TTY menu code.  */
+      if (f != SELECTED_FRAME ())
+       Ftop_level ();
       /* Make "Cancel" equivalent to C-g unless FOR_CLICK (which means
         the menu was invoked with a mouse event as POSITION).  */
       if (! for_click)
@@ -3795,12 +3819,13 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
       break;
     }
 
-  unbind_to (specpdl_count, Qnil);
+ tty_menu_end:
 
+  unbind_to (specpdl_count, Qnil);
   return entry;
 }
 
-#endif /* HAVE_MENUS && !MSDOS */
+#endif /* !MSDOS */
 
 \f
 #ifndef MSDOS
@@ -3809,7 +3834,7 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
  ***********************************************************************/
 
 /* Initialize the tty-dependent part of frame F.  The frame must
-   already have its device initialized. */
+   already have its device initialized.  */
 
 void
 create_tty_output (struct frame *f)
@@ -3823,30 +3848,25 @@ create_tty_output (struct frame *f)
   f->output_data.tty = t;
 }
 
-/* Delete frame F's face cache, and its tty-dependent part. */
+/* Delete frame F's face cache, and its tty-dependent part.  */
 
 static void
 tty_free_frame_resources (struct frame *f)
 {
   eassert (FRAME_TERMCAP_P (f));
-
-  if (FRAME_FACE_CACHE (f))
-    free_frame_faces (f);
-
+  free_frame_faces (f);
   xfree (f->output_data.tty);
 }
 
 #else  /* MSDOS */
 
-/* Delete frame F's face cache. */
+/* Delete frame F's face cache.  */
 
 static void
 tty_free_frame_resources (struct frame *f)
 {
   eassert (FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f));
-
-  if (FRAME_FACE_CACHE (f))
-    free_frame_faces (f);
+  free_frame_faces (f);
 }
 #endif /* MSDOS */
 \f
@@ -3965,12 +3985,8 @@ dissociate_if_controlling_tty (int fd)
 struct terminal *
 init_tty (const char *name, const char *terminal_type, bool must_succeed)
 {
-#ifdef TERMINFO
-  char **address = 0;
-#else
   char *area;
   char **address = &area;
-#endif
   int status;
   struct tty_display_info *tty = NULL;
   struct terminal *terminal = NULL;
@@ -4061,13 +4077,9 @@ init_tty (const char *name, const char *terminal_type, bool must_succeed)
   /* On some systems, tgetent tries to access the controlling
      terminal.  */
   block_tty_out_signal ();
-#ifdef TERMINFO
-  status = tgetent (0, terminal_type);
-#else
   status = tgetent (tty->termcap_term_buffer, terminal_type);
   if (tty->termcap_term_buffer[TERMCAP_BUFFER_SIZE - 1])
     emacs_abort ();
-#endif
   unblock_tty_out_signal ();
 
   if (status < 0)
@@ -4098,9 +4110,7 @@ use the Bourne shell command `TERM=... export TERM' (C-shell:\n\
                    terminal_type);
     }
 
-#ifndef TERMINFO
   area = tty->termcap_strings_buffer;
-#endif
   tty->TS_ins_line = tgetstr ("al", address);
   tty->TS_ins_multi_lines = tgetstr ("AL", address);
   tty->TS_bell = tgetstr ("bl", address);
@@ -4199,9 +4209,9 @@ use the Bourne shell command `TERM=... export TERM' (C-shell:\n\
   /* Since we make MagicWrap terminals look like AutoWrap, we need to have
      the former flag imply the latter.  */
   AutoWrap (tty) = MagicWrap (tty) || tgetflag ("am");
-  terminal->memory_below_frame = tgetflag ("db");
+  tty->memory_below_frame = tgetflag ("db");
   tty->TF_hazeltine = tgetflag ("hz");
-  terminal->must_write_spaces = tgetflag ("in");
+  tty->must_write_spaces = tgetflag ("in");
   tty->meta_key = tgetflag ("km") || tgetflag ("MT");
   tty->TF_insmode_motion = tgetflag ("mi");
   tty->TF_standout_motion = tgetflag ("ms");
@@ -4221,7 +4231,7 @@ use the Bourne shell command `TERM=... export TERM' (C-shell:\n\
     tty->specified_window = height;
 
     FRAME_VERTICAL_SCROLL_BAR_TYPE (f) = vertical_scroll_bar_none;
-    terminal->char_ins_del_ok = 1;
+    tty->char_ins_del_ok = 1;
     baud_rate = 19200;
   }
 #else  /* MSDOS */
@@ -4234,7 +4244,7 @@ use the Bourne shell command `TERM=... export TERM' (C-shell:\n\
     get_tty_size (fileno (tty->input), &width, &height);
     FrameCols (tty) = width;
     FrameRows (tty) = height;
-    terminal->char_ins_del_ok = 0;
+    tty->char_ins_del_ok = 0;
     init_baud_rate (fileno (tty->input));
   }
 #endif /* MSDOS */
@@ -4253,12 +4263,12 @@ use the Bourne shell command `TERM=... export TERM' (C-shell:\n\
   tty->delete_in_insert_mode = 1;
 
   UseTabs (tty) = 0;
-  terminal->scroll_region_ok = 0;
+  tty->scroll_region_ok = 0;
 
   /* Seems to insert lines when it's not supposed to, messing up the
      display.  In doing a trace, it didn't seem to be called much, so I
      don't think we're losing anything by turning it off.  */
-  terminal->line_ins_del_ok = 0;
+  tty->line_ins_del_ok = 0;
 
   tty->TN_max_colors = 16;  /* Must be non-zero for tty-display-color-p.  */
 #endif /* DOS_NT */
@@ -4268,11 +4278,7 @@ use the Bourne shell command `TERM=... export TERM' (C-shell:\n\
   tty->mouse_highlight.mouse_face_window = Qnil;
 #endif
 
-  terminal->kboard = xmalloc (sizeof *terminal->kboard);
-  init_kboard (terminal->kboard);
-  kset_window_system (terminal->kboard, Qnil);
-  terminal->kboard->next_kboard = all_kboards;
-  all_kboards = terminal->kboard;
+  terminal->kboard = allocate_kboard (Qnil);
   terminal->kboard->reference_count++;
   /* Don't let the initial kboard remain current longer than necessary.
      That would cause problems if a file loaded on startup tries to
@@ -4393,23 +4399,21 @@ use the Bourne shell command `TERM=... export TERM' (C-shell:\n\
 
   UseTabs (tty) = tabs_safe_p (fileno (tty->input)) && TabWidth (tty) == 8;
 
-  terminal->scroll_region_ok
+  tty->scroll_region_ok
     = (tty->Wcm->cm_abs
        && (tty->TS_set_window || tty->TS_set_scroll_region || tty->TS_set_scroll_region_1));
 
-  terminal->line_ins_del_ok
+  tty->line_ins_del_ok
     = (((tty->TS_ins_line || tty->TS_ins_multi_lines)
         && (tty->TS_del_line || tty->TS_del_multi_lines))
-       || (terminal->scroll_region_ok
+       || (tty->scroll_region_ok
            && tty->TS_fwd_scroll && tty->TS_rev_scroll));
 
-  terminal->char_ins_del_ok
+  tty->char_ins_del_ok
     = ((tty->TS_ins_char || tty->TS_insert_mode
         || tty->TS_pad_inserted_char || tty->TS_ins_multi_chars)
        && (tty->TS_del_char || tty->TS_del_multi_chars));
 
-  terminal->fast_clear_end_of_line = tty->TS_clr_line != 0;
-
   init_baud_rate (fileno (tty->input));
 
 #endif /* not DOS_NT */
@@ -4572,4 +4576,16 @@ bigger, or it may make it blink, or it may do nothing at all.  */);
 
   encode_terminal_src = NULL;
   encode_terminal_dst = NULL;
+
+#ifndef MSDOS
+  DEFSYM (Qtty_menu_next_item, "tty-menu-next-item");
+  DEFSYM (Qtty_menu_prev_item, "tty-menu-prev-item");
+  DEFSYM (Qtty_menu_next_menu, "tty-menu-next-menu");
+  DEFSYM (Qtty_menu_prev_menu, "tty-menu-prev-menu");
+  DEFSYM (Qtty_menu_select, "tty-menu-select");
+  DEFSYM (Qtty_menu_ignore, "tty-menu-ignore");
+  DEFSYM (Qtty_menu_exit, "tty-menu-exit");
+  DEFSYM (Qtty_menu_mouse_movement, "tty-menu-mouse-movement");
+  DEFSYM (Qtty_menu_navigation_map, "tty-menu-navigation-map");
+#endif
 }