emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

tty-child-frames beb5b771236: Preview from my branch on github


From: Gerd Moellmann
Subject: tty-child-frames beb5b771236: Preview from my branch on github
Date: Sat, 5 Oct 2024 12:58:36 -0400 (EDT)

branch: tty-child-frames
commit beb5b77123619758c1def6b9f227fa7568516775
Author: Gerd Möllmann <gerd@gnu.org>
Commit: Gerd Möllmann <gerd@gnu.org>

    Preview from my branch on github
---
 lisp/frame.el    |  47 +--
 lisp/xt-mouse.el |  11 +-
 src/alloc.c      |  12 +-
 src/dispextern.h |  45 ++-
 src/dispnew.c    | 979 ++++++++++++++++++++++++++++++++++++-------------------
 src/frame.c      | 407 +++++++++++++++++------
 src/frame.h      |  65 ++--
 src/keyboard.c   |   2 +-
 src/minibuf.c    |   6 +-
 src/nsfns.m      |   4 +-
 src/nsterm.m     |   4 +-
 src/scroll.c     |   4 +-
 src/term.c       | 253 ++++++++++++--
 src/terminal.c   |   6 +-
 src/xdisp.c      |  76 +++--
 src/xfaces.c     |   1 -
 16 files changed, 1355 insertions(+), 567 deletions(-)

diff --git a/lisp/frame.el b/lisp/frame.el
index 1b5aa8cff08..161b5acc42e 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -1495,6 +1495,13 @@ FRAME defaults to the selected frame."
   (let ((edges (frame-edges frame 'outer-edges)))
     (- (nth 3 edges) (nth 1 edges))))
 
+(defun frame-at (x y)
+  "Return frame containing pixel position X, Y."
+  (cl-loop for frame in (frame-list-z-order)
+           as (x0 y0 x1 y1) = (frame-edges frame)
+           when (and (<= x0 x (1- x1)) (<= y0 y (1- y1)))
+           return frame))
+
 (declare-function x-list-fonts "xfaces.c"
                   (pattern &optional face frame maximum width))
 
@@ -1722,6 +1729,7 @@ live frame and defaults to the selected one."
 (declare-function pgtk-frame-geometry "pgtkfns.c" (&optional frame))
 (declare-function haiku-frame-geometry "haikufns.c" (&optional frame))
 (declare-function android-frame-geometry "androidfns.c" (&optional frame))
+(declare-function tty-frame-geometry "term.c" (&optional frame))
 
 (defun frame-geometry (&optional frame)
   "Return geometric attributes of FRAME.
@@ -1778,24 +1786,7 @@ and width values are in pixels.
      ((eq frame-type 'android)
       (android-frame-geometry frame))
      (t
-      (list
-       '(outer-position 0 . 0)
-       (cons 'outer-size (cons (frame-width frame) (frame-height frame)))
-       '(external-border-size 0 . 0)
-       '(outer-border-width . 0)
-       '(title-bar-size 0 . 0)
-       '(menu-bar-external . nil)
-       (let ((menu-bar-lines (frame-parameter frame 'menu-bar-lines)))
-        (cons 'menu-bar-size
-              (if menu-bar-lines
-                  (cons (frame-width frame) 1)
-                1 0)))
-       '(tool-bar-external . nil)
-       '(tool-bar-position . nil)
-       '(tool-bar-size 0 . 0)
-       '(tab-bar-size 0 . 0)
-       (cons 'internal-border-width
-            (frame-parameter frame 'internal-border-width)))))))
+      (tty-frame-geometry frame)))))
 
 (defun frame--size-history (&optional frame)
   "Print history of resize operations for FRAME.
@@ -1904,6 +1895,7 @@ of frames like calls to map a frame or change its 
visibility."
 (declare-function pgtk-frame-edges "pgtkfns.c" (&optional frame type))
 (declare-function haiku-frame-edges "haikufns.c" (&optional frame type))
 (declare-function android-frame-edges "androidfns.c" (&optional frame type))
+(declare-function tty-frame-edges "term.c" (&optional frame type))
 
 (defun frame-edges (&optional frame type)
   "Return coordinates of FRAME's edges.
@@ -1934,7 +1926,7 @@ FRAME."
      ((eq frame-type 'android)
       (android-frame-edges frame type))
      (t
-      (list 0 0 (frame-width frame) (frame-height frame))))))
+      (tty-frame-edges frame type)))))
 
 (declare-function w32-mouse-absolute-pixel-position "w32fns.c")
 (declare-function x-mouse-absolute-pixel-position "xfns.c")
@@ -2087,6 +2079,7 @@ workarea attribute."
 ;; (declare-function pgtk-frame-list-z-order "pgtkfns.c" (&optional display))
 (declare-function haiku-frame-list-z-order "haikufns.c" (&optional display))
 (declare-function android-frame-list-z-order "androidfns.c" (&optional 
display))
+(declare-function tty-frame-list-z-order "term.c" (&optional display))
 
 (defun frame-list-z-order (&optional display)
   "Return list of Emacs's frames, in Z (stacking) order.
@@ -2114,7 +2107,9 @@ Return nil if DISPLAY contains no Emacs frame."
      ((eq frame-type 'haiku)
       (haiku-frame-list-z-order display))
      ((eq frame-type 'android)
-      (android-frame-list-z-order display)))))
+      (android-frame-list-z-order display))
+     (t
+      (tty-frame-list-z-order display)))))
 
 (declare-function x-frame-restack "xfns.c" (frame1 frame2 &optional above))
 (declare-function w32-frame-restack "w32fns.c" (frame1 frame2 &optional above))
@@ -2123,6 +2118,7 @@ Return nil if DISPLAY contains no Emacs frame."
 (declare-function haiku-frame-restack "haikufns.c" (frame1 frame2 &optional 
above))
 (declare-function android-frame-restack "androidfns.c" (frame1 frame2
                                                                &optional 
above))
+(declare-function tty-frame-restack "term.c" (frame1 frame2 &optional above))
 
 (defun frame-restack (frame1 frame2 &optional above)
   "Restack FRAME1 below FRAME2.
@@ -2146,6 +2142,7 @@ Some window managers may refuse to restack windows."
            (equal (frame-parameter frame1 'display)
                   (frame-parameter frame2 'display)))
       (let ((frame-type (framep-on-display frame1)))
+        ;; FIXME/tty: not implemented for ttys.
         (cond
          ((eq frame-type 'x)
           (x-frame-restack frame1 frame2 above))
@@ -2158,7 +2155,9 @@ Some window managers may refuse to restack windows."
          ((eq frame-type 'pgtk)
           (pgtk-frame-restack frame1 frame2 above))
          ((eq frame-type 'android)
-          (android-frame-restack frame1 frame2 above))))
+          (android-frame-restack frame1 frame2 above))
+         (t
+          (tty-frame-restack frame1 frame2 above))))
     (error "Cannot restack frames")))
 
 (defun frame-size-changed-p (&optional frame)
@@ -2311,6 +2310,7 @@ If DISPLAY is omitted or nil, it defaults to the selected 
frame's display."
       1))))
 
 (declare-function x-display-pixel-height "xfns.c" (&optional terminal))
+(declare-function tty-display-pixel-height "term.c" (&optional terminal))
 
 (defun display-pixel-height (&optional display)
   "Return the height of DISPLAY's screen in pixels.
@@ -2328,9 +2328,10 @@ with DISPLAY.  To get information for each physical 
monitor, use
      ((memq frame-type '(x w32 ns haiku pgtk android))
       (x-display-pixel-height display))
      (t
-      (frame-height (if (framep display) display (selected-frame)))))))
+      (tty-display-pixel-height display)))))
 
 (declare-function x-display-pixel-width "xfns.c" (&optional terminal))
+(declare-function tty-display-pixel-width "term.c" (&optional terminal))
 
 (defun display-pixel-width (&optional display)
   "Return the width of DISPLAY's screen in pixels.
@@ -2348,7 +2349,7 @@ with DISPLAY.  To get information for each physical 
monitor, use
      ((memq frame-type '(x w32 ns haiku pgtk android))
       (x-display-pixel-width display))
      (t
-      (frame-width (if (framep display) display (selected-frame)))))))
+      (tty-display-pixel-width display)))))
 
 (defcustom display-mm-dimensions-alist nil
   "Alist for specifying screen dimensions in millimeters.
diff --git a/lisp/xt-mouse.el b/lisp/xt-mouse.el
index 8cbb44ece14..4f32d3b5fda 100644
--- a/lisp/xt-mouse.el
+++ b/lisp/xt-mouse.el
@@ -293,7 +293,16 @@ which is the \"1006\" extension implemented in Xterm >= 
277."
                            (progn (setq xt-mouse-epoch (float-time)) 0)
                          (car (time-convert (time-since xt-mouse-epoch)
                                             1000))))
-             (w (window-at x y))
+             (frame (frame-at x y))
+             ;;(_ (message (format "*** %S" frame)))
+             (frame-pos (frame-position frame))
+             ;;(_ (message (format "*** %S" frame-pos)))
+             (x (- x (car frame-pos)))
+             (y (- y (cdr frame-pos)))
+             ;;(_ (message (format "*** %S %S" x y)))
+             (w (window-at x y frame))
+             ;;(_ (message (format "*** %S" w)))
+
              (ltrb (window-edges w))
              (left (nth 0 ltrb))
              (top (nth 1 ltrb))
diff --git a/src/alloc.c b/src/alloc.c
index 4fab0d54248..ac5cf06805b 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -6853,9 +6853,15 @@ mark_glyph_matrix (struct glyph_matrix *matrix)
            struct glyph *end_glyph = glyph + row->used[area];
 
            for (; glyph < end_glyph; ++glyph)
-             if (STRINGP (glyph->object)
-                 && !string_marked_p (XSTRING (glyph->object)))
-               mark_object (glyph->object);
+             {
+#if 0 /* FIXME/tty: Probably not needed */
+               if (glyph->frame)
+                 mark_object (glyph->frame);
+#endif
+               if (STRINGP (glyph->object)
+                   && !string_marked_p (XSTRING (glyph->object)))
+                 mark_object (glyph->object);
+             }
          }
       }
 }
diff --git a/src/dispextern.h b/src/dispextern.h
index cc248a4472e..59785b2d03d 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -482,6 +482,11 @@ struct glyph
      continuation glyphs, or the overlay-arrow glyphs on TTYs.  */
   Lisp_Object object;
 
+  /* Frame on which the glyph was produced. The face_id of this glyph
+     refers to the face_cache of this frame. This is used on tty frames
+     only. */
+  struct frame *frame;
+
   /* Width in pixels.  */
   short pixel_width;
 
@@ -626,10 +631,12 @@ struct glyph
 
 #define FONT_TYPE_UNKNOWN      0
 
-/* Is GLYPH a space?  */
+/* Is GLYPH a space in default face on frame FRAME?  */
 
-#define CHAR_GLYPH_SPACE_P(GLYPH) \
-  ((GLYPH).u.ch == SPACEGLYPH && (GLYPH).face_id == DEFAULT_FACE_ID)
+# define CHAR_GLYPH_SPACE_P(FRAME, GLYPH)      \
+  ((GLYPH).u.ch == SPACEGLYPH                  \
+   && (GLYPH).face_id == DEFAULT_FACE_ID       \
+   && (GLYPH).frame == (FRAME))
 
 /* Are glyph slices of glyphs *X and *Y equal?  It assumes that both
    glyphs have the same type.
@@ -654,6 +661,7 @@ struct glyph
       && (X)->u.val == (Y)->u.val                              \
       && GLYPH_SLICE_EQUAL_P (X, Y)                            \
       && (X)->face_id == (Y)->face_id                          \
+      && (X)->frame == (Y)->frame                              \
       && (X)->padding_p == (Y)->padding_p                      \
       && (X)->left_box_line_p == (Y)->left_box_line_p          \
       && (X)->right_box_line_p == (Y)->right_box_line_p                \
@@ -665,16 +673,18 @@ struct glyph
 #define GLYPH_CHAR_AND_FACE_EQUAL_P(X, Y)      \
   ((X)->u.ch == (Y)->u.ch                      \
    && (X)->face_id == (Y)->face_id             \
+   && (X)->frame == (Y)->frame                 \
    && (X)->padding_p == (Y)->padding_p)
 
 /* Fill a character glyph GLYPH.  CODE, FACE_ID, PADDING_P correspond
    to the bits defined for the typedef `GLYPH' in lisp.h.  */
 
-#define SET_CHAR_GLYPH(GLYPH, CODE, FACE_ID, PADDING_P)        \
+#define SET_CHAR_GLYPH(FRAME, GLYPH, CODE, FACE_ID, PADDING_P) \
      do                                                        \
        {                                               \
          (GLYPH).u.ch = (CODE);                                \
          (GLYPH).face_id = (FACE_ID);                  \
+         (GLYPH).frame = (FRAME);                      \
          (GLYPH).padding_p = (PADDING_P);              \
        }                                               \
      while (false)
@@ -682,11 +692,9 @@ struct glyph
 /* Fill a character type glyph GLYPH from a glyph typedef FROM as
    defined in lisp.h.  */
 
-#define SET_CHAR_GLYPH_FROM_GLYPH(GLYPH, FROM)                 \
-     SET_CHAR_GLYPH (GLYPH,                                    \
-                    GLYPH_CHAR (FROM),                         \
-                    GLYPH_FACE (FROM),                         \
-                    false)
+#define SET_CHAR_GLYPH_FROM_GLYPH(FRAME, GLYPH, FROM)          \
+  SET_CHAR_GLYPH (FRAME, GLYPH, GLYPH_CHAR (FROM),             \
+                 GLYPH_FACE (FROM), false)
 
 /* Construct a glyph code from a character glyph GLYPH.  If the
    character is multibyte, return -1 as we can't use glyph table for a
@@ -3805,7 +3813,7 @@ extern void adjust_frame_glyphs (struct frame *);
 void free_glyphs (struct frame *);
 void free_window_matrices (struct window *);
 void check_glyph_memory (void);
-void mirrored_line_dance (struct glyph_matrix *, int, int, int *, char *);
+void mirrored_line_dance (struct frame *f, int, int, int *, char *);
 void clear_glyph_matrix (struct glyph_matrix *);
 void clear_current_matrices (struct frame *f);
 void clear_desired_matrices (struct frame *);
@@ -3829,7 +3837,7 @@ extern bool frame_size_change_delayed (struct frame *);
 void init_display (void);
 void syms_of_display (void);
 extern void spec_glyph_lookup_face (struct window *, GLYPH *);
-extern void fill_up_frame_row_with_spaces (struct glyph_row *, int);
+extern void fill_up_frame_row_with_spaces (struct frame *, struct glyph_row *, 
int);
 
 /* Defined in terminal.c.  */
 
@@ -3911,6 +3919,21 @@ extern void gui_redo_mouse_highlight (Display_Info *);
 
 #endif /* HAVE_WINDOW_SYSTEM */
 
+struct frame *root_frame (struct frame *f);
+Lisp_Object frames_in_reverse_z_order (struct frame *f, bool visible);
+bool is_tty_frame (struct frame *f);
+bool is_tty_child_frame (struct frame *f);
+bool is_tty_root_frame (struct frame *f);
+Lisp_Object frames_with_root (struct frame *root, bool visible);
+bool combine_updates (Lisp_Object root_frames, bool force_p, bool 
inhibit_id_p);
+bool combine_updates_for_frame (struct frame *f, bool force_p, bool 
inhibit_id_p);
+void tty_raise_lower_frame (struct frame *f, bool raise);
+
+#ifdef GLYPH_DEBUG
+bool is_tty_desired_glyph (struct frame *f, struct glyph *g);
+bool is_tty_current_glyph (struct frame *f, struct glyph *g);
+#endif
+
 INLINE_HEADER_END
 
 #endif /* not DISPEXTERN_H_INCLUDED */
diff --git a/src/dispnew.c b/src/dispnew.c
index 1a243079e46..1089ab4c4f8 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -71,7 +71,7 @@ struct dim
 
 /* Function prototypes.  */
 
-static void update_frame_line (struct frame *, int, bool);
+static void write_row (struct frame *f, int vpos, bool updating_menu_p);
 static int required_matrix_height (struct window *);
 static int required_matrix_width (struct window *);
 static void increment_row_positions (struct glyph_row *, ptrdiff_t, ptrdiff_t);
@@ -80,9 +80,9 @@ static void build_frame_matrix_from_window_tree (struct 
glyph_matrix *,
 static void build_frame_matrix_from_leaf_window (struct glyph_matrix *,
                                                  struct window *);
 static void adjust_decode_mode_spec_buffer (struct frame *);
-static void fill_up_glyph_row_with_spaces (struct glyph_row *);
+static void fill_up_glyph_row_with_spaces (struct frame *, struct glyph_row *);
 static void clear_window_matrices (struct window *, bool);
-static void fill_up_glyph_row_area_with_spaces (struct glyph_row *, int);
+static void fill_up_glyph_row_area_with_spaces (struct frame *, struct 
glyph_row *, int);
 static int scrolling_window (struct window *, int);
 static bool update_window_line (struct window *, int, bool *);
 static void mirror_make_current (struct window *, int);
@@ -93,7 +93,7 @@ static void check_matrix_pointers (struct glyph_matrix *,
 static void mirror_line_dance (struct window *, int, int, int *, char *);
 static bool update_window_tree (struct window *, bool);
 static bool update_window (struct window *, bool);
-static bool update_frame_1 (struct frame *, bool, bool, bool, bool);
+static bool write_matrix (struct frame *, bool, bool, bool, bool);
 static bool scrolling (struct frame *);
 static void set_window_cursor_after_update (struct window *);
 static void adjust_frame_glyphs_for_window_redisplay (struct frame *);
@@ -122,11 +122,6 @@ static int glyph_pool_count;
 
 #endif /* GLYPH_DEBUG and ENABLE_CHECKING */
 
-/* If non-null, the frame whose frame matrices are manipulated.  If
-   null, window matrices are worked on.  */
-
-static struct frame *frame_matrix_frame;
-
 /* Convert vpos and hpos from frame to window and vice versa.
    This may only be used for terminal frames.  */
 
@@ -1178,7 +1173,12 @@ line_hash_code (struct frame *f, struct glyph_row *row)
       while (glyph < end)
        {
          int c = glyph->u.ch;
+         /* FIXME/tty: struct frame can move with igc, and so on.
+            But we need something that takes different frames
+            into account. */
          int face_id = glyph->face_id;
+         if (glyph->frame != f)
+           face_id += (ptrdiff_t) glyph->frame;
          if (FRAME_MUST_WRITE_SPACES (f))
            c -= SPACEGLYPH;
          hash = (((hash << 4) + (hash >> 24)) & 0x0fffffff) + c;
@@ -1213,7 +1213,7 @@ line_draw_cost (struct frame *f, struct glyph_matrix 
*matrix, int vpos)
   if (!FRAME_MUST_WRITE_SPACES (f))
     {
       /* Skip from the end over trailing spaces.  */
-      while (end > beg && CHAR_GLYPH_SPACE_P (*(end - 1)))
+      while (end > beg && CHAR_GLYPH_SPACE_P (f, *(end - 1)))
        --end;
 
       /* All blank line.  */
@@ -1221,7 +1221,7 @@ line_draw_cost (struct frame *f, struct glyph_matrix 
*matrix, int vpos)
        return 0;
 
       /* Skip over leading spaces.  */
-      while (CHAR_GLYPH_SPACE_P (*beg))
+      while (CHAR_GLYPH_SPACE_P (f, *beg))
        ++beg;
     }
 
@@ -2558,6 +2558,7 @@ build_frame_matrix_from_leaf_window (struct glyph_matrix 
*frame_matrix, struct w
   int window_y, frame_y;
   /* If non-zero, a glyph to insert at the right border of W.  */
   GLYPH right_border_glyph;
+  struct frame *f = XFRAME (w->frame);
 
   SET_GLYPH_FROM_CHAR (right_border_glyph, 0);
 
@@ -2599,10 +2600,10 @@ build_frame_matrix_from_leaf_window (struct 
glyph_matrix *frame_matrix, struct w
 
       /* Fill up the frame row with spaces up to the left margin of the
         window row.  */
-      fill_up_frame_row_with_spaces (frame_row, window_matrix->matrix_x);
+      fill_up_frame_row_with_spaces (f, frame_row, window_matrix->matrix_x);
 
       /* Fill up areas in the window matrix row with spaces.  */
-      fill_up_glyph_row_with_spaces (window_row);
+      fill_up_glyph_row_with_spaces (f, window_row);
 
       /* If only part of W's desired matrix has been built, and
          window_row wasn't displayed, use the corresponding current
@@ -2638,7 +2639,7 @@ build_frame_matrix_from_leaf_window (struct glyph_matrix 
*frame_matrix, struct w
                 glyph with the vertical border glyph.  */
              eassert (border->type == CHAR_GLYPH);
              border->type = CHAR_GLYPH;
-             SET_CHAR_GLYPH_FROM_GLYPH (*border, right_border_glyph);
+             SET_CHAR_GLYPH_FROM_GLYPH (f, *border, right_border_glyph);
            }
 
 #ifdef GLYPH_DEBUG
@@ -2701,11 +2702,11 @@ spec_glyph_lookup_face (struct window *w, GLYPH *glyph)
    To be called for frame-based redisplay, only.  */
 
 static void
-fill_up_glyph_row_with_spaces (struct glyph_row *row)
+fill_up_glyph_row_with_spaces (struct frame *f, struct glyph_row *row)
 {
-  fill_up_glyph_row_area_with_spaces (row, LEFT_MARGIN_AREA);
-  fill_up_glyph_row_area_with_spaces (row, TEXT_AREA);
-  fill_up_glyph_row_area_with_spaces (row, RIGHT_MARGIN_AREA);
+  fill_up_glyph_row_area_with_spaces (f, row, LEFT_MARGIN_AREA);
+  fill_up_glyph_row_area_with_spaces (f, row, TEXT_AREA);
+  fill_up_glyph_row_area_with_spaces (f, row, RIGHT_MARGIN_AREA);
 }
 
 
@@ -2713,15 +2714,19 @@ fill_up_glyph_row_with_spaces (struct glyph_row *row)
    frame-based redisplay only.  */
 
 static void
-fill_up_glyph_row_area_with_spaces (struct glyph_row *row, int area)
+fill_up_glyph_row_area_with_spaces (struct frame *f, struct glyph_row *row,
+                                   int area)
 {
   if (row->glyphs[area] < row->glyphs[area + 1])
     {
       struct glyph *end = row->glyphs[area + 1];
       struct glyph *text = row->glyphs[area] + row->used[area];
 
-      while (text < end)
-       *text++ = space_glyph;
+      for (; text < end; ++text)
+       {
+         *text = space_glyph;
+         text->frame = f;
+       }
       row->used[area] = text - row->glyphs[area];
     }
 }
@@ -2731,13 +2736,16 @@ fill_up_glyph_row_area_with_spaces (struct glyph_row 
*row, int area)
    reached.  In frame matrices only one area, TEXT_AREA, is used.  */
 
 void
-fill_up_frame_row_with_spaces (struct glyph_row *row, int upto)
+fill_up_frame_row_with_spaces (struct frame *f, struct glyph_row *row, int 
upto)
 {
   int i = row->used[TEXT_AREA];
   struct glyph *glyph = row->glyphs[TEXT_AREA];
 
-  while (i < upto)
-    glyph[i++] = space_glyph;
+  for (; i < upto; ++i)
+    {
+      glyph[i] = space_glyph;
+      glyph[i].frame = f;
+    }
 
   row->used[TEXT_AREA] = i;
 }
@@ -2748,17 +2756,6 @@ fill_up_frame_row_with_spaces (struct glyph_row *row, 
int upto)
       Mirroring operations on frame matrices in window matrices
  **********************************************************************/
 
-/* Set frame being updated via frame-based redisplay to F.  This
-   function must be called before updates to make explicit that we are
-   working on frame matrices or not.  */
-
-static void
-set_frame_matrix_frame (struct frame *f)
-{
-  frame_matrix_frame = f;
-}
-
-
 /* Make sure glyph row ROW in CURRENT_MATRIX is up to date.
    DESIRED_MATRIX is the desired matrix corresponding to
    CURRENT_MATRIX.  The update is done by exchanging glyph pointers
@@ -2768,9 +2765,10 @@ set_frame_matrix_frame (struct frame *f)
    operations in window matrices of frame_matrix_frame.  */
 
 static void
-make_current (struct glyph_matrix *desired_matrix,
-             struct glyph_matrix *current_matrix, int row)
+make_current (struct frame *f, struct window *w, int row)
 {
+  struct glyph_matrix *desired_matrix = f ? f->desired_matrix : 
w->desired_matrix;
+  struct glyph_matrix *current_matrix = f ? f->current_matrix : 
w->current_matrix;
   struct glyph_row *current_row = MATRIX_ROW (current_matrix, row);
   struct glyph_row *desired_row = MATRIX_ROW (desired_matrix, row);
   bool mouse_face_p = current_row->mouse_face_p;
@@ -2797,8 +2795,8 @@ make_current (struct glyph_matrix *desired_matrix,
 
   /* If we are called on frame matrices, perform analogous operations
      for window matrices.  */
-  if (frame_matrix_frame)
-    mirror_make_current (XWINDOW (frame_matrix_frame->root_window), row);
+  if (f)
+    mirror_make_current (XWINDOW (f->root_window), row);
 }
 
 
@@ -2862,9 +2860,11 @@ mirror_make_current (struct window *w, int frame_row)
    This function is called from do_scrolling and do_direct_scrolling.  */
 
 void
-mirrored_line_dance (struct glyph_matrix *matrix, int unchanged_at_top, int 
nlines,
+mirrored_line_dance (struct frame *f, int unchanged_at_top, int nlines,
                     int *copy_from, char *retained_p)
 {
+  struct glyph_matrix *matrix = f->current_matrix;
+
   /* A copy of original rows.  */
   struct glyph_row *old_rows;
 
@@ -2894,9 +2894,8 @@ mirrored_line_dance (struct glyph_matrix *matrix, int 
unchanged_at_top, int nlin
     }
 
   /* Do the same for window matrices, if MATRIX is a frame matrix.  */
-  if (frame_matrix_frame)
-    mirror_line_dance (XWINDOW (frame_matrix_frame->root_window),
-                      unchanged_at_top, nlines, copy_from, retained_p);
+  mirror_line_dance (XWINDOW (f->root_window),
+                    unchanged_at_top, nlines, copy_from, retained_p);
 
   SAFE_FREE ();
 }
@@ -3194,7 +3193,10 @@ redraw_frame (struct frame *f)
        future.  */
     SET_FRAME_GARBAGED (f);
 
-  clear_frame (f);
+  /* FIXME/tty: clear_frame is actually a "clear_terminal", i.e.
+     is clears the entire screen. */
+  if (!FRAME_PARENT_FRAME (f))
+    clear_frame (f);
   clear_current_matrices (f);
   update_end (f);
   fset_redisplay (f);
@@ -3229,145 +3231,501 @@ DEFUN ("redraw-display", Fredraw_display, 
Sredraw_display, 0, 0, "",
   return Qnil;
 }
 
-
 
-/***********************************************************************
-                            Frame Update
- ***********************************************************************/
+/**********************************************************************
+                           TTY Child Frames
+ **********************************************************************/
 
-/* Update frame F based on the data in desired matrices.
+/* The new thing that child frames on ttys provide is that frames on a
+   tty no longer always occupy the whole terminal. They can overlap
+   instead.
 
-   If FORCE_P, don't let redisplay be stopped by detecting pending input.
-   If INHIBIT_HAIRY_ID_P, don't try scrolling.
+   Let a "root" frame be a frame that has no parent frame. Such root
+   frames we require to be the size of the terminal screen. The current
+   glyph matrix of a root frame of a termimnal represents what is on the
+   screen. The desired matrix of a root frame represents what should be
+   one the screen.
 
-   Value is true if redisplay was stopped due to pending input.  */
+   Building the desired matrix of root frame proceeds by
 
-bool
-update_frame (struct frame *f, bool force_p, bool inhibit_hairy_id_p)
+   - building the desired matrix of the root frame itself which is
+     the bottommost frame in z-order,
+   - building desired matrices of child frames in z-order, topmost last,
+   - copying the desired glyphs from child frames to the desired glyphs
+     of the root frame
+
+   Updating the screen is then done using root frame matrices as it was
+   before child frames were introduced. Child frame current matrices are
+   updated by copying glyph contents of the current matrix of the root
+   frames to the current matrices of child frames. This imnplicitly
+   also updates the glyph contents of their windows' current matrices.  */
+
+struct rect
 {
-  /* True means display has been paused because of pending input.  */
-  bool paused_p;
-  struct window *root_window = XWINDOW (f->root_window);
+  int x, y, w, h;
+};
 
-  if (redisplay_dont_pause)
-    force_p = true;
-  else if (!force_p && detect_input_pending_ignore_squeezables ())
+#ifdef GLYPH_DEBUG
+static bool
+is_glyph_in_matrix (struct glyph_matrix *m, struct glyph *g)
+{
+  for (int y = 0; y < m->matrix_h; ++y)
     {
-      paused_p = true;
-      goto do_pause;
+      struct glyph_row *r = MATRIX_ROW (m, y);
+      if (g >= r->glyphs[0] && g < r->glyphs[0] + m->matrix_w)
+       return true;
     }
+  return false;
+}
 
-  if (FRAME_WINDOW_P (f))
+/* Return true if GLYPH is part of the desired matrix of F. */
+
+bool
+is_tty_desired_glyph (struct frame *f, struct glyph *g)
+{
+  eassert (is_tty_frame (f));
+  return is_glyph_in_matrix (f->desired_matrix, g);
+}
+
+/* Return true if GLYPH is part of the desired matrix of F. */
+
+bool
+is_tty_current_glyph (struct frame *f, struct glyph *g)
+{
+  eassert (is_tty_frame (f));
+  return is_glyph_in_matrix (f->current_matrix, g);
+}
+#endif // GLYPH_DEBUG
+
+/* Compute the intersection of R1 and R2 in R. Value is true if R1 and
+   R2 intersect, false otherwise. */
+
+static bool
+rect_intersect (struct rect *r, struct rect r1, struct rect r2)
+{
+  int x1 = max (r1.x, r2.x);
+  int x2 = min (r1.x + r1.w, r2.x + r2.w);
+  if (x2 < x1)
+    return false;
+  int y1 = max (r1.y, r2.y);
+  int y2 = min (r1.y + r1.h, r2.y + r2.h);
+  if (y2 < y1)
+    return false;
+  *r = (struct rect) { .x = x1, .y = y1, .w = x2 - x1, .h = y2 - y1 };
+  return true;
+}
+
+/* Return the rectanlge frame F occupies. */
+
+static struct rect
+frame_rect (struct frame *f)
+{
+  return (struct rect) {
+    .x = f->left_pos, .y = f->top_pos, .w = f->total_cols, .h = f->total_lines
+  };
+}
+
+/* Return the root frame of frame F. Follow the parent_frame chain until
+   we reach a frame that has no parent. That is the root frame. Note
+   that the root of a root frame is itself. */
+
+struct frame *
+root_frame (struct frame *f)
+{
+  while (FRAME_PARENT_FRAME (f))
+    f = FRAME_PARENT_FRAME (f);
+  return f;
+}
+
+/* Return true if F1 is an ancestor of F2.  */
+
+static bool
+is_frame_ancestor (struct frame *f1, struct frame *f2)
+{
+  for (struct frame *f = FRAME_PARENT_FRAME (f2); f; f = FRAME_PARENT_FRAME 
(f))
+    if (f == f1)
+      return true;
+  return false;
+}
+
+/* Return a list of all frames having root frame ROOT. */
+
+Lisp_Object
+frames_with_root (struct frame *root, bool visible)
+{
+  Lisp_Object list = Qnil;
+  Lisp_Object tail, frame;
+  FOR_EACH_FRAME (tail, frame)
     {
-      /* We are working on window matrix basis.  All windows whose
-        flag must_be_updated_p is set have to be updated.  */
+      struct frame *f = XFRAME (frame);
+      if (FRAME_VISIBLE_P (f) && root_frame (f) == root)
+       list = Fcons (frame, list);
+    }
+  return list;
+}
 
-      /* Record that we are not working on frame matrices.  */
-      set_frame_matrix_frame (NULL);
+/* Compare frames F1 and F2 for z-order. Value is like strcmp. */
 
-      /* Update all windows in the window tree of F, maybe stopping
-        when pending input is detected.  */
-      update_begin (f);
+static bool
+frame_z_order_cmp (struct frame *f1, struct frame *f2)
+{
+  if (f1 == f2)
+    return 0;
+  if (is_frame_ancestor (f1, f2))
+    return -1;
+  return f1->z_order - f2->z_order;
+}
 
-#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
-      /* Update the menu bar on X frames that don't have toolkit
-        support.  */
-      if (WINDOWP (f->menu_bar_window))
-       update_window (XWINDOW (f->menu_bar_window), true);
-#endif
+DEFUN ("frame--z-order-sort-predicate",
+       Fframe__z_order_sort_predicate,
+       Sframe__z_order_sort_predicate,
+       2, 2, 0,
+       doc: /* Internal frame sorting function A < B.  */)
+  (Lisp_Object a, Lisp_Object b)
+{
+  CHECK_FRAME (a); CHECK_FRAME (b);
+  return frame_z_order_cmp (XFRAME (a), XFRAME (b)) < 0 ? Qt : Qnil;
+}
 
-#if defined (HAVE_WINDOW_SYSTEM)
-      /* Update the tab-bar window, if present.  */
-      if (WINDOWP (f->tab_bar_window))
+/* Return a z-order list of frames on the same terminal as F.  The list
+   is ordered topmost frame last. Note that this list may contain
+   more than one root frame plus their children. */
+
+Lisp_Object
+frames_in_reverse_z_order (struct frame *f, bool visible)
+{
+  struct frame *root = root_frame (f);
+  Lisp_Object frames = frames_with_root (root, visible);
+  frames = CALLN (Fsort, frames, Qframe__z_order_sort_predicate);
+  eassert (FRAMEP (XCAR (frames)));
+  eassert (XFRAME (XCAR (frames)) == root);
+  return frames;
+}
+
+void
+tty_raise_lower_frame (struct frame *f, bool raise)
+{
+  struct frame *parent = FRAME_PARENT_FRAME (f);
+  if (parent)
+    {
+      Lisp_Object children = frames_in_reverse_z_order (root_frame (f), false);
+      Lisp_Object siblings = Qnil;
+      for (Lisp_Object tail = children; CONSP (tail); tail = XCDR (tail))
        {
-         struct window *w = XWINDOW (f->tab_bar_window);
+         struct frame *child = XFRAME (XCAR (tail));
+         if (FRAME_PARENT_FRAME (child) == parent)
+           siblings = Fcons (XCAR (tail), siblings);
+       }
 
-         /* Update tab-bar window.  */
-         if (w->must_be_updated_p)
+      int n = list_length (siblings);
+      if (n > 1)
+       {
+         int i;
+         if (raise)
+           f->z_order = 0, i = 1;
+         else
+           f->z_order = n - 1, i = 0;
+         for (Lisp_Object tail = siblings; CONSP (tail); tail = XCDR (tail))
            {
-             Lisp_Object tem;
+             struct frame *child = XFRAME (XCAR (tail));
+             if (f != child)
+               child->z_order = i++;
+           }
+       }
+    }
+}
 
-             update_window (w, true);
-             w->must_be_updated_p = false;
+/* Return true if frame F is a tty frame. */
 
-             /* Swap tab-bar strings.  We swap because we want to
-                reuse strings.  */
-             tem = f->current_tab_bar_string;
-             fset_current_tab_bar_string (f, f->desired_tab_bar_string);
-             fset_desired_tab_bar_string (f, tem);
-           }
+bool
+is_tty_frame (struct frame *f)
+{
+  return FRAME_TERMCAP_P (f);
+}
+
+/* Return true if frame F is a tty child frame. */
+
+bool
+is_tty_child_frame (struct frame *f)
+{
+  return FRAME_PARENT_FRAME (f) && is_tty_frame (f);
+}
+
+/* Return true if frame F is a tty root frame. */
+
+bool
+is_tty_root_frame (struct frame *f)
+{
+  return !FRAME_PARENT_FRAME (f) && is_tty_frame (f);
+}
+
+/* Return the index of the first enabled row in MATRIX, or -1 if there
+   is none. */
+
+static int
+first_enabled_row (struct glyph_matrix *matrix)
+{
+  for (int i = 0; i < matrix->nrows; ++i)
+    if (MATRIX_ROW_ENABLED_P (matrix, i))
+      return i;
+  return -1;
+}
+
+/* On tty frame F, make desired matrix current, without writing
+   to the terminal. */
+
+static void
+make_matrix_current (struct frame *f)
+{
+  int first_row = first_enabled_row (f->desired_matrix);
+  if (first_row >= 0)
+    for (int i = first_row; i < f->desired_matrix->nrows; ++i)
+      if (MATRIX_ROW_ENABLED_P (f->desired_matrix, i))
+       make_current (f, NULL, i);
+}
+
+/* Copy to ROOT's desired matrix what we need from CHILD's current frame 
matrix. */
+
+static void
+copy_child_glyphs (struct frame *root, struct frame *child)
+{
+  eassert (!FRAME_PARENT_FRAME (root));
+  eassert (is_frame_ancestor (root, child));
+
+  /* Determine the intersection of the child frame rectangle with
+     the root frame. This is basically clipping the child frame to
+     the root frame rectangle. */
+  struct rect r;
+  if (!rect_intersect (&r, frame_rect (root), frame_rect (child)))
+    return;
+
+  /* Build CHILD's current matrix which we need to copy from it. */
+  make_matrix_current (child);
+
+  /* The first row of the child's matrix that is visible in the parent
+     depends on how many child rows are above the parent, which is
+     abs (child->top_pos) if top_pos is negative. */
+  int child_y = child->top_pos < 0 ? abs (child->top_pos) : 0;
+
+  /* The first column of the child's matrix that is visible in the
+     parent depends on how many child columns are to the left of the
+     parent, which is abs (child->left_pos) if left_pos is negative. */
+  int child_x = child->left_pos < 0 ? abs (child->left_pos) : 0;
+
+  /* For all rows in the intersection, copy glyphs from the child's
+     current matrix to the root's desired matrix, enabling those
+     rows. */
+  for (int y = r.y; y < r.y + r.h; ++y, ++child_y)
+    {
+      /* Start with the root's desired matrix row. If that hasn't
+        been redisplayed, copy from the root's current matrix. */
+      struct glyph_row *root_row = MATRIX_ROW (root->desired_matrix, y);
+      if (!root_row->enabled_p)
+       {
+         struct glyph_row *from = MATRIX_ROW (root->current_matrix, y);
+         memcpy (root_row->glyphs[0], from->glyphs[0],
+                 root->current_matrix->matrix_w * sizeof (struct glyph));
+         root_row->enabled_p = true;
        }
+
+      /* Copy the child's current row contents over it. */
+      struct glyph_row *child_row = MATRIX_ROW (child->current_matrix, 
child_y);
+      memcpy (root_row->glyphs[0] + r.x,
+             child_row->glyphs[0] + child_x,
+             r.w * sizeof (struct glyph));
+
+      /* Compute a new hash since we changed glyphs. */
+      root_row->hash = row_hash (root_row);
+    }
+}
+
+/***********************************************************************
+                            Frame Update
+ ***********************************************************************/
+
+/* Update the menu bar on X frames that don't have toolkit
+   support.  */
+
+static void
+update_menu_bar (struct frame *f)
+{
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
+  if (WINDOWP (f->menu_bar_window))
+    update_window (XWINDOW (f->menu_bar_window), true);
 #endif
+}
 
-#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
-      /* Update the tool-bar window, if present.  */
-      if (WINDOWP (f->tool_bar_window))
+#ifdef HAVE_WINDOW_SYSTEM
+static void
+update_bar_window (Lisp_Object window, Lisp_Object *current,
+                      Lisp_Object *desired)
+{
+  if (WINDOWP (window))
+    {
+      struct window *w = XWINDOW (window);
+      if (w->must_be_updated_p)
        {
-         struct window *w = XWINDOW (f->tool_bar_window);
+         update_window (w, true);
+         w->must_be_updated_p = false;
+         Lisp_Object tem = *current;
+         *current = *desired;
+         *desired = tem;
+       }
+    }
+}
+#endif
 
-         /* Update tool-bar window.  */
-         if (w->must_be_updated_p)
-           {
-             Lisp_Object tem;
+/* Update the tab-bar window of frame F, if present.
+   FIXME/tty: This is almost identical to the updating
+   of the tab bar. */
 
-             update_window (w, true);
-             w->must_be_updated_p = false;
+static void
+update_tab_bar (struct frame *f)
+{
+#if defined(HAVE_WINDOW_SYSTEM)
+  update_bar_window (f->tab_bar_window, &f->current_tab_bar_string,
+                    &f->desired_tab_bar_string);
+#endif
+}
 
-             /* Swap tool-bar strings.  We swap because we want to
-                reuse strings.  */
-             tem = f->current_tool_bar_string;
-             fset_current_tool_bar_string (f, f->desired_tool_bar_string);
-             fset_desired_tool_bar_string (f, tem);
-           }
-       }
+static void
+update_tool_bar (struct frame *f)
+{
+#if defined(HAVE_WINDOW_SYSTEM) && !defined(HAVE_EXT_TOOL_BAR)
+  update_bar_window (f->tool_bar_window, &f->current_tool_bar_string,
+                    &f->desired_tool_bar_string);
 #endif
+}
 
-      /* Update windows.  */
-      paused_p = update_window_tree (root_window, force_p);
-      update_end (f);
-    }
-  else
+static bool
+update_window_frame (struct frame *f, bool force_p)
+{
+  eassert (FRAME_WINDOW_P (f));
+  update_begin (f);
+  update_menu_bar (f);
+  update_tab_bar (f);
+  update_tool_bar (f);
+  struct window *root_window = XWINDOW (f->root_window);
+  bool paused_p = update_window_tree (root_window, force_p);
+  update_end (f);
+  set_window_update_flags (root_window, false);
+  return paused_p;
+}
+
+static bool
+update_initial_frame (struct frame *f, bool force_p)
+{
+  build_frame_matrix (f);
+  struct window *root_window = XWINDOW (f->root_window);
+  set_window_update_flags (root_window, false);
+  return false;
+}
+
+static void
+flush_terminal (struct frame *f)
+{
+  if (FRAME_TTY (f)->termscript)
+    fflush (FRAME_TTY (f)->termscript);
+  fflush (FRAME_TTY (f)->output);
+}
+
+static bool
+update_tty_frame (struct frame *f, bool force_p)
+{
+  build_frame_matrix (f);
+  return false;
+}
+
+bool
+combine_updates_for_frame (struct frame *f, bool force_p, bool 
inhibit_scrolling)
+{
+  struct frame *root = root_frame (f);
+  eassert (FRAME_VISIBLE_P (root));
+
+  /* Process child frames in reverse z-order, topmost last.  For each
+     child, copy what we need to the root's desired matrix. */
+  Lisp_Object z_order = frames_in_reverse_z_order (root, true);
+  for (Lisp_Object tail = XCDR (z_order); CONSP (tail); tail = XCDR (tail))
     {
-      /* We are working on frame matrix basis.  Set the frame on whose
-        frame matrix we operate.  */
-      set_frame_matrix_frame (f);
+      struct frame *child = XFRAME (XCAR (tail));
+      copy_child_glyphs (root, child);
+    }
 
-      /* Build F's desired matrix from window matrices.  */
-      build_frame_matrix (f);
+  update_begin (root);
+  bool paused = write_matrix (root, force_p, inhibit_scrolling, 1, false);
+  if (!paused)
+    make_matrix_current (root);
+  update_end (root);
+  flush_terminal (root);
 
-      /* Update the display.  */
-      if (FRAME_INITIAL_P (f))
-        /* No actual display to update so the "update" is a nop and
-           obviously isn't interrupted by pending input.  */
-        paused_p = false;
-      else
-        {
-          update_begin (f);
-          paused_p = update_frame_1 (f, force_p, inhibit_hairy_id_p, 1, false);
-          update_end (f);
-        }
-
-      if (FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f))
-        {
-          if (FRAME_TTY (f)->termscript)
-           fflush (FRAME_TTY (f)->termscript);
-         if (FRAME_TERMCAP_P (f))
-           fflush (FRAME_TTY (f)->output);
-        }
-
-      /* Check window matrices for lost pointers.  */
+  for (Lisp_Object tail = z_order; CONSP (tail); tail = XCDR (tail))
+    {
+      struct frame *f = XFRAME (XCAR (tail));
+      struct window *root_window = XWINDOW (f->root_window);
+      set_window_update_flags (root_window, false);
+      clear_desired_matrices (f);
 #ifdef GLYPH_DEBUG
       check_window_matrix_pointers (root_window);
-      add_frame_display_history (f, paused_p);
+      add_frame_display_history (f, false);
 #endif
     }
 
- do_pause:
-  /* Reset flags indicating that a window should be updated.  */
-  set_window_update_flags (root_window, false);
+  return paused;
+}
 
-  display_completed = !paused_p;
-  return paused_p;
+/* Update on the screen all root frames ROOTS. Called from
+   redisplay_internal as the last step of redisplaying. */
+
+bool
+combine_updates (Lisp_Object roots, bool force_p, bool inhibit_scrolling)
+{
+  for (; CONSP (roots); roots = XCDR (roots))
+    {
+      struct frame *root = XFRAME (XCAR (roots));
+      if (combine_updates_for_frame (root, force_p, inhibit_scrolling))
+       {
+         display_completed = false;
+         return true;
+       }
+    }
+
+  display_completed = true;
+  return false;
+}
+
+/* Update frame F based on the data in desired matrices.
+
+   If FORCE_P, don't let redisplay be stopped by detecting pending input.
+   If INHIBIT_SCROLLING, don't try scrolling.
+
+   Value is true if redisplay was stopped due to pending input.  */
+
+bool
+update_frame (struct frame *f, bool force_p, bool inhibit_scrolling)
+{
+  struct window *root_window = XWINDOW (f->root_window);
+
+  if (redisplay_dont_pause)
+    force_p = true;
+  else if (!force_p && detect_input_pending_ignore_squeezables ())
+    {
+      /* Reset flags indicating that a window should be updated.  */
+      set_window_update_flags (root_window, false);
+      display_completed = false;
+      return true;
+    }
+
+  bool paused;
+  if (FRAME_WINDOW_P (f))
+    paused = update_window_frame (f, force_p);
+  else if (FRAME_INITIAL_P (f))
+    paused = update_initial_frame (f, force_p);
+  else
+    paused = update_tty_frame (f, force_p);
+
+  if (paused)
+    display_completed = false;
+  return paused;
 }
 
 /* Update a TTY frame F that has a menu dropped down over some of its
@@ -3384,29 +3742,25 @@ void
 update_frame_with_menu (struct frame *f, int row, int col)
 {
   struct window *root_window = XWINDOW (f->root_window);
-  bool paused_p, cursor_at_point_p;
+  bool cursor_at_point_p;
 
   eassert (FRAME_TERMCAP_P (f));
 
-  /* We are working on frame matrix basis.  Set the frame on whose
-     frame matrix we operate.  */
-  set_frame_matrix_frame (f);
-
   /* Update the display.  */
   update_begin (f);
   cursor_at_point_p = !(row >= 0 && col >= 0);
-  /* Force update_frame_1 not to stop due to pending input, and not
-     try scrolling.  */
-  paused_p = update_frame_1 (f, 1, 1, cursor_at_point_p, true);
+  /* Do not stop due to pending input, and do not try scrolling. This
+     means that write_glyphs will always return false. */
+  write_matrix (f, 1, 1, cursor_at_point_p, true);
+  make_matrix_current (f);
+  clear_desired_matrices (f);
   /* ROW and COL tell us where in the menu to position the cursor, so
      that screen readers know the active region on the screen.  */
   if (!cursor_at_point_p)
     cursor_to (f, row, col);
   update_end (f);
+  flush_terminal (f);
 
-  if (FRAME_TTY (f)->termscript)
-    fflush (FRAME_TTY (f)->termscript);
-  fflush (FRAME_TTY (f)->output);
   /* Check window matrices for lost pointers.  */
 #if GLYPH_DEBUG
 #if 0
@@ -3415,12 +3769,12 @@ update_frame_with_menu (struct frame *f, int row, int 
col)
         making any updates to the window matrices.  */
   check_window_matrix_pointers (root_window);
 #endif
-  add_frame_display_history (f, paused_p);
+  add_frame_display_history (f, false);
 #endif
 
   /* Reset flags indicating that a window should be updated.  */
   set_window_update_flags (root_window, false);
-  display_completed = !paused_p;
+  display_completed = true;
 }
 
 /* Update the mouse position for a frame F.  This handles both
@@ -3507,9 +3861,6 @@ update_single_window (struct window *w)
     {
       struct frame *f = XFRAME (WINDOW_FRAME (w));
 
-      /* Record that this is not a frame-based redisplay.  */
-      set_frame_matrix_frame (NULL);
-
       /* Update W.  */
       update_begin (f);
       update_window (w, true);
@@ -3688,6 +4039,8 @@ update_window (struct window *w, bool force_p)
 
 #ifdef HAVE_WINDOW_SYSTEM
       gui_update_window_begin (w);
+#else
+      (void) changed_p;
 #endif
       yb = window_text_bottom_y (w);
       row = MATRIX_ROW (desired_matrix, 0);
@@ -4321,7 +4674,7 @@ update_window_line (struct window *w, int vpos, bool 
*mouse_face_overwritten_p)
 
   /* Update current_row from desired_row.  */
   was_stipple = current_row->stipple_p;
-  make_current (w->desired_matrix, w->current_matrix, vpos);
+  make_current (NULL, w, vpos);
 
   /* If only a partial update was performed, any stipple already
      displayed in MATRIX_ROW (w->current_matrix, vpos) might still be
@@ -4931,7 +5284,92 @@ scrolling_window (struct window *w, int tab_line_p)
                         Frame-Based Updates
  ************************************************************************/
 
-/* Update the desired frame matrix of frame F.
+static void
+tty_set_cursor (void)
+{
+  struct frame *f = SELECTED_FRAME ();
+
+  if ((cursor_in_echo_area
+       /* If we are showing a message instead of the mini-buffer,
+         show the cursor for the message instead of for the
+         (now hidden) mini-buffer contents.  */
+       || (BASE_EQ (minibuf_window, selected_window)
+          && BASE_EQ (minibuf_window, echo_area_window)
+          && !NILP (echo_area_buffer[0])))
+      /* These cases apply only to the frame that contains
+        the active mini-buffer window.  */
+      && FRAME_HAS_MINIBUF_P (f)
+      && BASE_EQ (FRAME_MINIBUF_WINDOW (f), echo_area_window))
+    {
+      int top = WINDOW_TOP_EDGE_LINE (XWINDOW (FRAME_MINIBUF_WINDOW (f)));
+      int col;
+
+      /* Put cursor at the end of the prompt.  If the mini-buffer
+        is several lines high, find the last line that has
+        any text on it.  */
+      int row = FRAME_TOTAL_LINES (f);
+      do
+       {
+         row--;
+         col = 0;
+
+         if (MATRIX_ROW_ENABLED_P (f->current_matrix, row))
+           {
+             /* Frame rows are filled up with spaces that
+                must be ignored here.  */
+             struct glyph_row *r = MATRIX_ROW (f->current_matrix, row);
+             struct glyph *start = r->glyphs[TEXT_AREA];
+
+             col = r->used[TEXT_AREA];
+             while (0 < col && start[col - 1].charpos < 0)
+               col--;
+           }
+       }
+      while (row > top && col == 0);
+
+      /* We exit the loop with COL at the glyph _after_ the last one.  */
+      if (col > 0)
+       col--;
+
+      /* Make sure COL is not out of range.  */
+      if (col >= FRAME_CURSOR_X_LIMIT (f))
+       {
+         /* If we have another row, advance cursor into it.  */
+         if (row < FRAME_TOTAL_LINES (f) - 1)
+           {
+             col = FRAME_LEFT_SCROLL_BAR_COLS (f);
+             row++;
+           }
+         /* Otherwise move it back in range.  */
+         else
+           col = FRAME_CURSOR_X_LIMIT (f) - 1;
+       }
+
+      cursor_to (f, row, col);
+    }
+  else
+    {
+      /* We have only one cursor on terminal frames.  Use it to
+        display the cursor of the selected window.  */
+      struct window *w = XWINDOW (FRAME_SELECTED_WINDOW (f));
+      if (w->cursor.vpos >= 0
+         /* The cursor vpos may be temporarily out of bounds
+            in the following situation:  There is one window,
+            with the cursor in the lower half of it.  The window
+            is split, and a message causes a redisplay before
+            a new cursor position has been computed.  */
+         && w->cursor.vpos < WINDOW_TOTAL_LINES (w))
+       {
+         int x = WINDOW_TO_FRAME_HPOS (w, w->cursor.hpos);
+         int y = WINDOW_TO_FRAME_VPOS (w, w->cursor.vpos);
+
+         x += max (0, w->left_margin_cols);
+         cursor_to (f, y, x);
+       }
+    }
+}
+
+/* Write desired matix of tty frame F and make it current.
 
    FORCE_P means that the update should not be stopped by pending input.
    INHIBIT_ID_P means that scrolling by insert/delete should not be tried.
@@ -4940,167 +5378,58 @@ scrolling_window (struct window *w, int tab_line_p)
    Value is true if update was stopped due to pending input.  */
 
 static bool
-update_frame_1 (struct frame *f, bool force_p, bool inhibit_id_p,
-               bool set_cursor_p, bool updating_menu_p)
+write_matrix (struct frame *f, bool force_p, bool inhibit_id_p,
+             bool set_cursor_p, bool updating_menu_p)
 {
-  /* Frame matrices to work on.  */
-  struct glyph_matrix *current_matrix = f->current_matrix;
-  struct glyph_matrix *desired_matrix = f->desired_matrix;
-  int i;
-  bool pause_p;
-  int preempt_count = clip_to_bounds (1, baud_rate / 2400 + 1, INT_MAX);
-
-  eassert (current_matrix && desired_matrix);
-
-  if (baud_rate != FRAME_COST_BAUD_RATE (f))
-    calculate_costs (f);
-
   if (!force_p && detect_input_pending_ignore_squeezables ())
-    {
-      pause_p = 1;
-      goto do_pause;
-    }
+    return true;
 
   /* If we cannot insert/delete lines, it's no use trying it.  */
   if (!FRAME_LINE_INS_DEL_OK (f))
-    inhibit_id_p = 1;
+    inhibit_id_p = true;
 
-  /* See if any of the desired lines are enabled; don't compute for
-     i/d line if just want cursor motion.  */
-  for (i = 0; i < desired_matrix->nrows; i++)
-    if (MATRIX_ROW_ENABLED_P (desired_matrix, i))
-      break;
+  if (baud_rate != FRAME_COST_BAUD_RATE (f))
+    calculate_costs (f);
 
-  /* Try doing i/d line, if not yet inhibited.  */
-  if (!inhibit_id_p && i < desired_matrix->nrows)
+ /* See if any of the desired lines are enabled; don't compute for
+     i/d line if just want cursor motion.  */
+  int first_row = first_enabled_row (f->desired_matrix);
+  if (!inhibit_id_p && first_row >= 0)
     force_p |= scrolling (f);
 
-  /* Update the individual lines as needed.  Do bottom line first.  */
-  if (MATRIX_ROW_ENABLED_P (desired_matrix, desired_matrix->nrows - 1))
-    update_frame_line (f, desired_matrix->nrows - 1, updating_menu_p);
+  /* Update the individual lines as needed.  Do bottom line first.  This
+     is done so that messages are made visible when pausing. */
+  int last_row = f->desired_matrix->nrows - 1;
+  if (MATRIX_ROW_ENABLED_P (f->desired_matrix, last_row))
+    write_row (f, last_row, updating_menu_p);
 
-  /* Now update the rest of the lines.  */
-  for (i = 0; i < desired_matrix->nrows - 1 && (force_p || !input_pending); 
i++)
+  bool pause_p = false;
+  if (first_row >= 0)
     {
-      if (MATRIX_ROW_ENABLED_P (desired_matrix, i))
-       {
-         /* Note that output_buffer_size being 0 means that we want the
-            old default behavior of flushing output every now and then.  */
-         if (FRAME_TERMCAP_P (f) && FRAME_TTY (f)->output_buffer_size == 0)
-           {
-             /* Flush out every so many lines.
-                Also flush out if likely to have more than 1k buffered
-                otherwise.   I'm told that some telnet connections get
-                really screwed by more than 1k output at once.  */
-             FILE *display_output = FRAME_TTY (f)->output;
-             if (display_output)
-               {
-                 ptrdiff_t outq = __fpending (display_output);
-                 if (outq > 900
-                     || (outq > 20 && ((i - 1) % preempt_count == 0)))
-                   fflush (display_output);
-               }
-           }
+      const int preempt_count = clip_to_bounds (1, baud_rate / 2400 + 1, 
INT_MAX);
 
-         if (!force_p && (i - 1) % preempt_count == 0)
-           detect_input_pending_ignore_squeezables ();
+      for (int i = first_row, n = 0; i < last_row; ++i)
+       if (MATRIX_ROW_ENABLED_P (f->desired_matrix, i))
+         {
+           if (!force_p && n % preempt_count == 0
+               && detect_input_pending_ignore_squeezables ())
+             {
+               pause_p = true;
+               break;
+             }
 
-         update_frame_line (f, i, updating_menu_p);
-       }
+           write_row (f, i, updating_menu_p);
+           ++n;
+         }
     }
 
-  pause_p = 0 < i && i < FRAME_TOTAL_LINES (f) - 1;
-
   /* Now just clean up termcap drivers and set cursor, etc.  */
   if (!pause_p && set_cursor_p)
-    {
-      if ((cursor_in_echo_area
-          /* If we are showing a message instead of the mini-buffer,
-             show the cursor for the message instead of for the
-             (now hidden) mini-buffer contents.  */
-          || (BASE_EQ (minibuf_window, selected_window)
-              && BASE_EQ (minibuf_window, echo_area_window)
-              && !NILP (echo_area_buffer[0])))
-         /* These cases apply only to the frame that contains
-            the active mini-buffer window.  */
-         && FRAME_HAS_MINIBUF_P (f)
-         && BASE_EQ (FRAME_MINIBUF_WINDOW (f), echo_area_window))
-       {
-         int top = WINDOW_TOP_EDGE_LINE (XWINDOW (FRAME_MINIBUF_WINDOW (f)));
-         int col;
-
-         /* Put cursor at the end of the prompt.  If the mini-buffer
-            is several lines high, find the last line that has
-            any text on it.  */
-         int row = FRAME_TOTAL_LINES (f);
-         do
-           {
-             row--;
-             col = 0;
-
-             if (MATRIX_ROW_ENABLED_P (current_matrix, row))
-               {
-                 /* Frame rows are filled up with spaces that
-                    must be ignored here.  */
-                 struct glyph_row *r = MATRIX_ROW (current_matrix, row);
-                 struct glyph *start = r->glyphs[TEXT_AREA];
-
-                 col = r->used[TEXT_AREA];
-                 while (0 < col && start[col - 1].charpos < 0)
-                   col--;
-               }
-           }
-         while (row > top && col == 0);
-
-         /* We exit the loop with COL at the glyph _after_ the last one.  */
-         if (col > 0)
-           col--;
-
-         /* Make sure COL is not out of range.  */
-         if (col >= FRAME_CURSOR_X_LIMIT (f))
-           {
-             /* If we have another row, advance cursor into it.  */
-             if (row < FRAME_TOTAL_LINES (f) - 1)
-               {
-                 col = FRAME_LEFT_SCROLL_BAR_COLS (f);
-                 row++;
-               }
-             /* Otherwise move it back in range.  */
-             else
-               col = FRAME_CURSOR_X_LIMIT (f) - 1;
-           }
+    tty_set_cursor ();
 
-         cursor_to (f, row, col);
-       }
-      else
-       {
-         /* We have only one cursor on terminal frames.  Use it to
-            display the cursor of the selected window.  */
-         struct window *w = XWINDOW (FRAME_SELECTED_WINDOW (f));
-         if (w->cursor.vpos >= 0
-             /* The cursor vpos may be temporarily out of bounds
-                in the following situation:  There is one window,
-                with the cursor in the lower half of it.  The window
-                is split, and a message causes a redisplay before
-                a new cursor position has been computed.  */
-             && w->cursor.vpos < WINDOW_TOTAL_LINES (w))
-           {
-             int x = WINDOW_TO_FRAME_HPOS (w, w->cursor.hpos);
-             int y = WINDOW_TO_FRAME_VPOS (w, w->cursor.vpos);
-
-             x += max (0, w->left_margin_cols);
-             cursor_to (f, y, x);
-           }
-       }
-    }
-
- do_pause:
-
-  clear_desired_matrices (f);
   return pause_p;
 }
 
-
 /* Do line insertions/deletions on frame F for frame-based redisplay.  */
 
 static bool
@@ -5209,12 +5538,12 @@ scrolling (struct frame *frame)
    which is LEN glyphs long.  */
 
 static int
-count_blanks (struct glyph *r, int len)
+count_blanks (struct frame *f, struct glyph *r, int len)
 {
   int i;
 
   for (i = 0; i < len; ++i)
-    if (!CHAR_GLYPH_SPACE_P (r[i]))
+    if (!CHAR_GLYPH_SPACE_P (f, r[i]))
       break;
 
   return i;
@@ -5250,7 +5579,7 @@ count_match (struct glyph *str1, struct glyph *end1, 
struct glyph *str2, struct
 /* Perform a frame-based update on line VPOS in frame FRAME.  */
 
 static void
-update_frame_line (struct frame *f, int vpos, bool updating_menu_p)
+write_row (struct frame *f, int vpos, bool updating_menu_p)
 {
   struct glyph *obody, *nbody, *op1, *op2, *np1, *nend;
   int tem;
@@ -5264,11 +5593,6 @@ update_frame_line (struct frame *f, int vpos, bool 
updating_menu_p)
   bool colored_spaces_p = (FACE_FROM_ID (f, DEFAULT_FACE_ID)->background
                           != FACE_TTY_DEFAULT_BG_COLOR);
 
-  /* This should never happen, but evidently sometimes does if one
-     resizes the frame quickly enough.  Prevent aborts in cmcheckmagic.  */
-  if (vpos >= FRAME_TOTAL_LINES (f))
-    return;
-
   if (colored_spaces_p)
     write_spaces_p = 1;
 
@@ -5287,7 +5611,7 @@ update_frame_line (struct frame *f, int vpos, bool 
updating_menu_p)
 
       /* Ignore trailing spaces, if we can.  */
       if (!write_spaces_p)
-       while (olen > 0 && CHAR_GLYPH_SPACE_P (obody[olen-1]))
+       while (olen > 0 && CHAR_GLYPH_SPACE_P (f, obody[olen-1]))
          olen--;
     }
 
@@ -5316,7 +5640,7 @@ update_frame_line (struct frame *f, int vpos, bool 
updating_menu_p)
     {
       /* Ignore spaces at the end, if we can.  */
       if (!write_spaces_p)
-       while (nlen > 0 && CHAR_GLYPH_SPACE_P (nbody[nlen - 1]))
+       while (nlen > 0 && CHAR_GLYPH_SPACE_P (f, nbody[nlen - 1]))
          --nlen;
 
       /* Write the contents of the desired line.  */
@@ -5338,15 +5662,13 @@ update_frame_line (struct frame *f, int vpos, bool 
updating_menu_p)
        /* Make sure we are in the right row, otherwise cursor movement
           with cmgoto might use `ch' in the wrong row.  */
        cursor_to (f, vpos, 0);
-
-      make_current (desired_matrix, current_matrix, vpos);
       return;
     }
 
   /* Pretend trailing spaces are not there at all,
      unless for one reason or another we must write all spaces.  */
   if (!write_spaces_p)
-    while (nlen > 0 && CHAR_GLYPH_SPACE_P (nbody[nlen - 1]))
+    while (nlen > 0 && CHAR_GLYPH_SPACE_P (f, nbody[nlen - 1]))
       nlen--;
 
   /* If there's no i/d char, quickly do the best we can without it.  */
@@ -5383,9 +5705,6 @@ update_frame_line (struct frame *f, int vpos, bool 
updating_menu_p)
          cursor_to (f, vpos, nlen);
          clear_end_of_line (f, olen);
        }
-
-      /* Make current row = desired row.  */
-      make_current (desired_matrix, current_matrix, vpos);
       return;
     }
 
@@ -5399,7 +5718,7 @@ update_frame_line (struct frame *f, int vpos, bool 
updating_menu_p)
       if (write_spaces_p)
        nsp = 0;
       else
-       nsp = count_blanks (nbody, nlen);
+       nsp = count_blanks (f, nbody, nlen);
 
       if (nlen > nsp)
        {
@@ -5407,14 +5726,12 @@ update_frame_line (struct frame *f, int vpos, bool 
updating_menu_p)
          write_glyphs (f, nbody + nsp, nlen - nsp);
        }
 
-      /* Exchange contents between current_frame and new_frame.  */
-      make_current (desired_matrix, current_matrix, vpos);
       return;
     }
 
   /* Compute number of leading blanks in old and new contents.  */
-  osp = count_blanks (obody, olen);
-  nsp = (colored_spaces_p ? 0 : count_blanks (nbody, nlen));
+  osp = count_blanks (f, obody, olen);
+  nsp = (colored_spaces_p ? 0 : count_blanks (f, nbody, nlen));
 
   /* Compute number of matching chars starting with first non-blank.  */
   begmatch = count_match (obody + osp, obody + olen,
@@ -5425,7 +5742,7 @@ update_frame_line (struct frame *f, int vpos, bool 
updating_menu_p)
   if (!write_spaces_p && osp + begmatch == olen)
     {
       np1 = nbody + nsp;
-      while (np1 + begmatch < nend && CHAR_GLYPH_SPACE_P (np1[begmatch]))
+      while (np1 + begmatch < nend && CHAR_GLYPH_SPACE_P (f, np1[begmatch]))
        ++begmatch;
     }
 
@@ -5566,9 +5883,6 @@ update_frame_line (struct frame *f, int vpos, bool 
updating_menu_p)
       cursor_to (f, vpos, nlen);
       clear_end_of_line (f, olen);
     }
-
-  /* Exchange contents between current_frame and new_frame.  */
-  make_current (desired_matrix, current_matrix, vpos);
 }
 
 
@@ -5967,7 +6281,8 @@ handle_window_change_signal (int sig)
            {
              struct frame *f = XFRAME (frame);
 
-             if (FRAME_TERMCAP_P (f) && FRAME_TTY (f) == tty)
+             if (FRAME_TERMCAP_P (f) && FRAME_TTY (f) == tty
+                 && !FRAME_PARENT_FRAME (f))
                /* Record the new sizes, but don't reallocate the data
                   structures now.  Let that be done later outside of the
                   signal handler.  */
@@ -6534,7 +6849,7 @@ init_display_interactive (void)
 
   /* Construct the space glyph.  */
   space_glyph.type = CHAR_GLYPH;
-  SET_CHAR_GLYPH (space_glyph, ' ', DEFAULT_FACE_ID, 0);
+  SET_CHAR_GLYPH (NULL, space_glyph, ' ', DEFAULT_FACE_ID, 0);
   space_glyph.charpos = -1;
 
   inverse_video = 0;
@@ -6808,6 +7123,7 @@ syms_of_display (void)
   defsubr (&Ssend_string_to_terminal);
   defsubr (&Sinternal_show_cursor);
   defsubr (&Sinternal_show_cursor_p);
+  defsubr (&Sframe__z_order_sort_predicate);
 
 #ifdef GLYPH_DEBUG
   defsubr (&Sdump_redisplay_history);
@@ -6818,6 +7134,7 @@ syms_of_display (void)
 
   /* This is the "purpose" slot of a display table.  */
   DEFSYM (Qdisplay_table, "display-table");
+  DEFSYM (Qframe__z_order_sort_predicate, "frame--z-order-sort-predicate");
 
   DEFSYM (Qredisplay_dont_pause, "redisplay-dont-pause");
 
@@ -6921,6 +7238,8 @@ Possible values are t (below the tool bar), nil (above 
the tool bar).
 This option affects only builds where the tool bar is not external.  */);
 
   pdumper_do_now_and_after_load (syms_of_display_for_pdumper);
+
+  Fprovide (intern_c_string ("tty-child-frames"), Qnil);
 }
 
 static void
diff --git a/src/frame.c b/src/frame.c
index 7f4bf274ad9..f14b29c02ef 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -130,6 +130,14 @@ decode_window_system_frame (Lisp_Object frame)
 #endif
 }
 
+struct frame *
+decode_tty_frame (Lisp_Object frame)
+{
+  struct frame *f = decode_live_frame (frame);
+  check_tty (f);
+  return f;
+}
+
 void
 check_window_system (struct frame *f)
 {
@@ -141,6 +149,13 @@ check_window_system (struct frame *f)
         : "Window system is not in use or not initialized");
 }
 
+void
+check_tty (struct frame *f)
+{
+  if (!f || !FRAME_TERMCAP_P (f))
+    error ("tty frame should be used");
+}
+
 /* Return the value of frame parameter PROP in frame FRAME.  */
 
 Lisp_Object
@@ -182,6 +197,17 @@ set_menu_bar_lines (struct frame *f, Lisp_Object value, 
Lisp_Object oldval)
   int olines = FRAME_MENU_BAR_LINES (f);
   int nlines = TYPE_RANGED_FIXNUMP (int, value) ? XFIXNUM (value) : 0;
 
+  /* FIXME/tty: menu bars on child frames don't work on all platforms,
+     which is the reason why prepare_menu_bar does not update_menu_bar
+     for child frames (info from Martin Rudalics). This could be
+     implemented in ttys, but it's unclear if it is worth it. */
+  if (is_tty_child_frame (f))
+    {
+      FRAME_MENU_BAR_LINES (f) = 0;
+      FRAME_MENU_BAR_HEIGHT (f) = 0;
+      return;
+    }
+
   /* Right now, menu bars don't work properly in minibuf-only frames;
      most of the commands try to apply themselves to the minibuffer
      frame itself, and get an error because you can't switch buffers
@@ -389,7 +415,6 @@ frame_windows_min_size (Lisp_Object frame, Lisp_Object 
horizontal,
 }
 
 
-#ifdef HAVE_WINDOW_SYSTEM
 /**
  * keep_ratio:
  *
@@ -508,7 +533,6 @@ keep_ratio (struct frame *f, struct frame *p, int 
old_width, int old_height,
        }
     }
 }
-#endif
 
 
 static void
@@ -832,9 +856,11 @@ adjust_frame_size (struct frame *f, int new_text_width, 
int new_text_height,
       resize_frame_windows (f, new_inner_width, true);
 
       /* MSDOS frames cannot PRETEND, as they change frame size by
-        manipulating video hardware.  */
-      if ((FRAME_TERMCAP_P (f) && !pretend) || FRAME_MSDOS_P (f))
-       FrameCols (FRAME_TTY (f)) = new_text_cols;
+        manipulating video hardware.  FIXME/tty: it looks wrong to set
+        FrameCols here. See also the comment for FrameRows above. */
+      if (is_tty_root_frame (f))
+       if ((FRAME_TERMCAP_P (f) && !pretend) || FRAME_MSDOS_P (f))
+         FrameCols (FRAME_TTY (f)) = new_text_cols;
 
 #if defined (HAVE_WINDOW_SYSTEM)
       if (WINDOWP (f->tab_bar_window))
@@ -866,9 +892,15 @@ adjust_frame_size (struct frame *f, int new_text_width, 
int new_text_height,
       resize_frame_windows (f, new_inner_height, false);
 
       /* MSDOS frames cannot PRETEND, as they change frame size by
-        manipulating video hardware.  */
-      if ((FRAME_TERMCAP_P (f) && !pretend) || FRAME_MSDOS_P (f))
-       FrameRows (FRAME_TTY (f)) = new_text_lines + FRAME_TOP_MARGIN (f);
+        manipulating video hardware.
+
+        FIXME/tty: ti looks strange to set FrameRows here. The terminal
+        emulator window or console has some physical size, and I wonder
+        how one would change that size from here. Or what it is good for
+        to set it here. */
+      if (is_tty_root_frame (f))
+       if ((FRAME_TERMCAP_P (f) && !pretend) || FRAME_MSDOS_P (f))
+         FrameRows (FRAME_TTY (f)) = new_text_lines + FRAME_TOP_MARGIN (f);
     }
   else if (new_text_lines != old_text_lines)
     call2 (Qwindow__pixel_to_total, frame, Qnil);
@@ -910,7 +942,6 @@ adjust_frame_size (struct frame *f, int new_text_width, int 
new_text_height,
 
   unblock_input ();
 
-#ifdef HAVE_WINDOW_SYSTEM
   {
     /* Adjust size of F's child frames.  */
     Lisp_Object frames, frame1;
@@ -920,7 +951,6 @@ adjust_frame_size (struct frame *f, int new_text_width, int 
new_text_height,
        keep_ratio (XFRAME (frame1), f, old_native_width, old_native_height,
                    new_native_width, new_native_height);
   }
-#endif
 }
 
 /* Allocate basically initialized frame.  */
@@ -967,12 +997,12 @@ make_frame (bool mini_p)
   f->line_height = 1;  /* !FRAME_WINDOW_P value.  */
   f->new_width = -1;
   f->new_height = -1;
+  f->no_special_glyphs = false;
 #ifdef HAVE_WINDOW_SYSTEM
   f->vertical_scroll_bar_type = vertical_scroll_bar_none;
   f->horizontal_scroll_bars = false;
   f->want_fullscreen = FULLSCREEN_NONE;
   f->undecorated = false;
-  f->no_special_glyphs = false;
 #ifndef HAVE_NTGUI
   f->override_redirect = false;
 #endif
@@ -1089,7 +1119,6 @@ make_frame (bool mini_p)
   return f;
 }
 
-#ifdef HAVE_WINDOW_SYSTEM
 /* Make a frame using a separate minibuffer window on another frame.
    MINI_WINDOW is the minibuffer window to use.  nil means use the
    default (the global minibuffer).  */
@@ -1183,7 +1212,7 @@ make_minibuffer_frame (void)
                      : Fcar (Vminibuffer_list)), 0, 0);
   return f;
 }
-#endif /* HAVE_WINDOW_SYSTEM */
+
 
 /* Construct a frame that refers to a terminal.  */
 
@@ -1209,7 +1238,7 @@ make_initial_frame (void)
   tty_frame_count = 1;
   fset_name (f, build_pure_c_string ("F1"));
 
-  SET_FRAME_VISIBLE (f, 1);
+  SET_FRAME_VISIBLE (f, true);
 
   f->output_method = terminal->type;
   f->terminal = terminal;
@@ -1246,23 +1275,56 @@ make_initial_frame (void)
 #ifndef HAVE_ANDROID
 
 static struct frame *
-make_terminal_frame (struct terminal *terminal)
+make_terminal_frame (struct terminal *terminal, Lisp_Object parent,
+                    Lisp_Object params)
 {
-  register struct frame *f;
-  Lisp_Object frame;
   char name[sizeof "F" + INT_STRLEN_BOUND (tty_frame_count)];
 
   if (!terminal->name)
     error ("Terminal is not live, can't create new frames on it");
 
-  f = make_frame (1);
+  struct frame *f;
+  if (NILP (parent))
+    f = make_frame (true);
+  else
+    {
+      CHECK_FRAME (parent);
+
+      f = NULL;
+      Lisp_Object mini = Fassq (Qminibuffer, params);
+      if (CONSP (mini))
+       {
+         mini = Fcdr (mini);
+         struct kboard *kb = FRAME_KBOARD (XFRAME (parent));
+         if (EQ (mini, Qnone) || NILP (mini))
+           f = make_frame_without_minibuffer (Qnil, kb, Qnil);
+         else if (EQ (mini, Qonly))
+           {
+             /* FIXME/tty: No interest in this feature at the moment,
+                unless someone complains. */
+# if 0
+             f = make_minibuffer_frame ();
+             /* Not sure about this plus the unsplittable frame
+                paran. */
+             f->no_split = true;
+# endif
+           }
+         else if (WINDOWP (mini))
+           f = make_frame_without_minibuffer (mini, kb, Qnil);
+       }
+
+      if (f == NULL)
+       f = make_frame (true);
+      f->parent_frame = parent;
+    }
 
+  Lisp_Object frame;
   XSETFRAME (frame, f);
   Vframe_list = Fcons (frame, Vframe_list);
 
   fset_name (f, make_formatted_string (name, "F%"PRIdMAX, ++tty_frame_count));
 
-  SET_FRAME_VISIBLE (f, 1);
+  SET_FRAME_VISIBLE (f, true);
 
   f->terminal = terminal;
   f->terminal->reference_count++;
@@ -1287,7 +1349,15 @@ make_terminal_frame (struct terminal *terminal)
   f->horizontal_scroll_bars = false;
 #endif
 
-  FRAME_MENU_BAR_LINES (f) = NILP (Vmenu_bar_mode) ? 0 : 1;
+  /* FIXME/tty: menu bars on child frames don't work on all platforms,
+     which is the reason why prepare_menu_bar does not update_menu_bar
+     for child frames (info from Martin Rudalics). This could be
+     implemented in ttys, but it's unclear if it is worth it. */
+  if (NILP (parent))
+    FRAME_MENU_BAR_LINES (f) = NILP (Vmenu_bar_mode) ? 0 : 1;
+  else
+    FRAME_MENU_BAR_LINES (f) = 0;
+
   FRAME_TAB_BAR_LINES (f) = NILP (Vtab_bar_mode) ? 0 : 1;
   FRAME_LINES (f) = FRAME_LINES (f) - FRAME_MENU_BAR_LINES (f)
     - FRAME_TAB_BAR_LINES (f);
@@ -1296,16 +1366,16 @@ make_terminal_frame (struct terminal *terminal)
   FRAME_TEXT_HEIGHT (f) = FRAME_TEXT_HEIGHT (f) - FRAME_MENU_BAR_HEIGHT (f)
     - FRAME_TAB_BAR_HEIGHT (f);
 
-  /* Set the top frame to the newly created frame.  */
+  /* Mark current topmost frame obscured if we make a new root frame.
+     Child frames don't completely obscure other frames. */
   if (FRAMEP (FRAME_TTY (f)->top_frame)
-      && FRAME_LIVE_P (XFRAME (FRAME_TTY (f)->top_frame)))
-    SET_FRAME_VISIBLE (XFRAME (FRAME_TTY (f)->top_frame), 2); /* obscured */
-
-  FRAME_TTY (f)->top_frame = frame;
-
-  if (!noninteractive)
-    init_frame_faces (f);
+      && FRAME_LIVE_P (XFRAME (FRAME_TTY (f)->top_frame))
+      && NILP (parent))
+    SET_FRAME_VISIBLE (XFRAME (FRAME_TTY (f)->top_frame), false);
 
+  /* Set the top frame to the newly created frame.  */
+  if (!FRAME_PARENT_FRAME (f))
+    FRAME_TTY (f)->top_frame = frame;
   return f;
 }
 
@@ -1335,6 +1405,62 @@ get_future_frame_param (Lisp_Object parameter,
 
 #endif
 
+static int
+tty_child_pos_param (struct frame *child, Lisp_Object key,
+                    Lisp_Object params, int dflt)
+{
+  Lisp_Object val = Fassq (key, params);
+  if (CONSP (val))
+    {
+      val = XCDR (val);
+      if (FIXNUMP (val))
+       return XFIXNUM (val);
+    }
+  return dflt;
+}
+
+static int
+tty_child_size_param (struct frame *child, Lisp_Object key,
+                     Lisp_Object params, int dflt)
+{
+  Lisp_Object val = Fassq (key, params);
+  if (CONSP (val))
+    {
+      val = XCDR (val);
+      if (CONSP (val))
+       {
+         /* Width and height may look like (width text-pixels
+            . PIXELS) on window systems. Mimic that. */
+         val = XCDR (val);
+         if (EQ (val, Qtext_pixels))
+           val = XCDR (val);
+       }
+      else if (FLOATP (val))
+       {
+             /* Width and height may be a float, in which case
+                it's a multiple of the parent's value. */
+             struct frame *parent = FRAME_PARENT_FRAME (child);
+             int sz = (EQ (key, Qwidth) ? FRAME_TOTAL_COLS (parent)
+                       : FRAME_TOTAL_LINES (parent));
+             val = make_fixnum (XFLOAT_DATA (val) * sz);
+       }
+
+      if (FIXNUMP (val) && XFIXNUM (val) > 0)
+       return XFIXNUM (val);
+    }
+  return dflt;
+}
+
+static void
+child_frame_rect (struct frame *f, Lisp_Object params,
+                 int *x, int *y, int *w, int *h)
+{
+  *x = tty_child_pos_param (f, Qleft, params, 0);
+  *y = tty_child_pos_param (f, Qtop, params, 0);
+  *w = tty_child_size_param (f, Qwidth, params, FRAME_TOTAL_COLS (f));
+  *h = tty_child_size_param (f, Qheight, params, FRAME_TOTAL_LINES (f));
+}
+
 DEFUN ("make-terminal-frame", Fmake_terminal_frame, Smake_terminal_frame,
        1, 1, 0,
        doc: /* Create an additional terminal frame, possibly on another 
terminal.
@@ -1358,9 +1484,7 @@ affects all frames on the same terminal device.  */)
   error ("Text terminals are not supported on this platform");
   return Qnil;
 #else
-  struct frame *f;
   struct terminal *t = NULL;
-  Lisp_Object frame;
   struct frame *sf = SELECTED_FRAME ();
 
 #ifdef MSDOS
@@ -1390,7 +1514,7 @@ affects all frames on the same terminal device.  */)
       error ("Multiple terminals are not supported on this platform");
     if (!t)
       t = the_only_display_info.terminal;
-#endif
+# endif
   }
 
   if (!t)
@@ -1417,19 +1541,60 @@ affects all frames on the same terminal device.  */)
       SAFE_FREE ();
     }
 
-  f = make_terminal_frame (t);
+  /* Make a new frame. We need to know upfront if if a parent frame is
+     specified because we behave differently in this case, e.g. child
+     frames don't obscure other frames. */
+  Lisp_Object parent = Fcdr (Fassq (Qparent_frame, parms));
+  struct frame *f = make_terminal_frame (t, parent, parms);
 
-  {
-    int width, height;
-    get_tty_size (fileno (FRAME_TTY (f)->input), &width, &height);
-    /* With INHIBIT 5 pass correct text height to adjust_frame_size.  */
-    adjust_frame_size (f, width, height - FRAME_TOP_MARGIN (f),
-                      5, 0, Qterminal_frame);
-  }
+  if (!noninteractive)
+    init_frame_faces (f);
 
+  /* Visibility of root frames cannot be set with a frame parameter.
+     Their visibility solely depends on whether or not they are the
+     top_frame on the terminal. */
+  if (FRAME_PARENT_FRAME (f))
+    {
+      Lisp_Object visible = Fassq (Qvisibility, parms);
+      if (CONSP (visible))
+       SET_FRAME_VISIBLE (f, !NILP (visible));
+
+      /* FIXME/tty: Not having borders is currently deeply
+        engraved in the code, so we can't naively do it. */
+#if 0
+      Lisp_Object border = Fcdr (Fassq (Qborder_width, parms));
+      if (FIXNUMP (border))
+       f->border_width = XFIXNUM (border);
+      border = Fcdr (Fassq (Qchild_frame_border_width, parms));
+      if (FIXNUMP (border))
+       f->child_frame_border_width = XFIXNUM (border);
+# endif
+    }
+
+  /* Determine width and height of the frame. For root frames use the
+     width/height of the terminal. For child frames, take it from frame
+     parameters. Note that a default (80x25) has been set in
+     make_frame. We handle root frames in this way because otherwise we
+     would end up needing glyph matrices for the terminal, which is both
+     more work and has its downsides (think of clipping frames to the
+     terminal size).  */
+  int x = 0, y = 0, width, height;
+  if (FRAME_PARENT_FRAME (f))
+    child_frame_rect (f, parms, &x, &y, &width, &height);
+  else
+    get_tty_size (fileno (FRAME_TTY (f)->input), &width, &height);
+  adjust_frame_size (f, width, height - FRAME_TOP_MARGIN (f), 5, 0,
+                    Qterminal_frame);
   adjust_frame_glyphs (f);
+
   calculate_costs (f);
-  XSETFRAME (frame, f);
+
+  f->left_pos = x;
+  f->top_pos = y;
+  store_in_alist (&parms, Qleft, make_fixnum (x));
+  store_in_alist (&parms, Qtop, make_fixnum (y));
+  store_in_alist (&parms, Qwidth, make_fixnum (width));
+  store_in_alist (&parms, Qheight, make_fixnum (height));
 
   store_in_alist (&parms, Qtty_type, build_string (t->display_info.tty->type));
   store_in_alist (&parms, Qtty,
@@ -1451,7 +1616,11 @@ affects all frames on the same terminal device.  */)
   /* On terminal frames the `minibuffer' frame parameter is always
      virtually t.  Avoid that a different value in parms causes
      complaints, see Bug#24758.  */
-  store_in_alist (&parms, Qminibuffer, Qt);
+  if (!FRAME_PARENT_FRAME (f))
+    store_in_alist (&parms, Qminibuffer, Qt);
+
+  Lisp_Object frame;
+  XSETFRAME (frame, f);
   Fmodify_frame_parameters (frame, parms);
 
   f->can_set_window_size = true;
@@ -1546,24 +1715,32 @@ do_switch_frame (Lisp_Object frame, int track, int 
for_deletion, Lisp_Object nor
       struct tty_display_info *tty = FRAME_TTY (f);
       Lisp_Object top_frame = tty->top_frame;
 
-      /* Don't mark the frame garbaged and/or obscured if we are
-        switching to the frame that is already the top frame of that
-        TTY.  */
+      /* Don't mark the frame garbaged if we are switching to the frame
+        that is already the top frame of that TTY.  */
       if (!EQ (frame, top_frame))
        {
-         if (FRAMEP (top_frame))
-           /* Mark previously displayed frame as now obscured.  */
-           SET_FRAME_VISIBLE (XFRAME (top_frame), 2);
-         SET_FRAME_VISIBLE (f, 1);
-         /* If the new TTY frame changed dimensions, we need to
-            resync term.c's idea of the frame size with the new
-            frame's data.  */
-         if (FRAME_COLS (f) != FrameCols (tty))
-           FrameCols (tty) = FRAME_COLS (f);
-         if (FRAME_TOTAL_LINES (f) != FrameRows (tty))
-           FrameRows (tty) = FRAME_TOTAL_LINES (f);
+         SET_FRAME_VISIBLE (f, true);
+         if (is_tty_root_frame (f))
+           {
+             /* Mark previously displayed frame as no longer visible.  */
+             if (FRAMEP (top_frame))
+               SET_FRAME_VISIBLE (XFRAME (top_frame), false);
+
+             tty->top_frame = frame;
+
+             /* If the new TTY frame changed dimensions, we need to
+                resync term.c's idea of the frame size with the new
+                frame's data.  */
+             /* FIXME/tty: another place where we set
+                FrameRows/FrameCols, which is really a physical
+                property of the terminal, and nothing we set. That
+                doesn't look right to me. */
+             if (FRAME_COLS (f) != FrameCols (tty))
+               FrameCols (tty) = FRAME_COLS (f);
+             if (FRAME_TOTAL_LINES (f) != FrameRows (tty))
+               FrameRows (tty) = FRAME_TOTAL_LINES (f);
+           }
        }
-      tty->top_frame = frame;
     }
 
   sf->select_mini_window_flag = MINI_WINDOW_P (XWINDOW (sf->selected_window));
@@ -1605,9 +1782,7 @@ do_switch_frame (Lisp_Object frame, int track, int 
for_deletion, Lisp_Object nor
      (select-window (frame-root-window (make-frame))) doesn't end up
      with your typing being interpreted in the new frame instead of
      the one you're actually typing in.  */
-#ifdef HAVE_WINDOW_SYSTEM
   if (!frame_ancestor_p (f, sf))
-#endif
     internal_last_event_frame = Qnil;
 
   return frame;
@@ -1725,7 +1900,6 @@ parent window is the window-system's root window) or an 
embedded window
     return Qnil;
 }
 
-#ifdef HAVE_WINDOW_SYSTEM
 bool
 frame_ancestor_p (struct frame *af, struct frame *df)
 {
@@ -1741,7 +1915,6 @@ frame_ancestor_p (struct frame *af, struct frame *df)
 
   return false;
 }
-#endif
 
 DEFUN ("frame-ancestor-p", Fframe_ancestor_p, Sframe_ancestor_p,
        2, 2, 0,
@@ -1752,15 +1925,10 @@ ANCESTOR and DESCENDANT must be live frames and default 
to the selected
 frame.  */)
      (Lisp_Object ancestor, Lisp_Object descendant)
 {
-#ifdef HAVE_WINDOW_SYSTEM
   struct frame *af = decode_live_frame (ancestor);
   struct frame *df = decode_live_frame (descendant);
-
   return frame_ancestor_p (af, df) ? Qt : Qnil;
-#else
-  return Qnil;
-#endif
-  }
+}
 
 /* Return CANDIDATE if it can be used as 'other-than-FRAME' frame on the
    same tty (for tty frames) or among frames which uses FRAME's keyboard.
@@ -2282,7 +2450,7 @@ delete_frame (Lisp_Object frame, Lisp_Object force)
   fset_root_window (f, Qnil);
 
   Vframe_list = Fdelq (frame, Vframe_list);
-  SET_FRAME_VISIBLE (f, 0);
+  SET_FRAME_VISIBLE (f, false);
 
   /* Allow the vector of menu bar contents to be freed in the next
      garbage collection.  The frame object itself may not be garbage
@@ -2868,6 +3036,9 @@ If omitted, FRAME defaults to the currently selected 
frame.  */)
   if (FRAME_WINDOW_P (f) && FRAME_TERMINAL (f)->frame_visible_invisible_hook)
     FRAME_TERMINAL (f)->frame_visible_invisible_hook (f, true);
 
+  if (is_tty_frame (f))
+    SET_FRAME_VISIBLE (f, true);
+
   make_frame_visible_1 (f->root_window);
 
   /* Make menu bar update for the Buffers and Frames menus.  */
@@ -2918,6 +3089,12 @@ displayed in the terminal.  */)
   if (FRAME_WINDOW_P (f) && FRAME_TERMINAL (f)->frame_visible_invisible_hook)
     FRAME_TERMINAL (f)->frame_visible_invisible_hook (f, false);
 
+  /* FIXME/tty: the Elisp info manual says that this "usually" makes
+     child frames invisible, too, but without saying when not. Since users
+     can't rely on this, it's not implemented. */
+  if (is_tty_frame (f))
+    SET_FRAME_VISIBLE (f, false);
+
   /* Make menu bar update for the Buffers and Frames menus.  */
   windows_or_buffers_changed = 16;
 
@@ -3012,12 +3189,7 @@ doesn't support multiple overlapping frames, this 
function selects FRAME.  */)
 
   XSETFRAME (frame, f);
 
-  if (FRAME_TERMCAP_P (f))
-    /* On a text terminal select FRAME.  */
-    Fselect_frame (frame, Qnil);
-  else
-    /* Do like the documentation says. */
-    Fmake_frame_visible (frame);
+  Fmake_frame_visible (frame);
 
   if (FRAME_TERMINAL (f)->frame_raise_lower_hook)
     (*FRAME_TERMINAL (f)->frame_raise_lower_hook) (f, true);
@@ -3318,6 +3490,16 @@ store_frame_param (struct frame *f, Lisp_Object prop, 
Lisp_Object val)
       val = old_val;
     }
 
+  /* FIXME/tty: re-parenting is currently not implemented when changing
+     a root frame to a child frame or vice versa. Could be done, but
+     it's unclear if it's worth it. */
+  if (is_tty_frame (f))
+    {
+      if (EQ (prop, Qparent_frame)
+         && NILP (f->parent_frame) != NILP (val))
+       error ("Making a root frame a child or vice versa is not supported");
+    }
+
   /* The tty color needed to be set before the frame's parameter
      alist was updated with the new value.  This is not true any more,
      but we still do this test early on.  */
@@ -3441,13 +3623,9 @@ If FRAME is omitted or nil, return information on the 
currently selected frame.
   else
 #endif
     {
-      /* This ought to be correct in f->param_alist for an X frame.  */
-      Lisp_Object lines;
-
-      XSETFASTINT (lines, FRAME_MENU_BAR_LINES (f));
-      store_in_alist (&alist, Qmenu_bar_lines, lines);
-      XSETFASTINT (lines, FRAME_TAB_BAR_LINES (f));
-      store_in_alist (&alist, Qtab_bar_lines, lines);
+      store_in_alist (&alist, Qmenu_bar_lines, make_fixnum 
(FRAME_MENU_BAR_LINES (f)));
+      store_in_alist (&alist, Qtab_bar_lines, make_fixnum (FRAME_TAB_BAR_LINES 
(f)));
+      store_in_alist (&alist, Qvisibility, FRAME_VISIBLE_P (f) ? Qt : Qnil);
     }
 
   return alist;
@@ -3525,7 +3703,6 @@ If FRAME is nil, describe the currently selected frame.  
*/)
   return value;
 }
 
-
 DEFUN ("modify-frame-parameters", Fmodify_frame_parameters,
        Smodify_frame_parameters, 2, 2, 0,
        doc: /* Modify FRAME according to new values of its parameters in ALIST.
@@ -3563,6 +3740,7 @@ list, but are otherwise ignored.  */)
       USE_SAFE_ALLOCA;
       SAFE_ALLOCA_LISP (parms, 2 * length);
       values = parms + length;
+      Lisp_Object params = alist;
 
       /* Extract parm names and values into those vectors.  */
 
@@ -3588,6 +3766,25 @@ list, but are otherwise ignored.  */)
            update_face_from_frame_parameter (f, prop, val);
        }
 
+      if (is_tty_child_frame (f))
+       {
+         f->left_pos = tty_child_pos_param (f, Qleft, params, f->left_pos);
+         f->top_pos = tty_child_pos_param (f, Qtop, params, f->top_pos);
+
+         int w = tty_child_size_param (f, Qwidth, params, f->total_cols);
+         int h = tty_child_size_param (f, Qheight, params, f->total_lines);
+         if (w != f->total_cols || h != f->total_lines)
+           change_frame_size (f, w, h, false, false, false);
+
+         Lisp_Object visible = Fassq (Qvisibility, params);
+         if (CONSP (visible))
+           SET_FRAME_VISIBLE (f, !NILP (Fcdr (visible)));
+
+         Lisp_Object no_special = Fassq (Qno_special_glyphs, params);
+         if (CONSP (no_special))
+           FRAME_NO_SPECIAL_GLYPHS (f) = !NILP (Fcdr (no_special));
+       }
+
       SAFE_FREE ();
     }
   return Qnil;
@@ -3935,6 +4132,11 @@ bottom edge of FRAME's display.  */)
       (void) yval;
 #endif
     }
+  else if (is_tty_child_frame (f))
+    {
+      f->left_pos = xval;
+      f->top_pos = yval;
+    }
 
   return Qt;
 }
@@ -4246,6 +4448,28 @@ frame_float (struct frame *f, Lisp_Object val, enum 
frame_float_type what,
     }
 }
 
+/* Handle frame parameter change with frame parameter handler. F is the
+   frame whose frame parameter was changed.  PROP is the name of the
+   frame parameter.  VAL and OLD_VALUE are the current value and old
+   value of the frame parameter. */
+
+static void
+handle_frame_param (struct frame *f, Lisp_Object prop, Lisp_Object val,
+                   Lisp_Object old_value)
+{
+  Lisp_Object param_index = Fget (prop, Qx_frame_parameter);
+  if (FIXNATP (param_index) && XFIXNAT (param_index) < ARRAYELTS (frame_parms))
+    {
+      if (FRAME_RIF (f))
+       {
+         frame_parm_handler handler
+           = FRAME_RIF (f)->frame_parm_handlers[XFIXNAT (param_index)];
+         if (handler)
+           handler (f, val, old_value);
+       }
+    }
+}
+
 /* Change the parameters of frame F as specified by ALIST.
    If a parameter is not specially recognized, do nothing special;
    otherwise call the `gui_set_...' function for that parameter.
@@ -4387,17 +4611,9 @@ gui_set_frame_parameters_1 (struct frame *f, Lisp_Object 
alist,
        }
       else
        {
-         Lisp_Object param_index, old_value;
-
-         old_value = get_frame_param (f, prop);
-
+         Lisp_Object old_value = get_frame_param (f, prop);
          store_frame_param (f, prop, val);
-
-         param_index = Fget (prop, Qx_frame_parameter);
-         if (FIXNATP (param_index)
-             && XFIXNAT (param_index) < ARRAYELTS (frame_parms)
-             && FRAME_RIF (f)->frame_parm_handlers[XFIXNUM (param_index)])
-           (*(FRAME_RIF (f)->frame_parm_handlers[XFIXNUM (param_index)])) (f, 
val, old_value);
+         handle_frame_param (f, prop, val, old_value);
 
          if (!default_parameter && EQ (prop, Qfont))
            /* The user manually specified the `font' frame parameter.
@@ -4716,14 +4932,7 @@ gui_set_screen_gamma (struct frame *f, Lisp_Object 
new_value, Lisp_Object old_va
   /* Apply the new gamma value to the frame background.  */
   bgcolor = Fassq (Qbackground_color, f->param_alist);
   if (CONSP (bgcolor) && (bgcolor = XCDR (bgcolor), STRINGP (bgcolor)))
-    {
-      Lisp_Object parm_index = Fget (Qbackground_color, Qx_frame_parameter);
-      if (FIXNATP (parm_index)
-         && XFIXNAT (parm_index) < ARRAYELTS (frame_parms)
-         && FRAME_RIF (f)->frame_parm_handlers[XFIXNAT (parm_index)])
-         (*FRAME_RIF (f)->frame_parm_handlers[XFIXNAT (parm_index)])
-           (f, bgcolor, Qnil);
-    }
+    handle_frame_param (f, Qbackground_color, bgcolor, Qnil);
 
   clear_face_cache (true);     /* FIXME: Why of all frames?  */
   fset_redisplay (f);
diff --git a/src/frame.h b/src/frame.h
index 1d920d1a6bc..6a636ecec3f 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -161,10 +161,8 @@ struct frame
      Usually it is nil.  */
   Lisp_Object title;
 
-#if defined (HAVE_WINDOW_SYSTEM)
   /* This frame's parent frame, if it has one.  */
   Lisp_Object parent_frame;
-#endif /* HAVE_WINDOW_SYSTEM */
 
   /* Last device to move over this frame.  Any value that isn't a
      string means the "Virtual core pointer".  */
@@ -385,15 +383,8 @@ struct frame
      zero if the frame has been made invisible without an icon.  */
 
   /* Nonzero if the frame is currently displayed; we check
-     it to see if we should bother updating the frame's contents.
-
-     On ttys and on Windows NT/9X, to avoid wasting effort updating
-     visible frames that are actually completely obscured by other
-     windows on the display, we bend the meaning of visible slightly:
-     if equal to 2, then the frame is obscured - we still consider
-     it to be "visible" as seen from lisp, but we don't bother
-     updating it.  */
-  unsigned visible : 2;
+     it to see if we should bother updating the frame's contents. */
+  unsigned visible : 1;
 
   /* True if the frame is currently iconified.  Do not
      set this directly, use SET_FRAME_ICONIFIED instead.  */
@@ -486,11 +477,11 @@ struct frame
 
   /* The z-group this frame's window belongs to. */
   ENUM_BF (z_group) z_group : 2;
+#endif /* HAVE_WINDOW_SYSTEM */
 
   /* Non-zero if display of truncation and continuation glyphs outside
      the fringes is suppressed.  */
   bool_bf no_special_glyphs : 1;
-#endif /* HAVE_WINDOW_SYSTEM */
 
   /* True means set_window_size_hook requests can be processed for
      this frame.  */
@@ -740,7 +731,10 @@ struct frame
 #ifdef HAVE_TEXT_CONVERSION
   /* Text conversion state used by certain input methods.  */
   struct text_conversion_state conversion;
-#endif
+# endif
+
+  /* Z-order of child frames. */
+  int z_order;
 } GCALIGNED_STRUCT;
 
 /* Most code should use these functions to set Lisp fields in struct frame.  */
@@ -1021,9 +1015,11 @@ default_pixels_per_inch_y (void)
    does not have FRAME_DISPLAY_INFO.  */
 #ifdef HAVE_WINDOW_SYSTEM
 #ifndef HAVE_ANDROID
-# define MOUSE_HL_INFO(F)                                      \
+#   define MOUSE_HL_INFO(F)                                    \
   (FRAME_WINDOW_P (F)                                          \
-   ? &FRAME_DISPLAY_INFO(F)->mouse_highlight                   \
+  ? (FRAME_OUTPUT_DATA (F)                                     \
+     ? &FRAME_DISPLAY_INFO (F)->mouse_highlight                        \
+     : NULL)                                                   \
    : &(F)->output_data.tty->display_info->mouse_highlight)
 #else
 /* There is no "struct tty_output" on Android at all.  */
@@ -1176,9 +1172,6 @@ default_pixels_per_inch_y (void)
                                  && FRAME_X_VISIBLE (f)))
 #endif
 
-/* True if frame F is currently visible but hidden.  */
-#define FRAME_OBSCURED_P(f) ((f)->visible > 1)
-
 /* True if frame F is currently iconified.  */
 #define FRAME_ICONIFIED_P(f) (f)->iconified
 
@@ -1243,6 +1236,12 @@ default_pixels_per_inch_y (void)
 #define FRAME_HAS_VERTICAL_SCROLL_BARS_ON_RIGHT(f) ((void) (f), 0)
 #endif /* HAVE_WINDOW_SYSTEM */
 
+INLINE struct frame *
+FRAME_PARENT_FRAME (struct frame *f)
+{
+  return NILP (f->parent_frame) ? NULL : XFRAME (f->parent_frame);
+}
+
 #if defined (HAVE_WINDOW_SYSTEM)
 #define FRAME_UNDECORATED(f) ((f)->undecorated)
 #ifdef HAVE_NTGUI
@@ -1250,14 +1249,9 @@ default_pixels_per_inch_y (void)
 #else
 #define FRAME_OVERRIDE_REDIRECT(f) ((f)->override_redirect)
 #endif
-#define FRAME_PARENT_FRAME(f)                  \
-  (NILP ((f)->parent_frame)                    \
-   ? NULL                                      \
-   : XFRAME ((f)->parent_frame))
 #define FRAME_SKIP_TASKBAR(f) ((f)->skip_taskbar)
 #define FRAME_NO_FOCUS_ON_MAP(f) ((f)->no_focus_on_map)
 #define FRAME_NO_ACCEPT_FOCUS(f) ((f)->no_accept_focus)
-#define FRAME_NO_SPECIAL_GLYPHS(f) ((f)->no_special_glyphs)
 #define FRAME_Z_GROUP(f) ((f)->z_group)
 #define FRAME_Z_GROUP_NONE(f) ((f)->z_group == z_group_none)
 #define FRAME_Z_GROUP_ABOVE(f) ((f)->z_group == z_group_above)
@@ -1272,11 +1266,9 @@ default_pixels_per_inch_y (void)
 #else /* not HAVE_WINDOW_SYSTEM */
 #define FRAME_UNDECORATED(f) ((void) (f), 0)
 #define FRAME_OVERRIDE_REDIRECT(f) ((void) (f), 0)
-#define FRAME_PARENT_FRAME(f) ((void) (f), NULL)
 #define FRAME_SKIP_TASKBAR(f) ((void) (f), 0)
 #define FRAME_NO_FOCUS_ON_MAP(f) ((void) (f), 0)
 #define FRAME_NO_ACCEPT_FOCUS(f) ((void) (f), 0)
-#define FRAME_NO_SPECIAL_GLYPHS(f) ((void) (f), 0)
 #define FRAME_Z_GROUP(f) ((void) (f), z_group_none)
 #define FRAME_Z_GROUP_NONE(f) ((void) (f), true)
 #define FRAME_Z_GROUP_ABOVE(f) ((void) (f), false)
@@ -1284,6 +1276,8 @@ default_pixels_per_inch_y (void)
 #define FRAME_TOOLTIP_P(f) ((void) f, false)
 #endif /* HAVE_WINDOW_SYSTEM */
 
+#define FRAME_NO_SPECIAL_GLYPHS(f) ((f)->no_special_glyphs)
+
 /* Whether horizontal scroll bars are currently enabled for frame F.  */
 #if USE_HORIZONTAL_SCROLL_BARS
 #define FRAME_HAS_HORIZONTAL_SCROLL_BARS(f) \
@@ -1445,9 +1439,8 @@ extern bool frame_garbaged;
    if some changes were applied to it while it wasn't visible (and hence
    wasn't redisplayed).  */
 INLINE void
-SET_FRAME_VISIBLE (struct frame *f, int v)
+SET_FRAME_VISIBLE (struct frame *f, bool v)
 {
-  eassert (0 <= v && v <= 2);
   if (v)
     {
       if (v == 1 && f->visible != 1)
@@ -1504,13 +1497,14 @@ extern struct frame *decode_any_frame (Lisp_Object);
 extern struct frame *make_initial_frame (void);
 extern struct frame *make_frame (bool);
 #ifdef HAVE_WINDOW_SYSTEM
-extern struct frame *make_minibuffer_frame (void);
-extern struct frame *make_frame_without_minibuffer (Lisp_Object,
-                                                    struct kboard *,
-                                                    Lisp_Object);
 extern bool display_available (void);
 #endif
 
+struct frame *make_minibuffer_frame (void);
+struct frame *
+make_frame_without_minibuffer (Lisp_Object mini_window,
+                              KBOARD *kb, Lisp_Object display);
+
 INLINE bool
 window_system_available (struct frame *f)
 {
@@ -1522,6 +1516,8 @@ window_system_available (struct frame *f)
 }
 
 extern WINDOW_SYSTEM_RETURN void check_window_system (struct frame *);
+void check_tty (struct frame *f);
+struct frame *decode_tty_frame (Lisp_Object frame);
 extern void frame_make_pointer_invisible (struct frame *);
 extern void frame_make_pointer_visible (struct frame *);
 extern Lisp_Object delete_frame (Lisp_Object, Lisp_Object);
@@ -1617,15 +1613,11 @@ FRAME_CHILD_FRAME_BORDER_WIDTH (struct frame *f)
 INLINE int
 FRAME_INTERNAL_BORDER_WIDTH (struct frame *f)
 {
-#ifdef HAVE_WINDOW_SYSTEM
   return (FRAME_PARENT_FRAME(f)
          ? (FRAME_CHILD_FRAME_BORDER_WIDTH(f) >= 0
             ? FRAME_CHILD_FRAME_BORDER_WIDTH(f)
             : frame_dimension (f->internal_border_width))
          : frame_dimension (f->internal_border_width));
-#else
-  return frame_dimension (f->internal_border_width);
-#endif
 }
 
 /* Pixel-size of window divider lines.  */
@@ -1880,7 +1872,6 @@ extern Lisp_Object gui_display_get_resource (Display_Info 
*,
 extern void set_frame_menubar (struct frame *f, bool deep_p);
 extern void frame_set_mouse_pixel_position (struct frame *f, int pix_x, int 
pix_y);
 extern void free_frame_menubar (struct frame *);
-extern bool frame_ancestor_p (struct frame *af, struct frame *df);
 extern enum internal_border_part frame_internal_border_part (struct frame *f, 
int x, int y);
 
 #if defined HAVE_X_WINDOWS
@@ -1907,6 +1898,8 @@ gui_set_bitmap_icon (struct frame *f)
 #endif /* !HAVE_NS */
 #endif /* HAVE_WINDOW_SYSTEM */
 
+extern bool frame_ancestor_p (struct frame *af, struct frame *df);
+
 INLINE void
 flush_frame (struct frame *f)
 {
diff --git a/src/keyboard.c b/src/keyboard.c
index 6d28dca9aeb..bfb5fd3592b 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -5415,7 +5415,7 @@ static const char *const lispy_kana_keys[] =
 
 /* You'll notice that this table is arranged to be conveniently
    indexed by X Windows keysym values.  */
-#ifdef HAVE_NS
+#if defined HAVE_NS || !defined HAVE_WINDOW_SYSTEM
 /* FIXME: Why are we using X11 keysym values for NS?  */
 static
 #endif
diff --git a/src/minibuf.c b/src/minibuf.c
index 1f94e0e650e..c8267045397 100644
--- a/src/minibuf.c
+++ b/src/minibuf.c
@@ -913,7 +913,11 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, 
Lisp_Object prompt,
       XWINDOW (minibuf_window)->cursor.hpos = 0;
       XWINDOW (minibuf_window)->cursor.x = 0;
       XWINDOW (minibuf_window)->must_be_updated_p = true;
-      update_frame (XFRAME (selected_frame), true, true);
+      struct frame *sf = XFRAME (selected_frame);
+      update_frame (sf, true, true);
+      if (is_tty_frame (sf))
+       combine_updates_for_frame (sf, true, true);
+
 #ifndef HAVE_NTGUI
       flush_frame (XFRAME (XWINDOW (minibuf_window)->frame));
 #else
diff --git a/src/nsfns.m b/src/nsfns.m
index 3c012ca8f05..1055a4b37df 100644
--- a/src/nsfns.m
+++ b/src/nsfns.m
@@ -3351,7 +3351,7 @@ DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0,
              [nswindow orderFront: NSApp];
              [nswindow display];
 
-             SET_FRAME_VISIBLE (tip_f, 1);
+             SET_FRAME_VISIBLE (tip_f, true);
              unblock_input ();
 
              goto start_timer;
@@ -3534,7 +3534,7 @@ DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0,
       [nswindow orderFront: NSApp];
       [nswindow display];
 
-      SET_FRAME_VISIBLE (tip_f, YES);
+      SET_FRAME_VISIBLE (tip_f, true);
       FRAME_PIXEL_WIDTH (tip_f) = width;
       FRAME_PIXEL_HEIGHT (tip_f) = height;
       unblock_input ();
diff --git a/src/nsterm.m b/src/nsterm.m
index f68a22d9fbc..7ad794b6bdb 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1505,7 +1505,7 @@ ns_make_frame_visible (struct frame *f)
       EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
       EmacsWindow *window = (EmacsWindow *)[view window];
 
-      SET_FRAME_VISIBLE (f, 1);
+      SET_FRAME_VISIBLE (f, true);
       ns_raise_frame (f, ! FRAME_NO_FOCUS_ON_MAP (f));
 
       /* Making a new frame from a fullscreen frame will make the new frame
@@ -1550,7 +1550,7 @@ ns_make_frame_invisible (struct frame *f)
   check_window_system (f);
   view = FRAME_NS_VIEW (f);
   [[view window] orderOut: NSApp];
-  SET_FRAME_VISIBLE (f, 0);
+  SET_FRAME_VISIBLE (f, false);
   SET_FRAME_ICONIFIED (f, 0);
 }
 
diff --git a/src/scroll.c b/src/scroll.c
index fd2a9e71207..1d01ae90d72 100644
--- a/src/scroll.c
+++ b/src/scroll.c
@@ -366,7 +366,7 @@ do_scrolling (struct frame *frame, struct glyph_matrix 
*current_matrix,
     eassert (copy_from[k] >= 0 && copy_from[k] < window_size);
 
   /* Perform the row swizzling.  */
-  mirrored_line_dance (current_matrix, unchanged_at_top, window_size,
+  mirrored_line_dance (frame, unchanged_at_top, window_size,
                       copy_from, retained_p);
 
   /* Some sanity checks if GLYPH_DEBUG is defined.  */
@@ -780,7 +780,7 @@ do_direct_scrolling (struct frame *frame, struct 
glyph_matrix *current_matrix,
      copy_from[i] gives the original line to copy to I, and
      retained_p[copy_from[i]] is zero if line I in the new display is
      empty.  */
-  mirrored_line_dance (current_matrix, unchanged_at_top, window_size,
+  mirrored_line_dance (frame, unchanged_at_top, window_size,
                       copy_from, retained_p);
 
   if (terminal_window_p)
diff --git a/src/term.c b/src/term.c
index 1f524880054..37c0780b555 100644
--- a/src/term.c
+++ b/src/term.c
@@ -65,8 +65,8 @@ static int been_here = -1;
 #ifndef HAVE_ANDROID
 
 static void tty_set_scroll_region (struct frame *f, int start, int stop);
-static void turn_on_face (struct frame *, int face_id);
-static void turn_off_face (struct frame *, int face_id);
+static void turn_on_face (struct frame *f, struct face *face);
+static void turn_off_face (struct frame *f, struct face *face);
 static void tty_turn_off_highlight (struct tty_display_info *);
 static void tty_show_cursor (struct tty_display_info *);
 static void tty_hide_cursor (struct tty_display_info *);
@@ -788,13 +788,20 @@ tty_write_glyphs (struct frame *f, struct glyph *string, 
int len)
       /* Identify a run of glyphs with the same face.  */
       int face_id = string->face_id;
 
+      /* FIXME/tty: it happens that a single glyph's frame is NULL. It
+        might depend on a tab bar line being present, then switching
+        from a buffer without header line to one with header line and
+        opening a child frame. */
+      struct frame *face_id_frame = string->frame ? string->frame : f;
+
       for (n = 1; n < stringlen; ++n)
-       if (string[n].face_id != face_id)
+       if (string[n].face_id != face_id || string[n].frame != face_id_frame)
          break;
 
       /* Turn appearance modes of the face of the run on.  */
       tty_highlight_if_desired (tty);
-      turn_on_face (f, face_id);
+      struct face *face = FACE_FROM_ID (face_id_frame, face_id);
+      turn_on_face (f, face);
 
       if (n == stringlen)
        /* This is the last run.  */
@@ -812,7 +819,7 @@ tty_write_glyphs (struct frame *f, struct glyph *string, 
int len)
       string += n;
 
       /* Turn appearance modes off.  */
-      turn_off_face (f, face_id);
+      turn_off_face (f, face);
       tty_turn_off_highlight (tty);
     }
 
@@ -822,8 +829,8 @@ tty_write_glyphs (struct frame *f, struct glyph *string, 
int len)
 #ifndef DOS_NT
 
 static void
-tty_write_glyphs_with_face (register struct frame *f, register struct glyph 
*string,
-                           register int len, register int face_id)
+tty_write_glyphs_with_face (struct frame *f, struct glyph *string,
+                           int len, struct face *face)
 {
   unsigned char *conversion_buffer;
   struct coding_system *coding;
@@ -856,7 +863,7 @@ tty_write_glyphs_with_face (register struct frame *f, 
register struct glyph *str
 
   /* Turn appearance modes of the face.  */
   tty_highlight_if_desired (tty);
-  turn_on_face (f, face_id);
+  turn_on_face (f, face);
 
   coding->mode |= CODING_MODE_LAST_BLOCK;
   conversion_buffer = encode_terminal_code (string, len, coding);
@@ -871,7 +878,7 @@ tty_write_glyphs_with_face (register struct frame *f, 
register struct glyph *str
     }
 
   /* Turn appearance modes off.  */
-  turn_off_face (f, face_id);
+  turn_off_face (f, face);
   tty_turn_off_highlight (tty);
 
   cmcheckmagic (tty);
@@ -919,6 +926,7 @@ tty_insert_glyphs (struct frame *f, struct glyph *start, 
int len)
 
   while (len-- > 0)
     {
+      struct face *face = NULL;
       OUTPUT1_IF (tty, tty->TS_ins_char);
       if (!start)
        {
@@ -928,7 +936,10 @@ tty_insert_glyphs (struct frame *f, struct glyph *start, 
int len)
       else
        {
          tty_highlight_if_desired (tty);
-         turn_on_face (f, start->face_id);
+         int face_id = start->face_id;
+         struct frame *face_id_frame = start->frame;
+         face = FACE_FROM_ID (face_id_frame, face_id);
+         turn_on_face (f, face);
          glyph = start;
          ++start;
          /* We must open sufficient space for a character which
@@ -957,9 +968,9 @@ tty_insert_glyphs (struct frame *f, struct glyph *start, 
int len)
        }
 
       OUTPUT1_IF (tty, tty->TS_pad_inserted_char);
-      if (start)
+      if (face)
        {
-         turn_off_face (f, glyph->face_id);
+         turn_off_face (f, face);
          tty_turn_off_highlight (tty);
        }
     }
@@ -1542,6 +1553,7 @@ append_glyph (struct it *it)
       glyph->type = CHAR_GLYPH;
       glyph->pixel_width = 1;
       glyph->u.ch = it->char_to_display;
+      glyph->frame = it->f;
       glyph->face_id = it->face_id;
       glyph->avoid_cursor_p = it->avoid_cursor_p;
       glyph->multibyte_p = it->multibyte_p;
@@ -1769,6 +1781,7 @@ append_composite_glyph (struct it *it)
 
       glyph->avoid_cursor_p = it->avoid_cursor_p;
       glyph->multibyte_p = it->multibyte_p;
+      glyph->frame = it->f;
       glyph->face_id = it->face_id;
       glyph->padding_p = false;
       glyph->charpos = CHARPOS (it->position);
@@ -1855,6 +1868,7 @@ append_glyphless_glyph (struct it *it, int face_id, const 
char *str)
   glyph->pixel_width = 1;
   glyph->avoid_cursor_p = it->avoid_cursor_p;
   glyph->multibyte_p = it->multibyte_p;
+  glyph->frame = it->f;
   glyph->face_id = face_id;
   glyph->padding_p = false;
   glyph->charpos = CHARPOS (it->position);
@@ -1981,9 +1995,8 @@ produce_glyphless_glyph (struct it *it, Lisp_Object 
acronym)
    FACE_ID is a realized face ID number, in the face cache.  */
 
 static void
-turn_on_face (struct frame *f, int face_id)
+turn_on_face (struct frame *f, struct face *face)
 {
-  struct face *face = FACE_FROM_ID (f, face_id);
   unsigned long fg = face->foreground;
   unsigned long bg = face->background;
   struct tty_display_info *tty = FRAME_TTY (f);
@@ -2064,9 +2077,8 @@ turn_on_face (struct frame *f, int face_id)
 /* Turn off appearances of face FACE_ID on tty frame F.  */
 
 static void
-turn_off_face (struct frame *f, int face_id)
+turn_off_face (struct frame *f, struct face *face)
 {
-  struct face *face = FACE_FROM_ID (f, face_id);
   struct tty_display_info *tty = FRAME_TTY (f);
 
   if (tty->TS_exit_attribute_mode)
@@ -2399,7 +2411,7 @@ A suspended tty may be resumed by calling `resume-tty' on 
it.  */)
       t->display_info.tty->output = 0;
 
       if (FRAMEP (t->display_info.tty->top_frame))
-        SET_FRAME_VISIBLE (XFRAME (t->display_info.tty->top_frame), 0);
+        SET_FRAME_VISIBLE (XFRAME (t->display_info.tty->top_frame), false);
 
     }
 
@@ -2482,7 +2494,7 @@ frame's terminal). */)
          get_tty_size (fileno (t->display_info.tty->input), &width, &height);
          if (width != old_width || height != old_height)
            change_frame_size (f, width, height, false, false, false);
-         SET_FRAME_VISIBLE (XFRAME (t->display_info.tty->top_frame), 1);
+         SET_FRAME_VISIBLE (XFRAME (t->display_info.tty->top_frame), true);
        }
 
       set_tty_hooks (t);
@@ -2563,22 +2575,24 @@ tty_draw_row_with_mouse_face (struct window *w, struct 
glyph_row *row,
   struct frame *f = XFRAME (WINDOW_FRAME (w));
   struct tty_display_info *tty = FRAME_TTY (f);
   int face_id = tty->mouse_highlight.mouse_face_face_id;
-  int save_x, save_y, pos_x, pos_y;
 
   if (end_hpos >= row->used[TEXT_AREA])
     nglyphs = row->used[TEXT_AREA] - start_hpos;
 
-  pos_y = row->y + WINDOW_TOP_EDGE_Y (w);
-  pos_x = row->used[LEFT_MARGIN_AREA] + start_hpos + WINDOW_LEFT_EDGE_X (w);
+  int pos_y = row->y + WINDOW_TOP_EDGE_Y (w);
+  int pos_x = row->used[LEFT_MARGIN_AREA] + start_hpos + WINDOW_LEFT_EDGE_X 
(w);
 
   /* Save current cursor coordinates.  */
-  save_y = curY (tty);
-  save_x = curX (tty);
+  int save_y = curY (tty);
+  int save_x = curX (tty);
   cursor_to (f, pos_y, pos_x);
 
   if (draw == DRAW_MOUSE_FACE)
-    tty_write_glyphs_with_face (f, row->glyphs[TEXT_AREA] + start_hpos,
-                               nglyphs, face_id);
+    {
+      struct glyph *glyph = row->glyphs[TEXT_AREA] + start_hpos;
+      struct face *face = FACE_FROM_ID (glyph->frame, face_id);
+      tty_write_glyphs_with_face (f, glyph, nglyphs, face);
+    }
   else if (draw == DRAW_NORMAL_TEXT)
     write_glyphs (f, row->glyphs[TEXT_AREA] + start_hpos, nglyphs);
 
@@ -3969,7 +3983,7 @@ tty_free_frame_resources (struct frame *f)
 
 #endif
 
-
+
 
 #ifndef HAVE_ANDROID
 
@@ -4044,6 +4058,8 @@ set_tty_hooks (struct terminal *terminal)
   terminal->read_socket_hook = &tty_read_avail_input; /* keyboard.c */
   terminal->delete_frame_hook = &tty_free_frame_resources;
   terminal->delete_terminal_hook = &delete_tty;
+
+  terminal->frame_raise_lower_hook = tty_raise_lower_frame;
   /* Other hooks are NULL by default.  */
 }
 
@@ -4714,6 +4730,184 @@ delete_tty (struct terminal *terminal)
 
 #endif
 
+/* Return geometric attributes of FRAME.  According to the value of
+   ATTRIBUTES return the outer edges of FRAME (Qouter_edges), the
+   native edges of FRAME (Qnative_edges), or the inner edges of frame
+   (Qinner_edges).  Any other value means to return the geometry as
+   returned by Fx_frame_geometry.  */
+
+static Lisp_Object
+tty_frame_geometry (Lisp_Object frame, Lisp_Object attribute)
+{
+  struct frame *f = decode_live_frame (frame);
+  if (FRAME_INITIAL_P (f) || !FRAME_TTY (f))
+    return Qnil;
+
+  int native_width = f->pixel_width;
+  int native_height = f->pixel_height;
+
+  eassert (FRAME_PARENT_FRAME (f) || (f->left_pos == 0 && f->top_pos == 0));
+  int outer_left = f->left_pos;
+  int outer_top = f->top_pos;
+  int outer_right = outer_left + native_width;
+  int outer_bottom = outer_top + native_height;
+
+  int native_left = outer_left;
+  int native_top = outer_top;
+  int native_right = outer_right;
+  int native_bottom = outer_bottom;
+
+  int internal_border_width = FRAME_INTERNAL_BORDER_WIDTH (f);
+  int inner_left = native_left + internal_border_width;
+  int inner_top = native_top + internal_border_width;
+  int inner_right = native_right - internal_border_width;
+  int inner_bottom = native_bottom - internal_border_width;
+
+  int menu_bar_height = FRAME_MENU_BAR_HEIGHT (f);
+  inner_top += menu_bar_height;
+  int menu_bar_width = menu_bar_height ? native_width : 0;
+
+  int tab_bar_height = FRAME_TAB_BAR_HEIGHT (f);
+  int tab_bar_width = (tab_bar_height
+                      ? native_width - 2 * internal_border_width
+                      : 0);
+  inner_top += tab_bar_height;
+
+  int tool_bar_height = FRAME_TOOL_BAR_HEIGHT (f);
+  int tool_bar_width = (tool_bar_height
+                       ? native_width - 2 * internal_border_width
+                       : 0);
+
+  /* Subtract or add to the inner dimensions based on the tool bar
+     position.  */
+  if (EQ (FRAME_TOOL_BAR_POSITION (f), Qtop))
+    inner_top += tool_bar_height;
+  else
+    inner_bottom -= tool_bar_height;
+
+  /* Construct list.  */
+  if (EQ (attribute, Qouter_edges))
+    return list4i (outer_left, outer_top, outer_right, outer_bottom);
+  else if (EQ (attribute, Qnative_edges))
+    return list4i (native_left, native_top, native_right, native_bottom);
+  else if (EQ (attribute, Qinner_edges))
+    return list4i (inner_left, inner_top, inner_right, inner_bottom);
+  else
+    return list (Fcons (Qouter_position, Fcons (make_fixnum (outer_left),
+                                               make_fixnum (outer_top))),
+                Fcons (Qouter_size,
+                       Fcons (make_fixnum (outer_right - outer_left),
+                              make_fixnum (outer_bottom - outer_top))),
+                Fcons (Qouter_border_width, make_fixnum (0)),
+                Fcons (Qexternal_border_size,
+                       Fcons (make_fixnum (0), make_fixnum (0))),
+                Fcons (Qtitle_bar_size,
+                       Fcons (make_fixnum (0), make_fixnum (0))),
+                Fcons (Qmenu_bar_external, Qnil),
+                Fcons (Qmenu_bar_size,
+                       Fcons (make_fixnum (menu_bar_width),
+                              make_fixnum (menu_bar_height))),
+                Fcons (Qtab_bar_size,
+                       Fcons (make_fixnum (tab_bar_width),
+                              make_fixnum (tab_bar_height))),
+                Fcons (Qtool_bar_external, Qnil),
+                Fcons (Qtool_bar_position, FRAME_TOOL_BAR_POSITION (f)),
+                Fcons (Qtool_bar_size,
+                       Fcons (make_fixnum (tool_bar_width),
+                              make_fixnum (tool_bar_height))),
+                Fcons (Qinternal_border_width,
+                       make_fixnum (internal_border_width)));
+}
+
+DEFUN ("tty-frame-geometry", Ftty_frame_geometry, Stty_frame_geometry, 0, 1, 0,
+       doc: /* Return geometric attributes of terminal frame FRAME.
+              See also `frame-geometry'.  */)
+  (Lisp_Object frame)
+{
+  return tty_frame_geometry (frame, Qnil);
+}
+
+DEFUN ("tty-frame-edges", Ftty_frame_edges, Stty_frame_edges, 0, 2, 0,
+       doc: /* Return coordinates of FRAME's edges.
+              See also `frame-edges'.  */)
+  (Lisp_Object frame, Lisp_Object type)
+{
+  if (!EQ (type, Qouter_edges) && !EQ (type, Qinner_edges))
+    type = Qnative_edges;
+  return tty_frame_geometry (frame, type);
+}
+
+DEFUN ("tty-frame-list-z-order", Ftty_frame_list_z_order,
+       Stty_frame_list_z_order, 0, 1, 0,
+       doc: /* Return list of Emacs's frames, in Z (stacking) order.
+              See also `frame-list-z-order'.  */)
+  (Lisp_Object frame)
+{
+  struct frame *f = decode_tty_frame (frame);
+  Lisp_Object frames = frames_in_reverse_z_order (f, true);
+  return Fnreverse (frames);
+}
+
+DEFUN ("tty-frame-restack", Ftty_frame_restack,
+       Stty_frame_restack, 2, 3, 0,
+       doc: /* Restack FRAME1 below FRAME2 on terminals.
+.             See also `frame-restack'.  */)
+  (Lisp_Object frame1, Lisp_Object frame2, Lisp_Object above)
+{
+  /* FIXME/tty: implementation.  */
+  return Qnil;
+}
+
+static void
+tty_display_dimension (Lisp_Object frame, int *width, int *height)
+{
+  if (!FRAMEP (frame))
+    frame = Fselected_frame ();
+  struct frame *f = XFRAME (frame);
+  switch (f->output_method)
+    {
+    case output_initial:
+      *width = 80;
+      *height = 25;
+      break;
+    case output_termcap:
+      *width = FrameCols (FRAME_TTY (f));
+      *height = FrameRows (FRAME_TTY (f));
+      break;
+    case output_x_window:
+    case output_msdos_raw:
+    case output_w32:
+    case output_ns:
+    case output_pgtk:
+    case output_haiku:
+    case output_android:
+      emacs_abort ();
+      break;
+    }
+}
+
+DEFUN ("tty-display-pixel-width", Ftty_display_pixel_width,
+       Stty_display_pixel_width, 0, 1, 0,
+       doc: /* Return the width of DISPLAY's screen in pixels.
+.             See also `display-pixel-width'.  */)
+  (Lisp_Object display)
+{
+  int width, height;
+  tty_display_dimension (display, &width, &height);
+  return make_fixnum (width);
+}
+
+DEFUN ("tty-display-pixel-height", Ftty_display_pixel_height,
+       Stty_display_pixel_height, 0, 1, 0,
+       doc: /* Return the height of DISPLAY's screen in pixels.
+              See also `display-pixel-height'.  */)
+  (Lisp_Object display)
+{
+  int width, height;
+  tty_display_dimension (display, &width, &height);
+  return make_fixnum (height);
+}
+
 void
 syms_of_term (void)
 {
@@ -4770,6 +4964,13 @@ trigger redisplay.  */);
   defsubr (&Sgpm_mouse_stop);
 #endif /* HAVE_GPM */
 
+  defsubr (&Stty_frame_geometry);
+  defsubr (&Stty_frame_edges);
+  defsubr (&Stty_frame_list_z_order);
+  defsubr (&Stty_frame_restack);
+  defsubr (&Stty_display_pixel_width);
+  defsubr (&Stty_display_pixel_height);
+
 #if !defined DOS_NT && !defined HAVE_ANDROID
   default_orig_pair = NULL;
   default_set_foreground = NULL;
diff --git a/src/terminal.c b/src/terminal.c
index 5c21341d905..ad488ea3b52 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -111,7 +111,8 @@ void
 cursor_to (struct frame *f, int vpos, int hpos)
 {
   if (FRAME_TERMINAL (f)->cursor_to_hook)
-    (*FRAME_TERMINAL (f)->cursor_to_hook) (f, vpos, hpos);
+    (*FRAME_TERMINAL (f)->cursor_to_hook) (f, vpos + f->top_pos,
+                                          hpos + f->left_pos);
 }
 
 /* Similar but don't take any account of the wasted characters.  */
@@ -120,7 +121,8 @@ void
 raw_cursor_to (struct frame *f, int row, int col)
 {
   if (FRAME_TERMINAL (f)->raw_cursor_to_hook)
-    (*FRAME_TERMINAL (f)->raw_cursor_to_hook) (f, row, col);
+    (*FRAME_TERMINAL (f)->raw_cursor_to_hook) (f, row + f->top_pos,
+                                              col + f->left_pos);
 }
 
 /* Erase operations.  */
diff --git a/src/xdisp.c b/src/xdisp.c
index c74e81a3933..f19706005ad 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -943,7 +943,7 @@ redisplay_trace (char const *fmt, ...)
     {
       va_list ap;
       va_start (ap, fmt);
-      vprintf (fmt, ap);
+      vfprintf (stderr, fmt, ap);
       va_end (ap);
     }
 }
@@ -961,7 +961,7 @@ move_trace (char const *fmt, ...)
     {
       va_list ap;
       va_start (ap, fmt);
-      vprintf (fmt, ap);
+      vfprintf (stderr, fmt, ap);
       va_end (ap);
     }
 }
@@ -3348,9 +3348,7 @@ init_iterator (struct it *it, struct window *w,
      of the iterator's frame, when set, suppresses their display - by
      default for tooltip frames and when set via the 'no-special-glyphs'
      frame parameter.  */
-#ifdef HAVE_WINDOW_SYSTEM
-  if (!(FRAME_WINDOW_P (it->f) && it->f->no_special_glyphs))
-#endif
+  if (!it->f->no_special_glyphs)
     {
       if (it->line_wrap == TRUNCATE)
        {
@@ -13554,7 +13552,11 @@ echo_area_display (bool update_frame_p)
              flush_frame (f);
            }
          else
-           update_frame (f, true, true);
+           {
+             update_frame (f, true, true);
+             if (is_tty_frame (f))
+               combine_updates_for_frame (f, true, true);
+           }
 
          /* If cursor is in the echo area, make sure that the next
             redisplay displays the minibuffer, so that the cursor will
@@ -17033,6 +17035,9 @@ redisplay_internal (void)
   if (face_change)
     windows_or_buffers_changed = 47;
 
+  /* FIXME/tty: can we do better for tty child frames? It could be
+     a bit faster when we switch between child frames of the same
+     root frame. OTOH, it's probably not a frequent use case. */
   if ((FRAME_TERMCAP_P (sf) || FRAME_MSDOS_P (sf))
       && FRAME_TTY (sf)->previous_frame != sf)
     {
@@ -17055,6 +17060,7 @@ redisplay_internal (void)
     {
       struct frame *f = XFRAME (frame);
 
+      /* FRAME_REDISPLAY_P true basically means the frame is visible. */
       if (FRAME_REDISPLAY_P (f))
        {
          ++number_of_visible_frames;
@@ -17198,7 +17204,6 @@ redisplay_internal (void)
       && !current_buffer->clip_changed
       && !current_buffer->prevent_redisplay_optimizations_p
       && FRAME_REDISPLAY_P (XFRAME (w->frame))
-      && !FRAME_OBSCURED_P (XFRAME (w->frame))
       && !XFRAME (w->frame)->cursor_type_changed
       && !XFRAME (w->frame)->face_change
       /* Make sure recorded data applies to current buffer, etc.  */
@@ -17447,22 +17452,19 @@ redisplay_internal (void)
 
       propagate_buffer_redisplay ();
 
+      Lisp_Object tty_root_frames = Qnil;
       FOR_EACH_FRAME (tail, frame)
        {
          struct frame *f = XFRAME (frame);
 
-         /* We don't have to do anything for unselected terminal
-            frames.  */
-         if ((FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f))
-             && !EQ (FRAME_TTY (f)->top_frame, frame))
-           continue;
+         if (is_tty_root_frame (f))
+           tty_root_frames = Fcons (frame, tty_root_frames);
 
        retry_frame:
          if (FRAME_WINDOW_P (f) || FRAME_TERMCAP_P (f) || f == sf)
            {
-             bool gcscrollbars
-               /* Only GC scrollbars when we redisplay the whole frame.  */
-               = f->redisplay || !REDISPLAY_SOME_P ();
+             /* Only GC scrollbars when we redisplay the whole frame.  */
+             bool gcscrollbars = f->redisplay || !REDISPLAY_SOME_P ();
              bool f_redisplay_flag = f->redisplay;
 
              /* The X error handler may have deleted that frame before
@@ -17479,7 +17481,7 @@ redisplay_internal (void)
              if (gcscrollbars && FRAME_TERMINAL (f)->condemn_scroll_bars_hook)
                FRAME_TERMINAL (f)->condemn_scroll_bars_hook (f);
 
-             if (FRAME_REDISPLAY_P (f) && !FRAME_OBSCURED_P (f))
+             if (FRAME_REDISPLAY_P (f))
                {
                  /* Don't allow freeing images and faces for this
                     frame as long as the frame's update wasn't
@@ -17505,7 +17507,9 @@ redisplay_internal (void)
              if (gcscrollbars && FRAME_TERMINAL (f)->judge_scroll_bars_hook)
                FRAME_TERMINAL (f)->judge_scroll_bars_hook (f);
 
-             if (FRAME_REDISPLAY_P (f) && !FRAME_OBSCURED_P (f))
+             /* FIXME/tty: The FRAME_OBSCURE_P (f->visible == 2, :-()
+                seems to be something for Windows. I'm ignoring it. */
+             if (FRAME_REDISPLAY_P (f))
                {
                  /* If fonts changed on visible frame, display again.  */
                  if (f->fonts_changed)
@@ -17590,6 +17594,9 @@ redisplay_internal (void)
            }
        }
 
+      if (CONSP (tty_root_frames))
+       pending |= combine_updates (tty_root_frames, false, false);
+
       eassert (EQ (XFRAME (selected_frame)->selected_window, selected_window));
 
       if (!pending)
@@ -17611,7 +17618,7 @@ redisplay_internal (void)
            }
        }
     }
-  else if (FRAME_REDISPLAY_P (sf) && !FRAME_OBSCURED_P (sf))
+  else if (FRAME_REDISPLAY_P (sf))
     {
       sf->inhibit_clear_image_cache = true;
       displayed_buffer = XBUFFER (XWINDOW (selected_window)->contents);
@@ -17662,7 +17669,7 @@ redisplay_internal (void)
        unrequest_sigio ();
       STOP_POLLING;
 
-      if (FRAME_REDISPLAY_P (sf) && !FRAME_OBSCURED_P (sf))
+      if (FRAME_REDISPLAY_P (sf))
        {
           if (hscroll_retries <= MAX_HSCROLL_RETRIES
               && hscroll_windows (selected_window))
@@ -17673,6 +17680,10 @@ redisplay_internal (void)
 
          XWINDOW (selected_window)->must_be_updated_p = true;
          pending = update_frame (sf, false, false);
+
+         if (is_tty_frame (sf))
+           pending |= combine_updates_for_frame (sf, false, false);
+
          sf->cursor_type_changed = false;
          sf->inhibit_clear_image_cache = false;
        }
@@ -17685,10 +17696,13 @@ redisplay_internal (void)
       Lisp_Object mini_window = FRAME_MINIBUF_WINDOW (sf);
       struct frame *mini_frame = XFRAME (WINDOW_FRAME (XWINDOW (mini_window)));
 
-      if (mini_frame != sf && FRAME_WINDOW_P (mini_frame))
+      /* FIXME/tty: is removing FRAME_WINDOW_P here right? */
+      if (mini_frame != sf /*&& FRAME_WINDOW_P (mini_frame)*/)
        {
          XWINDOW (mini_window)->must_be_updated_p = true;
          pending |= update_frame (mini_frame, false, false);
+         if (is_tty_frame (mini_frame))
+           pending |= combine_updates_for_frame (mini_frame, false, false);
          mini_frame->cursor_type_changed = false;
           if (!pending && hscroll_retries <= MAX_HSCROLL_RETRIES
               && hscroll_windows (mini_window))
@@ -23975,6 +23989,7 @@ extend_face_to_end_of_line (struct it *it)
        {
          it->glyph_row->glyphs[TEXT_AREA][0] = space_glyph;
          it->glyph_row->glyphs[TEXT_AREA][0].face_id = face->id;
+         it->glyph_row->glyphs[TEXT_AREA][0].frame = f;
          it->glyph_row->used[TEXT_AREA] = 1;
        }
       /* Mode line and the header line don't have margins, and
@@ -23994,6 +24009,7 @@ extend_face_to_end_of_line (struct it *it)
              it->glyph_row->glyphs[LEFT_MARGIN_AREA][0] = space_glyph;
              it->glyph_row->glyphs[LEFT_MARGIN_AREA][0].face_id =
                default_face->id;
+             it->glyph_row->glyphs[LEFT_MARGIN_AREA][0].frame = f;
              it->glyph_row->used[LEFT_MARGIN_AREA] = 1;
            }
          if (WINDOW_RIGHT_MARGIN_WIDTH (it->w) > 0
@@ -24002,6 +24018,7 @@ extend_face_to_end_of_line (struct it *it)
              it->glyph_row->glyphs[RIGHT_MARGIN_AREA][0] = space_glyph;
              it->glyph_row->glyphs[RIGHT_MARGIN_AREA][0].face_id =
                default_face->id;
+             it->glyph_row->glyphs[RIGHT_MARGIN_AREA][0].frame = f;
              it->glyph_row->used[RIGHT_MARGIN_AREA] = 1;
            }
 
@@ -24366,9 +24383,11 @@ highlight_trailing_whitespace (struct it *it)
              while (glyph >= start
                     && BUFFERP (glyph->object)
                     && (glyph->type == STRETCH_GLYPH
-                        || (glyph->type == CHAR_GLYPH
-                            && glyph->u.ch == ' ')))
-               (glyph--)->face_id = face_id;
+                        || (glyph->type == CHAR_GLYPH && glyph->u.ch == ' ')))
+               {
+                 glyph->frame = it->f;
+                 (glyph--)->face_id = face_id;
+               }
            }
          else
            {
@@ -24377,7 +24396,10 @@ highlight_trailing_whitespace (struct it *it)
                     && (glyph->type == STRETCH_GLYPH
                         || (glyph->type == CHAR_GLYPH
                             && glyph->u.ch == ' ')))
-               (glyph++)->face_id = face_id;
+               {
+                 glyph->frame = it->f;
+                 (glyph++)->face_id = face_id;
+               }
            }
        }
     }
@@ -27230,7 +27252,7 @@ display_menu_bar (struct window *w)
 
 /* Deep copy of a glyph row, including the glyphs.  */
 static void
-deep_copy_glyph_row (struct glyph_row *to, struct glyph_row *from)
+deep_copy_glyph_row (struct frame *f, struct glyph_row *to, struct glyph_row 
*from)
 {
   struct glyph *pointers[1 + LAST_AREA];
   int to_used = to->used[TEXT_AREA];
@@ -27251,7 +27273,7 @@ deep_copy_glyph_row (struct glyph_row *to, struct 
glyph_row *from)
   /* If we filled only part of the TO row, fill the rest with
      space_glyph (which will display as empty space).  */
   if (to_used > from->used[TEXT_AREA])
-    fill_up_frame_row_with_spaces (to, to_used);
+    fill_up_frame_row_with_spaces (f, to, to_used);
 }
 
 /* Display one menu item on a TTY, by overwriting the glyphs in the
@@ -27300,7 +27322,7 @@ display_tty_menu_item (const char *item_text, int 
width, int face_id,
   it.last_visible_x = FRAME_COLS (f) - 1;
   row = it.glyph_row;
   /* Start with the row contents from the current matrix.  */
-  deep_copy_glyph_row (row, f->current_matrix->rows + y);
+  deep_copy_glyph_row (f, row, f->current_matrix->rows + y);
   bool saved_width = row->full_width_p;
   row->full_width_p = true;
   bool saved_reversed = row->reversed_p;
diff --git a/src/xfaces.c b/src/xfaces.c
index e248279e9b7..fd321734d14 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -696,7 +696,6 @@ void
 free_frame_faces (struct frame *f)
 {
   struct face_cache *face_cache = FRAME_FACE_CACHE (f);
-
   if (face_cache)
     {
       free_face_cache (face_cache);



reply via email to

[Prev in Thread] Current Thread [Next in Thread]