use xmalloc_atomic for many pointerless objects
[bpt/emacs.git] / src / term.c
index 451d608..500c1f2 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"
@@ -127,6 +131,9 @@ enum no_color_bit
 
 static int max_frame_cols;
 
+static Lisp_Object Qtty_mode_set_strings;
+static Lisp_Object Qtty_mode_reset_strings;
+
 \f
 
 #ifdef HAVE_GPM
@@ -157,6 +164,28 @@ tty_ring_bell (struct frame *f)
 
 /* Set up termcap modes for Emacs. */
 
+static void
+tty_send_additional_strings (struct terminal *terminal, Lisp_Object sym)
+{
+  Lisp_Object lisp_terminal;
+  Lisp_Object extra_codes;
+  struct tty_display_info *tty = terminal->display_info.tty;
+
+  XSETTERMINAL (lisp_terminal, terminal);
+  for (extra_codes = Fterminal_parameter (lisp_terminal, sym);
+       CONSP (extra_codes);
+       extra_codes = XCDR (extra_codes))
+    {
+      Lisp_Object string = XCAR (extra_codes);
+      if (STRINGP (string))
+        {
+          fwrite (SDATA (string), 1, SBYTES (string), tty->output);
+          if (tty->termscript)
+            fwrite (SDATA (string), 1, SBYTES (string), tty->termscript);
+        }
+    }
+}
+
 static void
 tty_set_terminal_modes (struct terminal *terminal)
 {
@@ -179,6 +208,7 @@ tty_set_terminal_modes (struct terminal *terminal)
       OUTPUT_IF (tty, visible_cursor ? tty->TS_cursor_visible : tty->TS_cursor_normal);
       OUTPUT_IF (tty, tty->TS_keypad_mode);
       losecursor (tty);
+      tty_send_additional_strings (terminal, Qtty_mode_set_strings);
       fflush (tty->output);
     }
 }
@@ -192,6 +222,7 @@ tty_reset_terminal_modes (struct terminal *terminal)
 
   if (tty->output)
     {
+      tty_send_additional_strings (terminal, Qtty_mode_reset_strings);
       tty_turn_off_highlight (tty);
       tty_turn_off_insert (tty);
       OUTPUT_IF (tty, tty->TS_end_keypad_mode);
@@ -216,6 +247,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. */
@@ -496,7 +528,8 @@ static ptrdiff_t encode_terminal_dst_size;
    sequence, and return a pointer to that byte sequence.  */
 
 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;
@@ -1330,7 +1363,7 @@ term_get_fkeys_1 (void)
   if (!KEYMAPP (KVAR (kboard, Vinput_decode_map)))
     kset_input_decode_map (kboard, Fmake_sparse_keymap (Qnil));
 
-  for (i = 0; i < (sizeof (keys) / sizeof (keys[0])); i++)
+  for (i = 0; i < ARRAYELTS (keys); i++)
     {
       char *sequence = tgetstr (keys[i].cap, address);
       if (sequence)
@@ -1413,18 +1446,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
   }
 
@@ -1810,27 +1831,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
@@ -1857,7 +1861,7 @@ produce_glyphless_glyph (struct it *it, Lisp_Object acronym)
            acronym = XCDR (acronym);
          buf[0] = '[';
          str = STRINGP (acronym) ? SSDATA (acronym) : "";
-         for (len = 0; len < 6 && str[len] && ASCII_BYTE_P (str[len]); len++)
+         for (len = 0; len < 6 && str[len] && ASCII_CHAR_P (str[len]); len++)
            buf[1 + len] = str[len];
          buf[1 + len] = ']';
          len += 2;
@@ -1900,55 +1904,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);
@@ -1973,7 +1940,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);
@@ -1981,7 +1948,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);
@@ -2035,12 +2002,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)))        \
@@ -2111,17 +2076,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;
@@ -2454,7 +2411,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);
        }
 
@@ -2786,17 +2743,17 @@ 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 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 directly 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
+   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
    selection is made and the menu should be popped down.
 
@@ -2806,6 +2763,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;
@@ -2813,15 +2772,19 @@ static const char *menu_help_message, *prev_menu_help_message;
    last menu help message.  */
 static int menu_help_paneno, menu_help_itemno;
 
-static int menu_x, menu_y;
+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;
@@ -2832,38 +2795,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;
     }
 }
 
@@ -2880,7 +2832,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;
@@ -2893,7 +2846,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++)
     {
@@ -2904,61 +2857,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);
-    }
-  else
-    {
-      mx = menu_x;
-      my = menu_y;
+      *x = XINT (lmx);
+      *y = XINT (lmy);
     }
-#else
-  mx = menu_x;
-  my = menu_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
@@ -2966,31 +2917,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 ();
@@ -3000,7 +2952,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);
@@ -3017,16 +2969,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;
@@ -3035,7 +2990,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);
@@ -3047,12 +3002,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)
 {
@@ -3070,6 +3025,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.  */
 
@@ -3089,33 +3075,7 @@ restore_desired_matrix (struct frame *f, struct glyph_matrix *saved)
       to->used[TEXT_AREA] = from->used[TEXT_AREA];
       to->enabled_p = from->enabled_p;
       to->hash = from->hash;
-      xfree (from->glyphs[TEXT_AREA]);
-      nbytes = from->used[LEFT_MARGIN_AREA] * sizeof (struct glyph);
-      if (nbytes)
-       {
-         eassert (to->glyphs[LEFT_MARGIN_AREA] != from->glyphs[LEFT_MARGIN_AREA]);
-         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] * sizeof (struct glyph);
-      if (nbytes)
-       {
-         eassert (to->glyphs[RIGHT_MARGIN_AREA] != from->glyphs[RIGHT_MARGIN_AREA]);
-         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;
     }
-
-  xfree (saved->rows);
-  xfree (saved);
 }
 
 static void
@@ -3124,17 +3084,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;
 
       xfree (from->glyphs[TEXT_AREA]);
-      if (from->used[LEFT_MARGIN_AREA])
-       xfree (from->glyphs[LEFT_MARGIN_AREA]);
-      if (from->used[RIGHT_MARGIN_AREA])
-       xfree (from->glyphs[RIGHT_MARGIN_AREA]);
     }
 
   xfree (saved->rows);
@@ -3146,140 +3102,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);
 }
 
-/* Read user input and return X and Y coordinates where that input
-   puts us.  We only consider mouse movement and click events and
-   keyboard movement commands; the rest are ignored.
+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;
 
-   Value is -1 if C-g was pressed, zero otherwise.  */
-static int
-read_menu_input (struct frame *sf, int *x, int *y, bool *first_time)
+/* Read user input and return X and Y coordinates where that input
+   puts us.  We only consider mouse movement and click events, and
+   keyboard movement commands; the rest are ignored.  */
+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;
-      /* FIXME: Following 2 or 3 lines are temporary!  */
-      menu_x = *x;
-      menu_y = *y;
       sf->mouse_moved = 1;
-      return 0;
     }
-
-  while (1)
+  else
     {
-#if 0
+      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 7:     /* ^G */
-             return -1;
-           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;
-         break;
+         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
-      int volatile dx = 0;
-      int volatile dy = 0;
-      int volatile st = 0;
-
-      *x += dx;
-      *y += dy;
-      if (dx != 0 || dy != 0)
+      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;
-      menu_x = *x;
-      menu_y = *y;
-      Sleep (300);
       return st;
-#endif
     }
-  return 0;
-}
-
-/* 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.  */
@@ -3288,11 +3216,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]
@@ -3326,31 +3249,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.  */
-  tty_hide_cursor (tty);
+  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];
@@ -3370,19 +3290,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 ();
-      if (read_menu_input (sf, &x, &y, &first_time) == -1)
-       leave = 1;
-      else 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])
@@ -3398,11 +3366,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;
                        }
@@ -3412,12 +3379,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;
@@ -3429,68 +3395,40 @@ 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);
-             tty_hide_cursor (tty);
-             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);
-  tty_show_cursor (tty);       /* turn cursor back on */
+  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
@@ -3498,22 +3436,14 @@ tty_menu_activate (tty_menu *menu, int *pane, int *selidx,
      (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;
@@ -3531,18 +3461,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)
@@ -3576,8 +3498,90 @@ 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;
+       }
+    }
+}
+
+/* WINDOWSNT uses this as menu_show_hook, see w32console.c.  */
 Lisp_Object
-tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
+tty_menu_show (struct frame *f, int x, int y, int menuflags,
               Lisp_Object title, const char **error_name)
 {
   tty_menu *menu;
@@ -3585,15 +3589,13 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
   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)
@@ -3613,13 +3615,15 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
       return Qnil;
     }
 
-  /* 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 = SPECPDL_INDEX ();
+
+  /* Avoid crashes if, e.g., another client will connect while we
+     are in a menu.  */
+  temporarily_switch_to_single_kboard (f);
 
   /* 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;
@@ -3638,7 +3642,7 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
          prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
          pane_string = (NILP (pane_name)
                         ? "" : SSDATA (pane_name));
-         if (keymaps && !NILP (prefix))
+         if ((menuflags & MENU_KEYMAPS) && !NILP (prefix))
            pane_string++;
 
          lpane = tty_menu_add_pane (menu, pane_string);
@@ -3646,7 +3650,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;
 
@@ -3689,7 +3694,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));
@@ -3702,13 +3707,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++;
@@ -3725,30 +3730,30 @@ 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;
        }
     }
 
@@ -3758,14 +3763,15 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
 #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.  */
+     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
 
@@ -3773,11 +3779,11 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
 
   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,
+                             menuflags & MENU_KBD_NAVIGATION);
   entry = pane_prefix = Qnil;
 
   switch (status)
@@ -3803,7 +3809,7 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
                    {
                      entry
                        = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
-                     if (keymaps != 0)
+                     if (menuflags & MENU_KEYMAPS)
                        {
                          entry = Fcons (entry, Qnil);
                          if (!NILP (pane_prefix))
@@ -3818,24 +3824,36 @@ 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)
+      if (!(menuflags & MENU_FOR_CLICK))
         Fsignal (Qquit, Qnil);
       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
@@ -3844,7 +3862,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)
@@ -3858,30 +3876,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
@@ -3910,6 +3923,7 @@ clear_tty_hooks (struct terminal *terminal)
   terminal->frame_rehighlight_hook = 0;
   terminal->frame_raise_lower_hook = 0;
   terminal->fullscreen_hook = 0;
+  terminal->menu_show_hook = 0;
   terminal->set_vertical_scroll_bar_hook = 0;
   terminal->condemn_scroll_bars_hook = 0;
   terminal->redeem_scroll_bar_hook = 0;
@@ -3928,43 +3942,25 @@ clear_tty_hooks (struct terminal *terminal)
 static void
 set_tty_hooks (struct terminal *terminal)
 {
-  terminal->rif = 0; /* ttys don't support window-based redisplay. */
-
   terminal->cursor_to_hook = &tty_cursor_to;
   terminal->raw_cursor_to_hook = &tty_raw_cursor_to;
-
   terminal->clear_to_end_hook = &tty_clear_to_end;
   terminal->clear_frame_hook = &tty_clear_frame;
   terminal->clear_end_of_line_hook = &tty_clear_end_of_line;
-
   terminal->ins_del_lines_hook = &tty_ins_del_lines;
-
   terminal->insert_glyphs_hook = &tty_insert_glyphs;
   terminal->write_glyphs_hook = &tty_write_glyphs;
   terminal->delete_glyphs_hook = &tty_delete_glyphs;
-
   terminal->ring_bell_hook = &tty_ring_bell;
-
   terminal->reset_terminal_modes_hook = &tty_reset_terminal_modes;
   terminal->set_terminal_modes_hook = &tty_set_terminal_modes;
-  terminal->update_begin_hook = 0; /* Not needed. */
   terminal->update_end_hook = &tty_update_end;
+  terminal->menu_show_hook = &tty_menu_show;
   terminal->set_terminal_window_hook = &tty_set_terminal_window;
-
-  terminal->mouse_position_hook = 0; /* Not needed. */
-  terminal->frame_rehighlight_hook = 0; /* Not needed. */
-  terminal->frame_raise_lower_hook = 0; /* Not needed. */
-
-  terminal->set_vertical_scroll_bar_hook = 0; /* Not needed. */
-  terminal->condemn_scroll_bars_hook = 0; /* Not needed. */
-  terminal->redeem_scroll_bar_hook = 0; /* Not needed. */
-  terminal->judge_scroll_bars_hook = 0; /* Not needed. */
-
   terminal->read_socket_hook = &tty_read_avail_input; /* keyboard.c */
-  terminal->frame_up_to_date_hook = 0; /* Not needed. */
-
   terminal->delete_frame_hook = &tty_free_frame_resources;
   terminal->delete_terminal_hook = &delete_tty;
+  /* Other hooks are NULL by default.  */
 }
 
 /* If FD is the controlling terminal, drop it.  */
@@ -3979,9 +3975,10 @@ dissociate_if_controlling_tty (int fd)
       /* setsid failed, presumably because Emacs is already a process
         group leader.  Fall back on the obsolescent way to dissociate
         a controlling tty.  */
-      block_tty_out_signal ();
+      sigset_t oldset;
+      block_tty_out_signal (&oldset);
       ioctl (fd, TIOCNOTTY, 0);
-      unblock_tty_out_signal ();
+      unblock_tty_out_signal (&oldset);
 #endif
     }
 }
@@ -4000,15 +3997,12 @@ 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;
+  sigset_t oldset;
   bool ctty = false;  /* True if asked to open controlling tty.  */
 
   if (!terminal_type)
@@ -4030,7 +4024,7 @@ init_tty (const char *name, const char *terminal_type, bool must_succeed)
   if (terminal)
     return terminal;
 
-  terminal = create_terminal ();
+  terminal = create_terminal (output_termcap, NULL);
 #ifdef MSDOS
   if (been_here > 0)
     maybe_fatal (0, 0, "Attempt to create another terminal %s", "",
@@ -4044,7 +4038,6 @@ init_tty (const char *name, const char *terminal_type, bool must_succeed)
   tty->next = tty_list;
   tty_list = tty;
 
-  terminal->type = output_termcap;
   terminal->display_info.tty = tty;
   tty->terminal = terminal;
 
@@ -4070,12 +4063,15 @@ init_tty (const char *name, const char *terminal_type, bool must_succeed)
        open a frame on the same terminal.  */
     int flags = O_RDWR | O_NOCTTY | (ctty ? 0 : O_IGNORE_CTTY);
     int fd = emacs_open (name, flags, 0);
-    tty->input = tty->output = fd < 0 || ! isatty (fd) ? 0 : fdopen (fd, "w+");
+    tty->input = tty->output =
+      ((fd < 0 || ! isatty (fd))
+       ? NULL
+       : fdopen (fd, "w+"));
 
     if (! tty->input)
       {
        char const *diagnostic
-         = tty->input ? "Not a tty device: %s" : "Could not open file: %s";
+         = (fd < 0) ? "Could not open file: %s" : "Not a tty device: %s";
        emacs_close (fd);
        maybe_fatal (must_succeed, terminal, diagnostic, diagnostic, name);
       }
@@ -4095,15 +4091,11 @@ 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
+  block_tty_out_signal (&oldset);
   status = tgetent (tty->termcap_term_buffer, terminal_type);
   if (tty->termcap_term_buffer[TERMCAP_BUFFER_SIZE - 1])
     emacs_abort ();
-#endif
-  unblock_tty_out_signal ();
+  unblock_tty_out_signal (&oldset);
 
   if (status < 0)
     {
@@ -4133,9 +4125,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);
@@ -4234,9 +4224,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");
@@ -4256,7 +4246,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 */
@@ -4269,7 +4259,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 */
@@ -4288,12 +4278,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 */
@@ -4303,11 +4293,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
@@ -4428,23 +4414,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 */
@@ -4607,4 +4591,19 @@ bigger, or it may make it blink, or it may do nothing at all.  */);
 
   encode_terminal_src = NULL;
   encode_terminal_dst = NULL;
+
+  DEFSYM (Qtty_mode_set_strings, "tty-mode-set-strings");
+  DEFSYM (Qtty_mode_reset_strings, "tty-mode-reset-strings");
+
+#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
 }