unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* "Final" version of tty child frames
@ 2024-10-22  4:46 Gerd Möllmann
  2024-10-22  5:29 ` Eli Zaretskii
                   ` (4 more replies)
  0 siblings, 5 replies; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-22  4:46 UTC (permalink / raw)
  To: Emacs Devel

I have (re-)created the scratch/tty-child-frames branch today, which
contains the code for child frames on ttys, based on a recent master.

I'm a happy user of this for a while now with corfu, vertico +
vertico-posframe + consult, transient + transient-posframe,
which-key + which-key-posframe. And my current todo list is now empty,
so here it is.

To use it, redefine these two functions:

#+begin_src emacs-lisp
(defun posframe-workable-p ()
  "Test posframe workable status."
  (and (>= emacs-major-version 26)
       (not (or noninteractive
                emacs-basic-display
                (or (featurep 'tty-child-frames)
                    (not (display-graphic-p)))
                (eq (frame-parameter (selected-frame) 'minibuffer) 'only)))))))

(cl-defgeneric corfu--popup-support-p ()
  "Return non-nil if child frames are supported."
  (or (display-graphic-p)
      (featurep 'tty-child-frames)))
#+end_src

To make things look nicer you might also want to

#+begin_src emacs-lisp
(push '(tty-non-selected-cursor . t) vertico-posframe-parameters)
(push '(undecorated . nil) vertico-posframe-parameters))
(push '(undecorated . nil) transient-posframe-parameters))
#+end_src

The 'undecorated' frame parameter lets Emacs draw a border around the
child frame (default is no border). The tty-non-selected-cursor
parameter makes redisplay put the terminal cursor in a non-selected
frame which I find nice for things like consult-buffer. Which is why I
added it :-).

What's there fits my personal needs entirely. I am not interested in
full support of child frames as they exist on window systems because I
don't see child frames being used except for things like posframe and
corfu and similar. And, TBH, I don't find the tty frame code fun to
work with.

Other things not contained:

- Support for anything but termcap frames. I bet I broke MSDOS.
- Mouse support except with xterm-mouse-mode (no GPM).
- Drawing borders like it is normally done (internal border and so
  on). Tried that once, git reset --hard, won't happen.
- Tooltips. I think tooltips could relatively easily be implemented
  with child frames by copying or extracting the non-native tooltip
  code from x-show-tip or some such. From my POV, that would be best
  done by refactoring the tooltip code across window systems which I
  can't do.
- Documentation. Would only make sense to write if this goes into GNU,
  which might or might not happen, depending on, from my side, how
  much work that is.

Disclaimer: As I mentioned already in other contexts, I don't want to
be the maintainer of anything, for personal reasons.

Have fun!



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22  4:46 "Final" version of tty child frames Gerd Möllmann
@ 2024-10-22  5:29 ` Eli Zaretskii
  2024-10-22  9:58   ` martin rudalics
  2024-10-28  4:35   ` Jared Finder
  2024-10-22  7:34 ` Dr. Arne Babenhauserheide
                   ` (3 subsequent siblings)
  4 siblings, 2 replies; 80+ messages in thread
From: Eli Zaretskii @ 2024-10-22  5:29 UTC (permalink / raw)
  To: Gerd Möllmann, Jared Finder; +Cc: emacs-devel, martin rudalics

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Date: Tue, 22 Oct 2024 06:46:15 +0200
> 
> I have (re-)created the scratch/tty-child-frames branch today, which
> contains the code for child frames on ttys, based on a recent master.

Thanks.

> Disclaimer: As I mentioned already in other contexts, I don't want to
> be the maintainer of anything, for personal reasons.

We have difficulty working with "fire and forget" contributions of
this size and complexity.  Maybe Jared (CC'ed) could take a look and
clean up the branch, to prepare it for landing.  Or someone else with
interest in this stuff (Martin?).



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22  4:46 "Final" version of tty child frames Gerd Möllmann
  2024-10-22  5:29 ` Eli Zaretskii
@ 2024-10-22  7:34 ` Dr. Arne Babenhauserheide
  2024-10-22  7:49   ` Gerd Möllmann
  2024-10-22  7:49   ` Eli Zaretskii
  2024-10-22  8:01 ` Eli Zaretskii
                   ` (2 subsequent siblings)
  4 siblings, 2 replies; 80+ messages in thread
From: Dr. Arne Babenhauserheide @ 2024-10-22  7:34 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Emacs Devel

[-- Attachment #1: Type: text/plain, Size: 434 bytes --]

Gerd Möllmann <gerd.moellmann@gmail.com> writes:

> - Documentation. Would only make sense to write if this goes into GNU,
>   which might or might not happen, depending on, from my side, how
>   much work that is.

Do you already have copyright assignment done so people can simply take
this up and merge it where it fits?

Best wishes,
Arne
-- 
Unpolitisch sein
heißt politisch sein,
ohne es zu merken.
draketo.de

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 1125 bytes --]

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22  7:34 ` Dr. Arne Babenhauserheide
@ 2024-10-22  7:49   ` Gerd Möllmann
  2024-10-22  7:49   ` Eli Zaretskii
  1 sibling, 0 replies; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-22  7:49 UTC (permalink / raw)
  To: Dr. Arne Babenhauserheide; +Cc: Emacs Devel

"Dr. Arne Babenhauserheide" <arne_bab@web.de> writes:

> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
>
>> - Documentation. Would only make sense to write if this goes into GNU,
>>   which might or might not happen, depending on, from my side, how
>>   much work that is.
>
> Do you already have copyright assignment done so people can simply take
> this up and merge it where it fits?

I have :-).



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22  7:34 ` Dr. Arne Babenhauserheide
  2024-10-22  7:49   ` Gerd Möllmann
@ 2024-10-22  7:49   ` Eli Zaretskii
  1 sibling, 0 replies; 80+ messages in thread
From: Eli Zaretskii @ 2024-10-22  7:49 UTC (permalink / raw)
  To: Dr. Arne Babenhauserheide; +Cc: gerd.moellmann, emacs-devel

> From: "Dr. Arne Babenhauserheide" <arne_bab@web.de>
> Cc: Emacs Devel <emacs-devel@gnu.org>
> Date: Tue, 22 Oct 2024 09:34:55 +0200
> 
> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
> 
> > - Documentation. Would only make sense to write if this goes into GNU,
> >   which might or might not happen, depending on, from my side, how
> >   much work that is.
> 
> Do you already have copyright assignment done so people can simply take
> this up and merge it where it fits?

Gerd has assignment on file for a long time.  Otherwise, he wouldn't
have had write access to the Emacs Git repository, and couldn't have
pushed this branch in the first place.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22  4:46 "Final" version of tty child frames Gerd Möllmann
  2024-10-22  5:29 ` Eli Zaretskii
  2024-10-22  7:34 ` Dr. Arne Babenhauserheide
@ 2024-10-22  8:01 ` Eli Zaretskii
  2024-10-22  8:21   ` Gerd Möllmann
  2024-10-23  3:05 ` Feng Shu
  2024-10-26  8:15 ` Gerd Möllmann
  4 siblings, 1 reply; 80+ messages in thread
From: Eli Zaretskii @ 2024-10-22  8:01 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Date: Tue, 22 Oct 2024 06:46:15 +0200
> 
> I have (re-)created the scratch/tty-child-frames branch today, which
> contains the code for child frames on ttys, based on a recent master.
> 
> I'm a happy user of this for a while now with corfu, vertico +
> vertico-posframe + consult, transient + transient-posframe,
> which-key + which-key-posframe. And my current todo list is now empty,
> so here it is.

Is there a way to test the feature without using corfu?  If so, can
you suggest some simple Lisp to see if the child frames work in a
build of this branch?

> Disclaimer: As I mentioned already in other contexts, I don't want to
> be the maintainer of anything, for personal reasons.
> 
> Have fun!

Does this compile cleanly for you?  I get gobs of warnings like this:

  dispnew.c: In function ‘gui_update_window_end’:
  dispnew.c:4495:34: warning: potential null pointer dereference [-Wnull-dereference]
   4495 |       hlinfo->mouse_face_beg_row = hlinfo->mouse_face_beg_col = -1;
	|       ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This seems to be because you've changed the definition of
MOUSE_HL_INFO to be this:

  #   define MOUSE_HL_INFO(F)                                     \
    (FRAME_WINDOW_P (F)                                           \
    ? (FRAME_OUTPUT_DATA (F)                                      \
       ? &FRAME_DISPLAY_INFO (F)->mouse_highlight                 \
       : NULL)                                                    \
     : &(F)->output_data.tty->display_info->mouse_highlight)

I don't understand the need for this NULL there.  What is its purpose,
and what will we lose by going back to the original definition?  I
believe that NULL is what's causing these warnings.

Thanks.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22  8:01 ` Eli Zaretskii
@ 2024-10-22  8:21   ` Gerd Möllmann
  2024-10-22  8:57     ` Eli Zaretskii
  2024-10-22  9:42     ` Eli Zaretskii
  0 siblings, 2 replies; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-22  8:21 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Gerd Möllmann <gerd.moellmann@gmail.com>
>> Date: Tue, 22 Oct 2024 06:46:15 +0200
>> 
>> I have (re-)created the scratch/tty-child-frames branch today, which
>> contains the code for child frames on ttys, based on a recent master.
>> 
>> I'm a happy user of this for a while now with corfu, vertico +
>> vertico-posframe + consult, transient + transient-posframe,
>> which-key + which-key-posframe. And my current todo list is now empty,
>> so here it is.
>
> Is there a way to test the feature without using corfu?  If so, can
> you suggest some simple Lisp to see if the child frames work in a
> build of this branch?

Thanks for taking a look.

Something built-in you could try is show-paren-mode with
show-paren-context-when-offscreen set to 'child-frame. Not very useful,
but it should display a child frame at (0,0) of a window.

Or maybe

(defun my-make-child ()
  (interactive)
  (make-frame `((parent-frame . ,(selected-frame))
		(background-color . "gray10")
		(foreground-color . "white")
		(internal-border-width . 1)
		(top . 15)
		(left . 40)
		(width . 80)
		(height . 25))))

which is not really the use case I have in mind but anyway.

>> Disclaimer: As I mentioned already in other contexts, I don't want to
>> be the maintainer of anything, for personal reasons.
>> 
>> Have fun!
>
> Does this compile cleanly for you?  I get gobs of warnings like this:

Yes it does, with clang.

>   dispnew.c: In function ‘gui_update_window_end’:
>   dispnew.c:4495:34: warning: potential null pointer dereference [-Wnull-dereference]
>    4495 |       hlinfo->mouse_face_beg_row = hlinfo->mouse_face_beg_col = -1;
> 	|       ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
> This seems to be because you've changed the definition of
> MOUSE_HL_INFO to be this:
>
>   #   define MOUSE_HL_INFO(F)                                     \
>     (FRAME_WINDOW_P (F)                                           \
>     ? (FRAME_OUTPUT_DATA (F)                                      \
>        ? &FRAME_DISPLAY_INFO (F)->mouse_highlight                 \
>        : NULL)                                                    \
>      : &(F)->output_data.tty->display_info->mouse_highlight)
>
> I don't understand the need for this NULL there.  What is its purpose,
> and what will we lose by going back to the original definition?  I
> believe that NULL is what's causing these warnings.

It probably crept in when I ported this. I can remove that if you want.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22  8:21   ` Gerd Möllmann
@ 2024-10-22  8:57     ` Eli Zaretskii
  2024-10-22  9:42     ` Eli Zaretskii
  1 sibling, 0 replies; 80+ messages in thread
From: Eli Zaretskii @ 2024-10-22  8:57 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: emacs-devel@gnu.org
> Date: Tue, 22 Oct 2024 10:21:43 +0200
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> (defun my-make-child ()
>   (interactive)
>   (make-frame `((parent-frame . ,(selected-frame))
> 		(background-color . "gray10")
> 		(foreground-color . "white")
> 		(internal-border-width . 1)
> 		(top . 15)
> 		(left . 40)
> 		(width . 80)
> 		(height . 25))))
> 
> which is not really the use case I have in mind but anyway.

Thanks.

> > Does this compile cleanly for you?  I get gobs of warnings like this:
> 
> Yes it does, with clang.
> 
> >   dispnew.c: In function ‘gui_update_window_end’:
> >   dispnew.c:4495:34: warning: potential null pointer dereference [-Wnull-dereference]
> >    4495 |       hlinfo->mouse_face_beg_row = hlinfo->mouse_face_beg_col = -1;
> > 	|       ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> >
> > This seems to be because you've changed the definition of
> > MOUSE_HL_INFO to be this:
> >
> >   #   define MOUSE_HL_INFO(F)                                     \
> >     (FRAME_WINDOW_P (F)                                           \
> >     ? (FRAME_OUTPUT_DATA (F)                                      \
> >        ? &FRAME_DISPLAY_INFO (F)->mouse_highlight                 \
> >        : NULL)                                                    \
> >      : &(F)->output_data.tty->display_info->mouse_highlight)
> >
> > I don't understand the need for this NULL there.  What is its purpose,
> > and what will we lose by going back to the original definition?  I
> > believe that NULL is what's causing these warnings.
> 
> It probably crept in when I ported this. I can remove that if you want.

No need, already done.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22  8:21   ` Gerd Möllmann
  2024-10-22  8:57     ` Eli Zaretskii
@ 2024-10-22  9:42     ` Eli Zaretskii
  2024-10-22 10:23       ` Gerd Möllmann
  2024-10-22 10:43       ` Gerd Möllmann
  1 sibling, 2 replies; 80+ messages in thread
From: Eli Zaretskii @ 2024-10-22  9:42 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: emacs-devel@gnu.org
> Date: Tue, 22 Oct 2024 10:21:43 +0200
> 
> Thanks for taking a look.

Can you explain this comment and the code change:

  +         /* Struct frame can move with igc, and so on.  But we need
  +            something that takes different frames into account. Use the
  +            face_cache pointer for that which is malloc'd. */
  +         if (glyph->frame && glyph->frame != f)
  +           face_id += (ptrdiff_t) glyph->frame->face_cache;

Why do we need to take the frame into account here?  Is this relevant
to the current GC used on master?



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22  5:29 ` Eli Zaretskii
@ 2024-10-22  9:58   ` martin rudalics
  2024-10-22 10:20     ` Eli Zaretskii
  2024-10-22 10:40     ` Gerd Möllmann
  2024-10-28  4:35   ` Jared Finder
  1 sibling, 2 replies; 80+ messages in thread
From: martin rudalics @ 2024-10-22  9:58 UTC (permalink / raw)
  To: Eli Zaretskii, Gerd Möllmann, Jared Finder; +Cc: emacs-devel

Building complains here as

In file included from ../../src/term.c:30:
../../src/lisp.h: In function ‘Ftty_display_pixel_height’:
../../src/lisp.h:406:24: warning: ‘height’ may be used uninitialized [-Wmaybe-uninitialized]
   406 |     XIL ((EMACS_INT) (((EMACS_UINT) (n) << INTTYPEBITS) + Lisp_Int0))
       |                        ^
../../src/term.c:4907:14: note: ‘height’ was declared here
  4907 |   int width, height;
       |              ^~~~~~
../../src/lisp.h: In function ‘Ftty_display_pixel_width’:
../../src/lisp.h:406:24: warning: ‘width’ may be used uninitialized [-Wmaybe-uninitialized]
   406 |     XIL ((EMACS_INT) (((EMACS_UINT) (n) << INTTYPEBITS) + Lisp_Int0))
       |                        ^
../../src/term.c:4896:7: note: ‘width’ was declared here
  4896 |   int width, height;
       |       ^~~~~

I cannot test much because I hardly ever use Emacs in a terminal window.
Setting size and position of the child frame work seamlessly.  I can
move the child frame completely out of the parent (but have not tried
what happens when it completely covers the parent) or make it invisible
and visible again.

One thing I noticed is that changing the background color works only
after I changed something like the position or size.  Also I would like
to get rid of those |+- borders.  What would I have to do?

martin

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22  9:58   ` martin rudalics
@ 2024-10-22 10:20     ` Eli Zaretskii
  2024-10-22 14:01       ` martin rudalics
  2024-10-22 10:40     ` Gerd Möllmann
  1 sibling, 1 reply; 80+ messages in thread
From: Eli Zaretskii @ 2024-10-22 10:20 UTC (permalink / raw)
  To: martin rudalics; +Cc: gerd.moellmann, jared, emacs-devel

> Date: Tue, 22 Oct 2024 11:58:59 +0200
> Cc: emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
> 
> Building complains here as
> 
> In file included from ../../src/term.c:30:
> ../../src/lisp.h: In function ‘Ftty_display_pixel_height’:
> ../../src/lisp.h:406:24: warning: ‘height’ may be used uninitialized [-Wmaybe-uninitialized]
>    406 |     XIL ((EMACS_INT) (((EMACS_UINT) (n) << INTTYPEBITS) + Lisp_Int0))
>        |                        ^
> ../../src/term.c:4907:14: note: ‘height’ was declared here
>   4907 |   int width, height;
>        |              ^~~~~~
> ../../src/lisp.h: In function ‘Ftty_display_pixel_width’:
> ../../src/lisp.h:406:24: warning: ‘width’ may be used uninitialized [-Wmaybe-uninitialized]
>    406 |     XIL ((EMACS_INT) (((EMACS_UINT) (n) << INTTYPEBITS) + Lisp_Int0))
>        |                        ^
> ../../src/term.c:4896:7: note: ‘width’ was declared here
>   4896 |   int width, height;
>        |       ^~~~~

I attempted to fix this, please see if the warnings are gone.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22  9:42     ` Eli Zaretskii
@ 2024-10-22 10:23       ` Gerd Möllmann
  2024-10-22 13:35         ` Eli Zaretskii
  2024-10-22 10:43       ` Gerd Möllmann
  1 sibling, 1 reply; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-22 10:23 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Gerd Möllmann <gerd.moellmann@gmail.com>
>> Cc: emacs-devel@gnu.org
>> Date: Tue, 22 Oct 2024 10:21:43 +0200
>> 
>> Thanks for taking a look.
>
> Can you explain this comment and the code change:
>
>   +         /* Struct frame can move with igc, and so on.  But we need
>   +            something that takes different frames into account. Use the
>   +            face_cache pointer for that which is malloc'd. */
>   +         if (glyph->frame && glyph->frame != f)
>   +           face_id += (ptrdiff_t) glyph->frame->face_cache;
>
> Why do we need to take the frame into account here?  Is this relevant
> to the current GC used on master?

I can try.

A combined frame matrix's glyph contents may come from different frames,
either the root frame one of its descendants (children, grandchildren
and so on). See copy_child_glyphs. glyph->frame is th eframe from where
the glyph stems. It is filled out when producing glyphs.

Face ids are valid only in frame's face cache. A Face with a given id in
one frame may be different from the same id another frame. So that's why
I'm taking the face cache into account here. Otherwise it could happen
that row hashes are the same although the contents are different. Not a
big deal, but one can avoid it, so I did.






^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22  9:58   ` martin rudalics
  2024-10-22 10:20     ` Eli Zaretskii
@ 2024-10-22 10:40     ` Gerd Möllmann
  2024-10-22 11:43       ` Po Lu
                         ` (2 more replies)
  1 sibling, 3 replies; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-22 10:40 UTC (permalink / raw)
  To: martin rudalics; +Cc: Eli Zaretskii, Jared Finder, emacs-devel

martin rudalics <rudalics@gmx.at> writes:

> Building complains here as
>
> In file included from ../../src/term.c:30:
> ../../src/lisp.h: In function ‘Ftty_display_pixel_height’:
> ../../src/lisp.h:406:24: warning: ‘height’ may be used uninitialized [-Wmaybe-uninitialized]
>   406 |     XIL ((EMACS_INT) (((EMACS_UINT) (n) << INTTYPEBITS) + Lisp_Int0))
>       |                        ^
> ../../src/term.c:4907:14: note: ‘height’ was declared here
>  4907 |   int width, height;
>       |              ^~~~~~
> ../../src/lisp.h: In function ‘Ftty_display_pixel_width’:
> ../../src/lisp.h:406:24: warning: ‘width’ may be used uninitialized [-Wmaybe-uninitialized]
>   406 |     XIL ((EMACS_INT) (((EMACS_UINT) (n) << INTTYPEBITS) + Lisp_Int0))
>       |                        ^
> ../../src/term.c:4896:7: note: ‘width’ was declared here
>  4896 |   int width, height;
>       |       ^~~~~

That's code like this:

{
  int width, height;
  tty_display_dimension (display, &width, &height);
  return make_fixnum (height);
}

So width and height are returned by the call to tty_display_dimension.
TBH, I can't make sense of the warning. FWIW, clang doesn't complain.
Don't know what's the usual way is to placate GCC here. What version is
that BTW?

> I cannot test much because I hardly ever use Emacs in a terminal window.
> Setting size and position of the child frame work seamlessly.  I can
> move the child frame completely out of the parent (but have not tried
> what happens when it completely covers the parent) or make it invisible
> and visible again.

👍

> One thing I noticed is that changing the background color works only
> after I changed something like the position or size.  

Probably a SET_FRAME_GARBAGED missing somewhere, or something like that.

> Also I would like to get rid of those |+- borders. What would I have
> to do?

If you want to get rid of the border completely add a frame parameter
(undecorated . t), Default if no no undecorated is specified, it to not
draw borders.

If you want Unicode chars instead, you could call
standard-display-unicode-special-glyphs.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22  9:42     ` Eli Zaretskii
  2024-10-22 10:23       ` Gerd Möllmann
@ 2024-10-22 10:43       ` Gerd Möllmann
  1 sibling, 0 replies; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-22 10:43 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Gerd Möllmann <gerd.moellmann@gmail.com>
>> Cc: emacs-devel@gnu.org
>> Date: Tue, 22 Oct 2024 10:21:43 +0200
>> 
>> Thanks for taking a look.
>
> Can you explain this comment and the code change:
>
>   +         /* Struct frame can move with igc, and so on.  But we need
>   +            something that takes different frames into account. Use the
>   +            face_cache pointer for that which is malloc'd. */
>   +         if (glyph->frame && glyph->frame != f)
>   +           face_id += (ptrdiff_t) glyph->frame->face_cache;
>
> Why do we need to take the frame into account here?  Is this relevant
> to the current GC used on master?

Forgot to answer tha GC part: It's not relevant to non-MPS GC. (I'm
using igc here, normally.)



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22 10:40     ` Gerd Möllmann
@ 2024-10-22 11:43       ` Po Lu
  2024-10-22 13:44       ` Eli Zaretskii
  2024-10-22 14:02       ` martin rudalics
  2 siblings, 0 replies; 80+ messages in thread
From: Po Lu @ 2024-10-22 11:43 UTC (permalink / raw)
  To: Gerd Möllmann
  Cc: martin rudalics, Eli Zaretskii, Jared Finder, emacs-devel

Gerd Möllmann <gerd.moellmann@gmail.com> writes:

> martin rudalics <rudalics@gmx.at> writes:
>
>> Building complains here as
>>
>> In file included from ../../src/term.c:30:
>> ../../src/lisp.h: In function ‘Ftty_display_pixel_height’:
>> ../../src/lisp.h:406:24: warning: ‘height’ may be used uninitialized [-Wmaybe-uninitialized]
>>   406 |     XIL ((EMACS_INT) (((EMACS_UINT) (n) << INTTYPEBITS) + Lisp_Int0))
>>       |                        ^
>> ../../src/term.c:4907:14: note: ‘height’ was declared here
>>  4907 |   int width, height;
>>       |              ^~~~~~
>> ../../src/lisp.h: In function ‘Ftty_display_pixel_width’:
>> ../../src/lisp.h:406:24: warning: ‘width’ may be used uninitialized [-Wmaybe-uninitialized]
>>   406 |     XIL ((EMACS_INT) (((EMACS_UINT) (n) << INTTYPEBITS) + Lisp_Int0))
>>       |                        ^
>> ../../src/term.c:4896:7: note: ‘width’ was declared here
>>  4896 |   int width, height;
>>       |       ^~~~~
>
> That's code like this:
>
> {
>   int width, height;
>   tty_display_dimension (display, &width, &height);
>   return make_fixnum (height);
> }
>
> So width and height are returned by the call to tty_display_dimension.
> TBH, I can't make sense of the warning. FWIW, clang doesn't complain.
> Don't know what's the usual way is to placate GCC here. What version is
> that BTW?

Insert UNINIT after the declaration of the variable affected by these
warnings.  E.g.,

  int height UNINIT ...



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22 10:23       ` Gerd Möllmann
@ 2024-10-22 13:35         ` Eli Zaretskii
  2024-10-22 13:43           ` Gerd Möllmann
  0 siblings, 1 reply; 80+ messages in thread
From: Eli Zaretskii @ 2024-10-22 13:35 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: emacs-devel@gnu.org
> Date: Tue, 22 Oct 2024 12:23:04 +0200
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> >> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> >> Cc: emacs-devel@gnu.org
> >> Date: Tue, 22 Oct 2024 10:21:43 +0200
> >> 
> >> Thanks for taking a look.
> >
> > Can you explain this comment and the code change:
> >
> >   +         /* Struct frame can move with igc, and so on.  But we need
> >   +            something that takes different frames into account. Use the
> >   +            face_cache pointer for that which is malloc'd. */
> >   +         if (glyph->frame && glyph->frame != f)
> >   +           face_id += (ptrdiff_t) glyph->frame->face_cache;
> >
> > Why do we need to take the frame into account here?  Is this relevant
> > to the current GC used on master?
> 
> I can try.
> 
> A combined frame matrix's glyph contents may come from different frames,
> either the root frame one of its descendants (children, grandchildren
> and so on). See copy_child_glyphs. glyph->frame is th eframe from where
> the glyph stems. It is filled out when producing glyphs.
> 
> Face ids are valid only in frame's face cache. A Face with a given id in
> one frame may be different from the same id another frame. So that's why
> I'm taking the face cache into account here. Otherwise it could happen
> that row hashes are the same although the contents are different. Not a
> big deal, but one can avoid it, so I did.

Why not simply include the frame pointer in the hash?  Adding a
pointer to a number looks kludgey, and might even make the hash
weaker.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22 13:35         ` Eli Zaretskii
@ 2024-10-22 13:43           ` Gerd Möllmann
  2024-10-22 13:55             ` Eli Zaretskii
  0 siblings, 1 reply; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-22 13:43 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Gerd Möllmann <gerd.moellmann@gmail.com>
>> Cc: emacs-devel@gnu.org
>> Date: Tue, 22 Oct 2024 12:23:04 +0200
>> 
>> Eli Zaretskii <eliz@gnu.org> writes:
>> 
>> >> From: Gerd Möllmann <gerd.moellmann@gmail.com>
>> >> Cc: emacs-devel@gnu.org
>> >> Date: Tue, 22 Oct 2024 10:21:43 +0200
>> >> 
>> >> Thanks for taking a look.
>> >
>> > Can you explain this comment and the code change:
>> >
>> >   +         /* Struct frame can move with igc, and so on.  But we need
>> >   +            something that takes different frames into account. Use the
>> >   +            face_cache pointer for that which is malloc'd. */
>> >   +         if (glyph->frame && glyph->frame != f)
>> >   +           face_id += (ptrdiff_t) glyph->frame->face_cache;
>> >
>> > Why do we need to take the frame into account here?  Is this relevant
>> > to the current GC used on master?
>> 
>> I can try.
>> 
>> A combined frame matrix's glyph contents may come from different frames,
>> either the root frame one of its descendants (children, grandchildren
>> and so on). See copy_child_glyphs. glyph->frame is th eframe from where
>> the glyph stems. It is filled out when producing glyphs.
>> 
>> Face ids are valid only in frame's face cache. A Face with a given id in
>> one frame may be different from the same id another frame. So that's why
>> I'm taking the face cache into account here. Otherwise it could happen
>> that row hashes are the same although the contents are different. Not a
>> big deal, but one can avoid it, so I did.
>
> Why not simply include the frame pointer in the hash?  

With igc, frames move in memory, face caches are malloc'd.

> Adding a pointer to a number looks kludgey, and might even make the
> hash weaker.

If you have something better, please tell. 



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22 10:40     ` Gerd Möllmann
  2024-10-22 11:43       ` Po Lu
@ 2024-10-22 13:44       ` Eli Zaretskii
  2024-10-22 14:01         ` Gerd Möllmann
  2024-10-22 14:02       ` martin rudalics
  2 siblings, 1 reply; 80+ messages in thread
From: Eli Zaretskii @ 2024-10-22 13:44 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: rudalics, jared, emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: Eli Zaretskii <eliz@gnu.org>,  Jared Finder <jared@finder.org>,
>   emacs-devel@gnu.org
> Date: Tue, 22 Oct 2024 12:40:21 +0200
> 
> martin rudalics <rudalics@gmx.at> writes:
> 
> > Building complains here as
> >
> > In file included from ../../src/term.c:30:
> > ../../src/lisp.h: In function ‘Ftty_display_pixel_height’:
> > ../../src/lisp.h:406:24: warning: ‘height’ may be used uninitialized [-Wmaybe-uninitialized]
> >   406 |     XIL ((EMACS_INT) (((EMACS_UINT) (n) << INTTYPEBITS) + Lisp_Int0))
> >       |                        ^
> > ../../src/term.c:4907:14: note: ‘height’ was declared here
> >  4907 |   int width, height;
> >       |              ^~~~~~
> > ../../src/lisp.h: In function ‘Ftty_display_pixel_width’:
> > ../../src/lisp.h:406:24: warning: ‘width’ may be used uninitialized [-Wmaybe-uninitialized]
> >   406 |     XIL ((EMACS_INT) (((EMACS_UINT) (n) << INTTYPEBITS) + Lisp_Int0))
> >       |                        ^
> > ../../src/term.c:4896:7: note: ‘width’ was declared here
> >  4896 |   int width, height;
> >       |       ^~~~~
> 
> That's code like this:
> 
> {
>   int width, height;
>   tty_display_dimension (display, &width, &height);
>   return make_fixnum (height);
> }
> 
> So width and height are returned by the call to tty_display_dimension.
> TBH, I can't make sense of the warning.

I could: tty_display_dimension includes a switch without 'default',
and thus might not set width and height to any value.  I installed
what should be a fix for that, and I hope Martin will confirm that the
warning is now gone.

> FWIW, clang doesn't complain.

It probably doesn't analyze the code so thoroughly.

> > One thing I noticed is that changing the background color works only
> > after I changed something like the position or size.  
> 
> Probably a SET_FRAME_GARBAGED missing somewhere, or something like that.

If someone gives the recipe for this, I could look into it.

> > Also I would like to get rid of those |+- borders. What would I have
> > to do?
> 
> If you want to get rid of the border completely add a frame parameter
> (undecorated . t), Default if no no undecorated is specified, it to not
> draw borders.

You mean, if undecorated is not specified, the default is to _draw_
the borders, yes?  Because the examples I used do not specify
undecorated, and the borders were drawn.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22 13:43           ` Gerd Möllmann
@ 2024-10-22 13:55             ` Eli Zaretskii
  2024-10-22 14:02               ` Gerd Möllmann
  0 siblings, 1 reply; 80+ messages in thread
From: Eli Zaretskii @ 2024-10-22 13:55 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: emacs-devel@gnu.org
> Date: Tue, 22 Oct 2024 15:43:29 +0200
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > Adding a pointer to a number looks kludgey, and might even make the
> > hash weaker.
> 
> If you have something better, please tell. 

Give each frame a number, and use that in the hash.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22 13:44       ` Eli Zaretskii
@ 2024-10-22 14:01         ` Gerd Möllmann
  0 siblings, 0 replies; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-22 14:01 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: rudalics, jared, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Gerd Möllmann <gerd.moellmann@gmail.com>
>> Cc: Eli Zaretskii <eliz@gnu.org>,  Jared Finder <jared@finder.org>,
>>   emacs-devel@gnu.org
>> Date: Tue, 22 Oct 2024 12:40:21 +0200
>> 
>> martin rudalics <rudalics@gmx.at> writes:
>> 
>> > Building complains here as
>> >
>> > In file included from ../../src/term.c:30:
>> > ../../src/lisp.h: In function ‘Ftty_display_pixel_height’:
>> > ../../src/lisp.h:406:24: warning: ‘height’ may be used uninitialized [-Wmaybe-uninitialized]
>> >   406 |     XIL ((EMACS_INT) (((EMACS_UINT) (n) << INTTYPEBITS) + Lisp_Int0))
>> >       |                        ^
>> > ../../src/term.c:4907:14: note: ‘height’ was declared here
>> >  4907 |   int width, height;
>> >       |              ^~~~~~
>> > ../../src/lisp.h: In function ‘Ftty_display_pixel_width’:
>> > ../../src/lisp.h:406:24: warning: ‘width’ may be used uninitialized [-Wmaybe-uninitialized]
>> >   406 |     XIL ((EMACS_INT) (((EMACS_UINT) (n) << INTTYPEBITS) + Lisp_Int0))
>> >       |                        ^
>> > ../../src/term.c:4896:7: note: ‘width’ was declared here
>> >  4896 |   int width, height;
>> >       |       ^~~~~
>> 
>> That's code like this:
>> 
>> {
>>   int width, height;
>>   tty_display_dimension (display, &width, &height);
>>   return make_fixnum (height);
>> }
>> 
>> So width and height are returned by the call to tty_display_dimension.
>> TBH, I can't make sense of the warning.
>
> I could: tty_display_dimension includes a switch without 'default',
> and thus might not set width and height to any value.  I installed
> what should be a fix for that, and I hope Martin will confirm that the
> warning is now gone.

Ok, thanks. I thought that was an exhaustive switch, but apparently not.

>
>> FWIW, clang doesn't complain.
>
> It probably doesn't analyze the code so thoroughly.
>
>> > One thing I noticed is that changing the background color works only
>> > after I changed something like the position or size.  
>> 
>> Probably a SET_FRAME_GARBAGED missing somewhere, or something like that.
>
> If someone gives the recipe for this, I could look into it.
>
>> > Also I would like to get rid of those |+- borders. What would I have
>> > to do?
>> 
>> If you want to get rid of the border completely add a frame parameter
>> (undecorated . t), Default if no no undecorated is specified, it to not
>> draw borders.
>
> You mean, if undecorated is not specified, the default is to _draw_
> the borders, yes?  Because the examples I used do not specify
> undecorated, and the borders were drawn.

It's this, so if it's not undecoreated.

  if (!FRAME_UNDECORATED (child))
    {
      /* Horizontal line above.  */

The double negation always gets me confused :-).



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22 10:20     ` Eli Zaretskii
@ 2024-10-22 14:01       ` martin rudalics
  2024-10-22 14:23         ` Eli Zaretskii
  0 siblings, 1 reply; 80+ messages in thread
From: martin rudalics @ 2024-10-22 14:01 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gerd.moellmann, jared, emacs-devel

 > I attempted to fix this, please see if the warnings are gone.

They are gone.

Thanks, martin



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22 10:40     ` Gerd Möllmann
  2024-10-22 11:43       ` Po Lu
  2024-10-22 13:44       ` Eli Zaretskii
@ 2024-10-22 14:02       ` martin rudalics
  2 siblings, 0 replies; 80+ messages in thread
From: martin rudalics @ 2024-10-22 14:02 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Eli Zaretskii, Jared Finder, emacs-devel

 > Don't know what's the usual way is to placate GCC here. What version is
 > that BTW?

12.2.0

 >> Also I would like to get rid of those |+- borders. What would I have
 >> to do?
 >
 > If you want to get rid of the border completely add a frame parameter
 > (undecorated . t), Default if no no undecorated is specified, it to not
 > draw borders.

Thanks.  I'm not quite sure whether these should not be called the
internal borders.  Decorations are more - on GUIs they include the title
bar.

Two further things I noticed: When point in the parent frame is
effectively hidden by the child frame, its cursor sometimes appears at
the right of the child frame and sometimes it's not shown.  I have not
understood the underlying principle for this behavior.  Also when the
selected region in the parent frame is active, its overlay covers the
child frame.  That's ugly.

martin



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22 13:55             ` Eli Zaretskii
@ 2024-10-22 14:02               ` Gerd Möllmann
  2024-10-22 14:40                 ` Eli Zaretskii
  0 siblings, 1 reply; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-22 14:02 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Gerd Möllmann <gerd.moellmann@gmail.com>
>> Cc: emacs-devel@gnu.org
>> Date: Tue, 22 Oct 2024 15:43:29 +0200
>> 
>> Eli Zaretskii <eliz@gnu.org> writes:
>> 
>> > Adding a pointer to a number looks kludgey, and might even make the
>> > hash weaker.
>> 
>> If you have something better, please tell. 
>
> Give each frame a number, and use that in the hash.

Thanks, that would work of course. Don't know if it's worth the effort.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22 14:01       ` martin rudalics
@ 2024-10-22 14:23         ` Eli Zaretskii
  0 siblings, 0 replies; 80+ messages in thread
From: Eli Zaretskii @ 2024-10-22 14:23 UTC (permalink / raw)
  To: martin rudalics; +Cc: gerd.moellmann, jared, emacs-devel

> Date: Tue, 22 Oct 2024 16:01:14 +0200
> Cc: gerd.moellmann@gmail.com, jared@finder.org, emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
> 
>  > I attempted to fix this, please see if the warnings are gone.
> 
> They are gone.

Thanks.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22 14:02               ` Gerd Möllmann
@ 2024-10-22 14:40                 ` Eli Zaretskii
  2024-10-22 19:19                   ` Paul Eggert
  0 siblings, 1 reply; 80+ messages in thread
From: Eli Zaretskii @ 2024-10-22 14:40 UTC (permalink / raw)
  To: Gerd Möllmann, Paul Eggert; +Cc: emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: emacs-devel@gnu.org
> Date: Tue, 22 Oct 2024 16:02:39 +0200
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> >> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> >> Cc: emacs-devel@gnu.org
> >> Date: Tue, 22 Oct 2024 15:43:29 +0200
> >> 
> >> Eli Zaretskii <eliz@gnu.org> writes:
> >> 
> >> > Adding a pointer to a number looks kludgey, and might even make the
> >> > hash weaker.
> >> 
> >> If you have something better, please tell. 
> >
> > Give each frame a number, and use that in the hash.
> 
> Thanks, that would work of course. Don't know if it's worth the effort.

Btw, the current code has a problem here:

          int face_id = glyph->face_id;
	  [...]
          if (glyph->frame && glyph->frame != f)
            face_id += (ptrdiff_t) glyph->frame->face_cache;
	  [...]
          hash = (((hash << 4) + (hash >> 24)) & 0x0fffffff) + c;
          hash = (((hash << 4) + (hash >> 24)) & 0x0fffffff) + face_id;

face_id is an 'int', but 'ptrdiff_t' is a 64-bit data type in 64-bit
builds.  So the addition of face_cache pointer could overflow, which
AFAIK is UB with signed data types.  Moreover, 'hash' is declared
'unsigned int', and so is the data type returned by line_hash_code,
which only makes the problem worse.

Paul, should we make line_hash_code return 'size_t' instead (and
change the data type of the variables in 'scrolling' accordingly)?  Or
maybe we should simply mask off high bits of the face_cache's pointer,
leaving only the low 32 bits, before adding it to the hash?



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22 14:40                 ` Eli Zaretskii
@ 2024-10-22 19:19                   ` Paul Eggert
  2024-10-23  3:18                     ` Gerd Möllmann
  0 siblings, 1 reply; 80+ messages in thread
From: Paul Eggert @ 2024-10-22 19:19 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel, Gerd Möllmann

[-- Attachment #1: Type: text/plain, Size: 737 bytes --]

On 2024-10-22 07:40, Eli Zaretskii wrote:
> Paul, should we make line_hash_code return 'size_t' instead (and
> change the data type of the variables in 'scrolling' accordingly)?  Or
> maybe we should simply mask off high bits of the face_cache's pointer,
> leaving only the low 32 bits, before adding it to the hash?

Either approach should be correct. The attached (untested and 
uninstalled) patch does the equivalent of the latter, which is simpler. 
I don't know whether the more-complicated one would perform better.

This patch also fixes a glitch in that one should cast pointers to 
uintptr_t or to intptr_t, not to ptrdiff_t. The difference can matter on 
unusual platforms like CheriBSD where uintptr_t is wider than ptrdiff_t.

[-- Attachment #2: 0001-Fix-UB-in-line_hash_code.patch --]
[-- Type: text/x-patch, Size: 1157 bytes --]

From f624661eec854f09b28076b8f1bf80b1ed326475 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Tue, 22 Oct 2024 12:18:14 -0700
Subject: [PATCH] Fix UB in line_hash_code

* src/dispnew.c (line_hash_code): Avoid undefined behavior on
integer overflow.
---
 src/dispnew.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/dispnew.c b/src/dispnew.c
index 200ffaaca21..1ece9cc1d45 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -1175,12 +1175,12 @@ line_hash_code (struct frame *f, struct glyph_row *row)
       while (glyph < end)
 	{
 	  int c = glyph->u.ch;
-	  int face_id = glyph->face_id;
+	  unsigned int face_id = glyph->face_id;
 	  /* Struct frame can move with igc, and so on.  But we need
 	     something that takes different frames into account.  Use the
 	     face_cache pointer for that which is malloc'd.  */
 	  if (glyph->frame && glyph->frame != f)
-	    face_id += (ptrdiff_t) glyph->frame->face_cache;
+	    face_id += (uintptr_t) glyph->frame->face_cache;
 	  if (FRAME_MUST_WRITE_SPACES (f))
 	    c -= SPACEGLYPH;
 	  hash = (((hash << 4) + (hash >> 24)) & 0x0fffffff) + c;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22  4:46 "Final" version of tty child frames Gerd Möllmann
                   ` (2 preceding siblings ...)
  2024-10-22  8:01 ` Eli Zaretskii
@ 2024-10-23  3:05 ` Feng Shu
  2024-10-23  3:13   ` Gerd Möllmann
  2024-10-23  7:11   ` Eli Zaretskii
  2024-10-26  8:15 ` Gerd Möllmann
  4 siblings, 2 replies; 80+ messages in thread
From: Feng Shu @ 2024-10-23  3:05 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Emacs Devel

Gerd Möllmann <gerd.moellmann@gmail.com> writes:

Will the below interfaces changed?

1. tty-child-frames
2. tty-non-selected-cursor
3. undecorated

If not, I have updated posframe, vertico-posframe and ivy-posframe to
support three interfaces.


> I have (re-)created the scratch/tty-child-frames branch today, which
> contains the code for child frames on ttys, based on a recent master.
>
> I'm a happy user of this for a while now with corfu, vertico +
> vertico-posframe + consult, transient + transient-posframe,
> which-key + which-key-posframe. And my current todo list is now empty,
> so here it is.
>
> To use it, redefine these two functions:
>
> #+begin_src emacs-lisp
> (defun posframe-workable-p ()
>   "Test posframe workable status."
>   (and (>= emacs-major-version 26)
>        (not (or noninteractive
>                 emacs-basic-display
>                 (or (featurep 'tty-child-frames)
>                     (not (display-graphic-p)))
>                 (eq (frame-parameter (selected-frame) 'minibuffer) 'only)))))))
>
> (cl-defgeneric corfu--popup-support-p ()
>   "Return non-nil if child frames are supported."
>   (or (display-graphic-p)
>       (featurep 'tty-child-frames)))
> #+end_src
>
>
> To make things look nicer you might also want to
>
> #+begin_src emacs-lisp
> (push '(tty-non-selected-cursor . t) vertico-posframe-parameters)
> (push '(undecorated . nil) vertico-posframe-parameters))
> (push '(undecorated . nil) transient-posframe-parameters))
> #+end_src
>
> The 'undecorated' frame parameter lets Emacs draw a border around the
> child frame (default is no border). The tty-non-selected-cursor
> parameter makes redisplay put the terminal cursor in a non-selected
> frame which I find nice for things like consult-buffer. Which is why I
> added it :-).
>
> What's there fits my personal needs entirely. I am not interested in
> full support of child frames as they exist on window systems because I
> don't see child frames being used except for things like posframe and
> corfu and similar. And, TBH, I don't find the tty frame code fun to
> work with.
>
> Other things not contained:
>
> - Support for anything but termcap frames. I bet I broke MSDOS.
> - Mouse support except with xterm-mouse-mode (no GPM).
> - Drawing borders like it is normally done (internal border and so
>   on). Tried that once, git reset --hard, won't happen.
> - Tooltips. I think tooltips could relatively easily be implemented
>   with child frames by copying or extracting the non-native tooltip
>   code from x-show-tip or some such. From my POV, that would be best
>   done by refactoring the tooltip code across window systems which I
>   can't do.
> - Documentation. Would only make sense to write if this goes into GNU,
>   which might or might not happen, depending on, from my side, how
>   much work that is.
>
> Disclaimer: As I mentioned already in other contexts, I don't want to
> be the maintainer of anything, for personal reasons.
>
> Have fun!
>
>

-- 




^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-23  3:05 ` Feng Shu
@ 2024-10-23  3:13   ` Gerd Möllmann
  2024-10-23  3:25     ` Feng Shu
  2024-10-23  7:11   ` Eli Zaretskii
  1 sibling, 1 reply; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-23  3:13 UTC (permalink / raw)
  To: Feng Shu; +Cc: Emacs Devel

Feng Shu <tumashu@163.com> writes:

> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
>
> Will the below interfaces changed?
>
> 1. tty-child-frames
> 2. tty-non-selected-cursor
> 3. undecorated

I don't plan to change anything.

>
> If not, I have updated posframe, vertico-posframe and ivy-posframe to
> support three interfaces.

Very nice, thank you!

I also got a message from github that Daniel added support to Corfu.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22 19:19                   ` Paul Eggert
@ 2024-10-23  3:18                     ` Gerd Möllmann
  0 siblings, 0 replies; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-23  3:18 UTC (permalink / raw)
  To: Paul Eggert; +Cc: Eli Zaretskii, emacs-devel

Paul Eggert <eggert@cs.ucla.edu> writes:

> On 2024-10-22 07:40, Eli Zaretskii wrote:
>> Paul, should we make line_hash_code return 'size_t' instead (and
>> change the data type of the variables in 'scrolling' accordingly)?  Or
>> maybe we should simply mask off high bits of the face_cache's pointer,
>> leaving only the low 32 bits, before adding it to the hash?
>
> Either approach should be correct. The attached (untested and
> uninstalled) patch does the equivalent of the latter, which is
> simpler. I don't know whether the more-complicated one would perform
> better.
>
> This patch also fixes a glitch in that one should cast pointers to
> uintptr_t or to intptr_t, not to ptrdiff_t. The difference can matter
> on unusual platforms like CheriBSD where uintptr_t is wider than
> ptrdiff_t.

Thanks Paul, I've pushed that to the branch.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-23  3:13   ` Gerd Möllmann
@ 2024-10-23  3:25     ` Feng Shu
  2024-10-23  3:36       ` Gerd Möllmann
  0 siblings, 1 reply; 80+ messages in thread
From: Feng Shu @ 2024-10-23  3:25 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Emacs Devel

[-- Attachment #1: Type: text/plain, Size: 365 bytes --]

Gerd Möllmann <gerd.moellmann@gmail.com> writes:

> Feng Shu <tumashu@163.com> writes:
>
>> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
>>
>> Will the below interfaces changed?
>>
>> 1. tty-child-frames
>> 2. tty-non-selected-cursor
>> 3. undecorated

border do not work well when current buffer is CJK, maybe because CJK Char width is
not 1



[-- Attachment #2: 2024-10-23_11-22.png --]
[-- Type: image/png, Size: 108954 bytes --]

[-- Attachment #3: Type: text/plain, Size: 251 bytes --]




>
> I don't plan to change anything.
>
>>
>> If not, I have updated posframe, vertico-posframe and ivy-posframe to
>> support three interfaces.
>
> Very nice, thank you!
>
> I also got a message from github that Daniel added support to Corfu.

-- 

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-23  3:25     ` Feng Shu
@ 2024-10-23  3:36       ` Gerd Möllmann
  2024-10-23  3:44         ` Feng Shu
  0 siblings, 1 reply; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-23  3:36 UTC (permalink / raw)
  To: Feng Shu; +Cc: Emacs Devel

Feng Shu <tumashu@163.com> writes:

> border do not work well when current buffer is CJK, maybe because CJK
> Char width is not 1

Could you please send me a bit of CJK text so that I have something I
can test this with?



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-23  3:36       ` Gerd Möllmann
@ 2024-10-23  3:44         ` Feng Shu
  2024-10-23  4:09           ` Gerd Möllmann
  0 siblings, 1 reply; 80+ messages in thread
From: Feng Shu @ 2024-10-23  3:44 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Emacs Devel

[-- Attachment #1: Type: text/plain, Size: 292 bytes --]

Gerd Möllmann <gerd.moellmann@gmail.com> writes:

> Feng Shu <tumashu@163.com> writes:
>
>> border do not work well when current buffer is CJK, maybe because CJK
>> Char width is not 1
>
> Could you please send me a bit of CJK text so that I have something I
> can test this with?


[-- Attachment #2: 1.org --]
[-- Type: text/plain, Size: 4183 bytes --]

* 《红楼梦》的版本

曹雪芹的《红楼梦》亲笔原稿肯定早就找不到了,是不是因为原稿字迹太潦草,让人抄齐了以后他就不想再留,烧了呢?因此现存所有的本子都是抄的,而且还都不是直接从曹雪芹原稿手迹抄,是二传手,是抄本的抄本,甚至是三传手,四传手。大家抄来抄去,很多抄手还特别有才,自由发挥添加删改了不少地方。这些不同的版本有很多,所以自然就产生了这样一个问题:哪一个版本是最接近曹雪芹原著的?看《红楼梦》应该看哪一种最好?所以有必要把这些版本都列一下:
* 带脂评的手抄本,共11个版本:


书名《脂砚斋重评石头记》,胡适在1927年发现的,现藏于美国康奈尔大学。只有1—8,13—16,25—28,共16回,4册,4回一册,有一千多条脂批。第一回有其它各本没有的一句话:“至脂砚斋甲戌抄阅再评仍用石头记”所以叫甲戌本。只有此本有“凡例”,后两本把凡例融合在了正文里。甲戌是1754年,乾隆19年,红楼梦刚写完,曹雪芹39岁。
* 这是第一个基本成熟的本子,虽然残缺很多,但原本肯定是完整的,胡适的这个甲戌本当然不是1754年的原抄本,是谁在什么时候抄的已经无法考证,应该晚于程本的1791年。1754年之前的本子一般认为是不成熟本,缺失太多,分散出现在庚辰本之后的本子里,包括程本。

2. 己卯本:


3. 庚辰本:

书名《脂砚斋重评石头记》,是在己卯本基础上修改后的最后定本,因此是所有版本中最重要的本子,可能最接近曹雪芹原笔的。1933年由徐星署所得,现存北大图书馆。此本只缺64,67回,当时抄的时候底本就少了这两回。因后四册目录页有“庚辰秋月定本”,所以叫庚辰本。庚辰是1760年,曹雪芹45岁。现存的这个本子当然不是1760年抄录的,但具体什么时间,什么人抄的已经没法考证了。
* 只有甲戌己卯庚辰这老三篇来源单一,血统比较纯正,此后的版本大都是各种本子的混合,来源混杂。曹雪芹原稿的书名显然是《石头记》而不是《红楼梦》。

4. 梦稿本:

书名《乾隆抄本百廿回红楼梦稿》,杨继振藏本,杨藏本,现存国博。大部分抄自甲戌己卯庚辰。其中67回是唯一来源于当时没有缺失的老三篇,是孤品,极为珍贵。来源于甲戌本之前不成熟本的是:25,26,27,28,35,36,37,39,75这9回。因未成熟本大部丢失,所以这9回有很高价值。

5. 戚序本:
* 戚蓼生,浙江湖州德清县人,清代乾隆年间的进士,于乾隆三十五年(1770年)至乾隆四十五年(1780年)在京做官。这段时间他整理出了抄录有脂批的《石头记》,并为之作序,只有前40回,有脂评,是老三篇的混合版。

6. 蒙府本:
* 书名《石头记》,1960年发现于清代一蒙古王府。现存国家图书馆,专用抄纸原抄部分74回,他人白纸抄补成120回。主要源自戚序本。
(def)
7. 列藏本:又称脂亚本,无书前题页。因藏于原苏联亚洲人民研究所列宁格勒分所,故名列藏本,是1962年李福清发现的。现存俄罗斯彼得堡东方研究所。存78回,缺5,6回。没有总书名。除少数几回名红楼梦外,各回皆名石头记。大部分抄自老三篇,有少量来源于未成熟本。

8. 舒序本:
* 书名《舒元炜序本红楼梦》,己酉本,存1-40回,抄于1798年,乾隆54年,是唯一有明确抄录时间的本子,所以有特殊价值,由吴晓铃收藏,除此以外,其他抄本都没有明确的抄录时间。

9. 卞藏本:
* 2006年发现,是早于甲戌本的不成熟本,很珍贵。有1-10回,和33-80回目录。

10. 甲辰本:
* 梦觉主人序本,1784年抄本,少量抄自老三篇,大部分源自程本,价值不大。1953年在山西发现,现存国家图书馆。

郑藏本:
* 
* 
* 

* 郑振铎藏本,只有

[-- Attachment #3: Type: text/plain, Size: 5 bytes --]


-- 

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-23  3:44         ` Feng Shu
@ 2024-10-23  4:09           ` Gerd Möllmann
  2024-10-23  4:40             ` Gerd Möllmann
  0 siblings, 1 reply; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-23  4:09 UTC (permalink / raw)
  To: Feng Shu; +Cc: Emacs Devel

Feng Shu <tumashu@163.com> writes:

> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
>
>> Feng Shu <tumashu@163.com> writes:
>>
>>> border do not work well when current buffer is CJK, maybe because CJK
>>> Char width is not 1
>>
>> Could you please send me a bit of CJK text so that I have something I
>> can test this with?

Thanks!



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-23  4:09           ` Gerd Möllmann
@ 2024-10-23  4:40             ` Gerd Möllmann
  2024-10-23  5:00               ` Gerd Möllmann
                                 ` (2 more replies)
  0 siblings, 3 replies; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-23  4:40 UTC (permalink / raw)
  To: Feng Shu; +Cc: Emacs Devel

Gerd Möllmann <gerd.moellmann@gmail.com> writes:

> Feng Shu <tumashu@163.com> writes:
>
>> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
>>
>>> Feng Shu <tumashu@163.com> writes:
>>>
>>>> border do not work well when current buffer is CJK, maybe because CJK
>>>> Char width is not 1
>>>
>>> Could you please send me a bit of CJK text so that I have something I
>>> can test this with?
>
> Thanks!

Hm, okay, but not sure how to fix that.

The problem comes indeed from the root frame displaying characters that
are more then 1 column wide, say N. When we output such a character to
the terminal, the cursor moves by N columns. Emacs represents that in
the glyph matrix by producing N glyphs for the character, all bu tthe
first have a padding flag set. So we have

  ... (C, 0) (C, 1) ...

in the glpyh matrix, where C is the character, and the number is the
padding flag.

Now assume, for example, that the child frame is posititoned so that its
left border where we want to display a '|' is in the column for the (C,
1).

That can't work, because writing C to the terminal moves 2 columns and 
will skip over the column where we want the '|' to appear.

I think the same should happen, in principle, without borders. Haven't
checked that though. Maybe we have to "clear the field" to the left and
right of child frames in the root frame matrix so that wide characters
don't interfere.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-23  4:40             ` Gerd Möllmann
@ 2024-10-23  5:00               ` Gerd Möllmann
  2024-10-23  7:49                 ` Eli Zaretskii
  2024-10-23  6:54               ` Feng Shu
  2024-10-23  7:28               ` Eli Zaretskii
  2 siblings, 1 reply; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-23  5:00 UTC (permalink / raw)
  To: Feng Shu; +Cc: Emacs Devel

Gerd Möllmann <gerd.moellmann@gmail.com> writes:

> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
>
>> Feng Shu <tumashu@163.com> writes:
>>
>>> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
>>>
>>>> Feng Shu <tumashu@163.com> writes:
>>>>
>>>>> border do not work well when current buffer is CJK, maybe because CJK
>>>>> Char width is not 1
>>>>
>>>> Could you please send me a bit of CJK text so that I have something I
>>>> can test this with?
>>
>> Thanks!
>
> Hm, okay, but not sure how to fix that.
>
> The problem comes indeed from the root frame displaying characters that
> are more then 1 column wide, say N. When we output such a character to
> the terminal, the cursor moves by N columns. Emacs represents that in
> the glyph matrix by producing N glyphs for the character, all bu tthe
> first have a padding flag set. So we have
>
>   ... (C, 0) (C, 1) ...
>
> in the glpyh matrix, where C is the character, and the number is the
> padding flag.
>
> Now assume, for example, that the child frame is posititoned so that its
> left border where we want to display a '|' is in the column for the (C,
> 1).
>
> That can't work, because writing C to the terminal moves 2 columns and 
> will skip over the column where we want the '|' to appear.
>
> I think the same should happen, in principle, without borders. Haven't
> checked that though. Maybe we have to "clear the field" to the left and
> right of child frames in the root frame matrix so that wide characters
> don't interfere.

Another question is if there is something lurking with R2L? I have no
idea what terminals do in that case, but I'd guess Eli knows :-).



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-23  4:40             ` Gerd Möllmann
  2024-10-23  5:00               ` Gerd Möllmann
@ 2024-10-23  6:54               ` Feng Shu
  2024-10-23  7:25                 ` Gerd Möllmann
  2024-10-23  7:28               ` Eli Zaretskii
  2 siblings, 1 reply; 80+ messages in thread
From: Feng Shu @ 2024-10-23  6:54 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Emacs Devel

Gerd Möllmann <gerd.moellmann@gmail.com> writes:

> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
>
>> Feng Shu <tumashu@163.com> writes:
>>
>>> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
>>>
>>>> Feng Shu <tumashu@163.com> writes:
>>>>
>>>>> border do not work well when current buffer is CJK, maybe because CJK
>>>>> Char width is not 1
>>>>
>>>> Could you please send me a bit of CJK text so that I have something I
>>>> can test this with?
>>
>> Thanks!
>
> Hm, okay, but not sure how to fix that.
>

This is a bad news, if we can not fix this problem, enable this feature
all the world will be not a good idea.



> The problem comes indeed from the root frame displaying characters that
> are more then 1 column wide, say N. When we output such a character to
> the terminal, the cursor moves by N columns. Emacs represents that in
> the glyph matrix by producing N glyphs for the character, all bu tthe
> first have a padding flag set. So we have
>
>   ... (C, 0) (C, 1) ...
>
> in the glpyh matrix, where C is the character, and the number is the
> padding flag.
>
> Now assume, for example, that the child frame is posititoned so that its
> left border where we want to display a '|' is in the column for the (C,
> 1).
>
> That can't work, because writing C to the terminal moves 2 columns and 
> will skip over the column where we want the '|' to appear.
>
> I think the same should happen, in principle, without borders. Haven't
> checked that though. Maybe we have to "clear the field" to the left and
> right of child frames in the root frame matrix so that wide characters
> don't interfere.

-- 




^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-23  3:05 ` Feng Shu
  2024-10-23  3:13   ` Gerd Möllmann
@ 2024-10-23  7:11   ` Eli Zaretskii
  1 sibling, 0 replies; 80+ messages in thread
From: Eli Zaretskii @ 2024-10-23  7:11 UTC (permalink / raw)
  To: Feng Shu; +Cc: gerd.moellmann, emacs-devel

> From: Feng Shu <tumashu@163.com>
> Cc: Emacs Devel <emacs-devel@gnu.org>
> Date: Wed, 23 Oct 2024 11:05:51 +0800
> 
> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
> 
> Will the below interfaces changed?
> 
> 1. tty-child-frames
> 2. tty-non-selected-cursor
> 3. undecorated
> 
> If not, I have updated posframe, vertico-posframe and ivy-posframe to
> support three interfaces.

It's too early to make changes in 3rd-party packages based on what you
see on the branch.  The branch is too young for that, and it's quite
possible that Lisp APIs related to this feature will change, when
enough people take a look at what's there.

I suggest to wait for this branch to land on master before you make
public changes in your packages.  Until then, please provide your
inputs and feedback for using the branch, so that it takes your use
cases into consideration.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-23  6:54               ` Feng Shu
@ 2024-10-23  7:25                 ` Gerd Möllmann
  0 siblings, 0 replies; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-23  7:25 UTC (permalink / raw)
  To: Feng Shu; +Cc: Emacs Devel

Feng Shu <tumashu@163.com> writes:

>> Hm, okay, but not sure how to fix that.
>
> This is a bad news, if we can not fix this problem, enable this feature
> all the world will be not a good idea.

Don't be pessinistic :-). I'm just figuring out what's the best way to
fix it. It's not that I don't have ideas.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-23  4:40             ` Gerd Möllmann
  2024-10-23  5:00               ` Gerd Möllmann
  2024-10-23  6:54               ` Feng Shu
@ 2024-10-23  7:28               ` Eli Zaretskii
  2024-10-23  7:37                 ` Gerd Möllmann
  2 siblings, 1 reply; 80+ messages in thread
From: Eli Zaretskii @ 2024-10-23  7:28 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: tumashu, emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: Emacs Devel <emacs-devel@gnu.org>
> Date: Wed, 23 Oct 2024 06:40:11 +0200
> 
> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
> 
> > Feng Shu <tumashu@163.com> writes:
> >
> >> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
> >>
> >>> Feng Shu <tumashu@163.com> writes:
> >>>
> >>>> border do not work well when current buffer is CJK, maybe because CJK
> >>>> Char width is not 1
> >>>
> >>> Could you please send me a bit of CJK text so that I have something I
> >>> can test this with?
> >
> > Thanks!
> 
> Hm, okay, but not sure how to fix that.
> 
> The problem comes indeed from the root frame displaying characters that
> are more then 1 column wide, say N. When we output such a character to
> the terminal, the cursor moves by N columns. Emacs represents that in
> the glyph matrix by producing N glyphs for the character, all bu tthe
> first have a padding flag set. So we have
> 
>   ... (C, 0) (C, 1) ...
> 
> in the glpyh matrix, where C is the character, and the number is the
> padding flag.
> 
> Now assume, for example, that the child frame is posititoned so that its
> left border where we want to display a '|' is in the column for the (C,
> 1).
> 
> That can't work, because writing C to the terminal moves 2 columns and 
> will skip over the column where we want the '|' to appear.

I think the solution should be the same as what we do when a line
needs to wrap (i.e. be continued) and we don't have enough columns to
put all the N glyphs before the continuation glyph: we don't show the
entire N-glyph group.  IOW, the border glyph should be shown instead
of multi-column character, padded with enough spaces before it to make
up for the M < N glyphs of C that could be shown before the border.
In your example above, C will not be shown, and instead we will show
one space glyph followed by the '|' glyph.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-23  7:28               ` Eli Zaretskii
@ 2024-10-23  7:37                 ` Gerd Möllmann
  2024-10-23  7:52                   ` Feng Shu
  0 siblings, 1 reply; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-23  7:37 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: tumashu, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Gerd Möllmann <gerd.moellmann@gmail.com>
>> Cc: Emacs Devel <emacs-devel@gnu.org>
>> Date: Wed, 23 Oct 2024 06:40:11 +0200
>> 
>> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
>> 
>> > Feng Shu <tumashu@163.com> writes:
>> >
>> >> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
>> >>
>> >>> Feng Shu <tumashu@163.com> writes:
>> >>>
>> >>>> border do not work well when current buffer is CJK, maybe because CJK
>> >>>> Char width is not 1
>> >>>
>> >>> Could you please send me a bit of CJK text so that I have something I
>> >>> can test this with?
>> >
>> > Thanks!
>> 
>> Hm, okay, but not sure how to fix that.
>> 
>> The problem comes indeed from the root frame displaying characters that
>> are more then 1 column wide, say N. When we output such a character to
>> the terminal, the cursor moves by N columns. Emacs represents that in
>> the glyph matrix by producing N glyphs for the character, all bu tthe
>> first have a padding flag set. So we have
>> 
>>   ... (C, 0) (C, 1) ...
>> 
>> in the glpyh matrix, where C is the character, and the number is the
>> padding flag.
>> 
>> Now assume, for example, that the child frame is posititoned so that its
>> left border where we want to display a '|' is in the column for the (C,
>> 1).
>> 
>> That can't work, because writing C to the terminal moves 2 columns and 
>> will skip over the column where we want the '|' to appear.
>
> I think the solution should be the same as what we do when a line
> needs to wrap (i.e. be continued) and we don't have enough columns to
> put all the N glyphs before the continuation glyph: we don't show the
> entire N-glyph group.  IOW, the border glyph should be shown instead
> of multi-column character, padded with enough spaces before it to make
> up for the M < N glyphs of C that could be shown before the border.
> In your example above, C will not be shown, and instead we will show
> one space glyph followed by the '|' glyph.

Yes, I came up with this (WIP):

/* If the glyph in ROW at position X is part of a wide character, change
   every glyph belonging to the wide character to a space glyph.  */

static void
neutralize_wide_char (struct frame *root, struct glyph_row *row, int x)
{
  eassert (x >= 0 && x < root->desired_matrix->matrix_w);
  struct glyph *glyph = row->glyphs[0] + x;
  if (glyph->type == CHAR_GLYPH && CHARACTER_WIDTH (glyph->u.ch) > 1)
    {
      struct glyph *row_start = row->glyphs[0];
      struct glyph *row_limit = row_start + row->used[0];

      /* Glyph is somewhere in a sequence of glyphs for a wide
	 character, find the start.  */
      while (glyph > row_start && glyph->padding_p)
	--glyph;

      /* Make everything in the sequence a space glyph.  */
      eassert (!glyph->padding_p);
      make_glyph_space (glyph);
      for (++glyph; glyph < row_limit && glyph->padding_p; ++glyph)
	make_glyph_space (glyph);
    }
}

I have some problems reproducing the whole thing on my system with the
CJK file Feng Shu gave me. Hm.






^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-23  5:00               ` Gerd Möllmann
@ 2024-10-23  7:49                 ` Eli Zaretskii
  2024-10-23  8:12                   ` Gerd Möllmann
  2024-10-23 11:04                   ` Gerd Möllmann
  0 siblings, 2 replies; 80+ messages in thread
From: Eli Zaretskii @ 2024-10-23  7:49 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: tumashu, emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: Emacs Devel <emacs-devel@gnu.org>
> Date: Wed, 23 Oct 2024 07:00:03 +0200
> 
> Another question is if there is something lurking with R2L? I have no
> idea what terminals do in that case, but I'd guess Eli knows :-).

Terminals should do nothing, and if your terminal emulator has a "bidi
reordering" feature, you should disable it, because Emacs does
everything by itself, and needs the terminal to be "dumb" in this
aspect.

Basically, we produce a glyph row where all the glyphs are flushed to
the right window edge, and there's empty space on the left of each
screen line to make it a full-width screen line.  The low-level write
code that writes to the terminal then works normally, writing glyphs
left to right.

The current branch hits an assertion violation when showing RTL text
in a child window.  (Showing RTL text in the parent window works
fine.)  Here's the recipe:

 emacs -Q -nw
 Evaluate in *scratch:

    (defun my-make-child ()
      (interactive)
      (make-frame `((parent-frame . ,(selected-frame))
		    (background-color . "gray10")
		    (foreground-color . "white")
		    (internal-border-width . 1)
		    (top . 15)
		    (left . 40)
		    (width . 80)
		    (height . 25))))

 M-x my-make-child RET
 C-x 5 o
 C-x C-f etc/tutorials/TUTORIAL.he RET

The backtrace from the assertion is below, including the data which is
involved.  As you see, X is 119 whereas the matrix_w is 108, which
violates the assertion.

Let me know if you want me to try something or show more data or maybe
tell you more about how RTL display works in TTY case.

  Thread 1 "emacs" received signal SIGABRT, Aborted.
  0x00007ff5201959fc in pthread_kill () from /lib/x86_64-linux-gnu/libc.so.6
  (gdb) bt
  #0  0x00007ff5201959fc in pthread_kill () at /lib/x86_64-linux-gnu/libc.so.6
  #1  0x00007ff520141476 in raise () at /lib/x86_64-linux-gnu/libc.so.6
  #2  0x0000559f4892c840 in terminate_due_to_signal
      (sig=sig@entry=6, backtrace_limit=backtrace_limit@entry=2147483647)
      at emacs.c:469
  #3  0x0000559f48933221 in die
      (msg=msg@entry=0x559f48c2ceb8 "x < root->current_matrix->matrix_w", file=file@entry=0x559f48c2c004 "dispnew.c", line=line@entry=3774) at alloc.c:8059
  #4  0x0000559f4891c4cb in is_cursor_obscured () at dispnew.c:3774
  #5  terminal_cursor_magic (topmost_child=0x559f4b07b9b0, root=0x559f4afcd050)
      at dispnew.c:3790
  #6  combine_updates_for_frame
      (f=<optimized out>, force_p=<optimized out>, inhibit_scrolling=<optimized out>) at dispnew.c:3840
  #7  0x0000559f48954656 in combine_updates
      (roots=roots@entry=0x7ff51c751723, force_p=force_p@entry=false, inhibit_scrolling=inhibit_scrolling@entry=false) at dispnew.c:3867
  #8  0x0000559f489af858 in redisplay_internal () at xdisp.c:17612
  #9  0x0000559f48abe24a in read_char
      (commandflag=1, map=0x7ff51c752503, prev_event=0x0, used_mouse_menu=0x7fff251757eb, end_time=0x0) at keyboard.c:2673
  #10 0x0000559f48abf794 in read_key_sequence
      (keybuf=0x7fff25175940, prompt=0x0, dont_downcase_last=<optimized out>, can_return_switch_frame=true, fix_current_buffer=true, prevent_redisplay=<optimized out>, disable_text_conversion_p=false) at keyboard.c:10747
  #11 0x0000559f48ac14f1 in command_loop_1 () at keyboard.c:1424
  #12 0x0000559f48b4fee7 in internal_condition_case
      (bfun=bfun@entry=0x559f48ac1280 <command_loop_1>, handlers=handlers@entry=0x90, hfun=hfun@entry=0x559f48ab4d50 <cmd_error>) at eval.c:1598
  #13 0x0000559f48aaab8e in command_loop_2 (handlers=handlers@entry=0x90)
      at keyboard.c:1163
  #14 0x0000559f48b4fd69 in internal_catch
      (tag=tag@entry=0x12360, func=func@entry=0x559f48aaab60 <command_loop_2>, arg=arg@entry=0x90) at eval.c:1277
  #15 0x0000559f48aaab29 in command_loop () at keyboard.c:1141
  #16 0x0000559f48ab4805 in recursive_edit_1 () at keyboard.c:749
  #17 0x0000559f48ab4bb8 in Frecursive_edit () at keyboard.c:832
  #18 0x0000559f489479e6 in main (argc=<optimized out>, argv=<optimized out>)
      at emacs.c:2624
  (gdb) up
  #1  0x00007fd8d9b21476 in raise () from /lib/x86_64-linux-gnu/libc.so.6
  (gdb)
  #2  0x000055ae22b9a840 in terminate_due_to_signal (sig=sig@entry=6,
      backtrace_limit=backtrace_limit@entry=2147483647) at emacs.c:469
  469       emacs_raise (sig);
  (gdb)
  #3  0x000055ae22ba1221 in die (
      msg=msg@entry=0x55ae22e9aeb8 "x < root->current_matrix->matrix_w",
      file=file@entry=0x55ae22e9a004 "dispnew.c", line=line@entry=3774)
      at alloc.c:8059
  8059      terminate_due_to_signal (SIGABRT, INT_MAX);
  (gdb)
  #4  0x000055ae22b8a4cb in is_cursor_obscured () at dispnew.c:3774
  3774      eassert (x < root->current_matrix->matrix_w);
  (gdb) p x
  $1 = 119
  (gdb) p root->current_matrix->matrix_w
  value has been optimized out
  (gdb) p root->current_matrix
  value has been optimized out
  (gdb) p root
  $2 = <optimized out>
  (gdb) up
  #5  terminal_cursor_magic (topmost_child=0x55ae245e89e8, root=0x55ae245c3050)
      at dispnew.c:3790
  3790      if (is_cursor_obscured ())
  (gdb) p root->current_matrix->matrix_w
  $3 = 108



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-23  7:37                 ` Gerd Möllmann
@ 2024-10-23  7:52                   ` Feng Shu
  2024-10-23  8:07                     ` Gerd Möllmann
  0 siblings, 1 reply; 80+ messages in thread
From: Feng Shu @ 2024-10-23  7:52 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Eli Zaretskii, emacs-devel

[-- Attachment #1: Type: text/plain, Size: 3385 bytes --]

Gerd Möllmann <gerd.moellmann@gmail.com> writes:

> Eli Zaretskii <eliz@gnu.org> writes:
>
>>> From: Gerd Möllmann <gerd.moellmann@gmail.com>
>>> Cc: Emacs Devel <emacs-devel@gnu.org>
>>> Date: Wed, 23 Oct 2024 06:40:11 +0200
>>> 
>>> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
>>> 
>>> > Feng Shu <tumashu@163.com> writes:
>>> >
>>> >> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
>>> >>
>>> >>> Feng Shu <tumashu@163.com> writes:
>>> >>>
>>> >>>> border do not work well when current buffer is CJK, maybe because CJK
>>> >>>> Char width is not 1
>>> >>>
>>> >>> Could you please send me a bit of CJK text so that I have something I
>>> >>> can test this with?
>>> >
>>> > Thanks!
>>> 
>>> Hm, okay, but not sure how to fix that.
>>> 
>>> The problem comes indeed from the root frame displaying characters that
>>> are more then 1 column wide, say N. When we output such a character to
>>> the terminal, the cursor moves by N columns. Emacs represents that in
>>> the glyph matrix by producing N glyphs for the character, all bu tthe
>>> first have a padding flag set. So we have
>>> 
>>>   ... (C, 0) (C, 1) ...
>>> 
>>> in the glpyh matrix, where C is the character, and the number is the
>>> padding flag.
>>> 
>>> Now assume, for example, that the child frame is posititoned so that its
>>> left border where we want to display a '|' is in the column for the (C,
>>> 1).
>>> 
>>> That can't work, because writing C to the terminal moves 2 columns and 
>>> will skip over the column where we want the '|' to appear.
>>
>> I think the solution should be the same as what we do when a line
>> needs to wrap (i.e. be continued) and we don't have enough columns to
>> put all the N glyphs before the continuation glyph: we don't show the
>> entire N-glyph group.  IOW, the border glyph should be shown instead
>> of multi-column character, padded with enough spaces before it to make
>> up for the M < N glyphs of C that could be shown before the border.
>> In your example above, C will not be shown, and instead we will show
>> one space glyph followed by the '|' glyph.
>
> Yes, I came up with this (WIP):
>
> /* If the glyph in ROW at position X is part of a wide character, change
>    every glyph belonging to the wide character to a space glyph.  */
>
> static void
> neutralize_wide_char (struct frame *root, struct glyph_row *row, int x)
> {
>   eassert (x >= 0 && x < root->desired_matrix->matrix_w);
>   struct glyph *glyph = row->glyphs[0] + x;
>   if (glyph->type == CHAR_GLYPH && CHARACTER_WIDTH (glyph->u.ch) > 1)
>     {
>       struct glyph *row_start = row->glyphs[0];
>       struct glyph *row_limit = row_start + row->used[0];
>
>       /* Glyph is somewhere in a sequence of glyphs for a wide
> 	 character, find the start.  */
>       while (glyph > row_start && glyph->padding_p)
> 	--glyph;
>
>       /* Make everything in the sequence a space glyph.  */
>       eassert (!glyph->padding_p);
>       make_glyph_space (glyph);
>       for (++glyph; glyph < row_limit && glyph->padding_p; ++glyph)
> 	make_glyph_space (glyph);
>     }
> }
>
> I have some problems reproducing the whole thing on my system with the
> CJK file Feng Shu gave me. Hm.

The below adjust will show more issue:-)

1. Reduce terminal's width
2. let a long line auto wrap to many lines.



[-- Attachment #2: 2024-10-23_15-51.png --]
[-- Type: image/png, Size: 129309 bytes --]

[-- Attachment #3: Type: text/plain, Size: 7 bytes --]




-- 

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-23  7:52                   ` Feng Shu
@ 2024-10-23  8:07                     ` Gerd Möllmann
  2024-10-23  9:07                       ` Feng Shu
  0 siblings, 1 reply; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-23  8:07 UTC (permalink / raw)
  To: Feng Shu; +Cc: Eli Zaretskii, emacs-devel

Feng Shu <tumashu@163.com> writes:

> The below adjust will show more issue:-)
>
> 1. Reduce terminal's width
> 2. let a long line auto wrap to many lines.

Could you please see what happens with what I just pushed to savannah?
The other one, with the borders, and this one.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-23  7:49                 ` Eli Zaretskii
@ 2024-10-23  8:12                   ` Gerd Möllmann
  2024-10-23 11:04                   ` Gerd Möllmann
  1 sibling, 0 replies; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-23  8:12 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: tumashu, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Gerd Möllmann <gerd.moellmann@gmail.com>
>> Cc: Emacs Devel <emacs-devel@gnu.org>
>> Date: Wed, 23 Oct 2024 07:00:03 +0200
>> 
>> Another question is if there is something lurking with R2L? I have no
>> idea what terminals do in that case, but I'd guess Eli knows :-).
>
> Terminals should do nothing, and if your terminal emulator has a "bidi
> reordering" feature, you should disable it, because Emacs does
> everything by itself, and needs the terminal to be "dumb" in this
> aspect.
>
> Basically, we produce a glyph row where all the glyphs are flushed to
> the right window edge, and there's empty space on the left of each
> screen line to make it a full-width screen line.  The low-level write
> code that writes to the terminal then works normally, writing glyphs
> left to right.

Thanks, that's good to know for sure.

>
> The current branch hits an assertion violation when showing RTL text
> in a child window.  (Showing RTL text in the parent window works
> fine.)  Here's the recipe:
>
>  emacs -Q -nw
>  Evaluate in *scratch:
>
>     (defun my-make-child ()
>       (interactive)
>       (make-frame `((parent-frame . ,(selected-frame))
> 		    (background-color . "gray10")
> 		    (foreground-color . "white")
> 		    (internal-border-width . 1)
> 		    (top . 15)
> 		    (left . 40)
> 		    (width . 80)
> 		    (height . 25))))
>
>  M-x my-make-child RET
>  C-x 5 o
>  C-x C-f etc/tutorials/TUTORIAL.he RET

I'll have a look, a bit later.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-23  8:07                     ` Gerd Möllmann
@ 2024-10-23  9:07                       ` Feng Shu
  2024-10-23  9:58                         ` Gerd Möllmann
  0 siblings, 1 reply; 80+ messages in thread
From: Feng Shu @ 2024-10-23  9:07 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Eli Zaretskii, emacs-devel

[-- Attachment #1: Type: text/plain, Size: 368 bytes --]

Gerd Möllmann <gerd.moellmann@gmail.com> writes:

> Feng Shu <tumashu@163.com> writes:
>
>> The below adjust will show more issue:-)
>>
>> 1. Reduce terminal's width
>> 2. let a long line auto wrap to many lines.
>
> Could you please see what happens with what I just pushed to savannah?
> The other one, with the borders, and this one.

Works, thanks!


[-- Attachment #2: 2024-10-23_17-06.png --]
[-- Type: image/png, Size: 187772 bytes --]

[-- Attachment #3: Type: text/plain, Size: 7 bytes --]




-- 

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-23  9:07                       ` Feng Shu
@ 2024-10-23  9:58                         ` Gerd Möllmann
  0 siblings, 0 replies; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-23  9:58 UTC (permalink / raw)
  To: Feng Shu; +Cc: Eli Zaretskii, emacs-devel

Feng Shu <tumashu@163.com> writes:

> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
>
>> Feng Shu <tumashu@163.com> writes:
>>
>>> The below adjust will show more issue:-)
>>>
>>> 1. Reduce terminal's width
>>> 2. let a long line auto wrap to many lines.
>>
>> Could you please see what happens with what I just pushed to savannah?
>> The other one, with the borders, and this one.
>
> Works, thanks!

Thanks!



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-23  7:49                 ` Eli Zaretskii
  2024-10-23  8:12                   ` Gerd Möllmann
@ 2024-10-23 11:04                   ` Gerd Möllmann
  2024-10-23 17:23                     ` Eli Zaretskii
  1 sibling, 1 reply; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-23 11:04 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: tumashu, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> The current branch hits an assertion violation when showing RTL text
> in a child window.  (Showing RTL text in the parent window works
> fine.)  Here's the recipe:
>
>  emacs -Q -nw
>  Evaluate in *scratch:
>
>     (defun my-make-child ()
>       (interactive)
>       (make-frame `((parent-frame . ,(selected-frame))
> 		    (background-color . "gray10")
> 		    (foreground-color . "white")
> 		    (internal-border-width . 1)
> 		    (top . 15)
> 		    (left . 40)
> 		    (width . 80)
> 		    (height . 25))))
>
>  M-x my-make-child RET
>  C-x 5 o
>  C-x C-f etc/tutorials/TUTORIAL.he RET

Could you please check with what I pushed now? There were several
problem, starting with an error I made while porting this, plus thinkos.
Strangely, I didn't run into the assertion with the recipe, so it's
probably best if you check yourself if it works now.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-23 11:04                   ` Gerd Möllmann
@ 2024-10-23 17:23                     ` Eli Zaretskii
  2024-10-23 17:52                       ` Gerd Möllmann
  0 siblings, 1 reply; 80+ messages in thread
From: Eli Zaretskii @ 2024-10-23 17:23 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: tumashu, emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: tumashu@163.com,  emacs-devel@gnu.org
> Date: Wed, 23 Oct 2024 13:04:17 +0200
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > The current branch hits an assertion violation when showing RTL text
> > in a child window.  (Showing RTL text in the parent window works
> > fine.)  Here's the recipe:
> >
> >  emacs -Q -nw
> >  Evaluate in *scratch:
> >
> >     (defun my-make-child ()
> >       (interactive)
> >       (make-frame `((parent-frame . ,(selected-frame))
> > 		    (background-color . "gray10")
> > 		    (foreground-color . "white")
> > 		    (internal-border-width . 1)
> > 		    (top . 15)
> > 		    (left . 40)
> > 		    (width . 80)
> > 		    (height . 25))))
> >
> >  M-x my-make-child RET
> >  C-x 5 o
> >  C-x C-f etc/tutorials/TUTORIAL.he RET
> 
> Could you please check with what I pushed now? There were several
> problem, starting with an error I made while porting this, plus thinkos.
> Strangely, I didn't run into the assertion with the recipe, so it's
> probably best if you check yourself if it works now.

The assertion violation is gone, and the display seems correct, but I
now get compilation warning:

    CC       dispnew.o
  In file included from dispnew.c:27:
  dispnew.c: In function ‘combine_updates_for_frame’:
  lisp.h:1096:68: warning: null pointer dereference [-Wnull-dereference]
   1096 |           && ((XUNTAG (a, Lisp_Vectorlike, union vectorlike_header)->size
	|                                                                    ^

I have no idea what is GCC talking about.

Thanks.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-23 17:23                     ` Eli Zaretskii
@ 2024-10-23 17:52                       ` Gerd Möllmann
  0 siblings, 0 replies; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-23 17:52 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: tumashu, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> The assertion violation is gone, and the display seems correct, but I
> now get compilation warning:
>
>     CC       dispnew.o
>   In file included from dispnew.c:27:
>   dispnew.c: In function ‘combine_updates_for_frame’:
>   lisp.h:1096:68: warning: null pointer dereference [-Wnull-dereference]
>    1096 |           && ((XUNTAG (a, Lisp_Vectorlike, union vectorlike_header)->size
> 	|                                                                    ^
>
> I have no idea what is GCC talking about.
>
> Thanks.

Thanks for checking!

I don't have an idea what GCC means either. Can one perhaps turn off
inlining for the compilation? Maybe GCC then prints something less
cryptic.




^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22  4:46 "Final" version of tty child frames Gerd Möllmann
                   ` (3 preceding siblings ...)
  2024-10-23  3:05 ` Feng Shu
@ 2024-10-26  8:15 ` Gerd Möllmann
  4 siblings, 0 replies; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-26  8:15 UTC (permalink / raw)
  To: Emacs Devel

[-- Attachment #1: Type: text/plain, Size: 460 bytes --]

Gerd Möllmann <gerd.moellmann@gmail.com> writes:

> Other things not contained:
>
...
> - Tooltips. I think tooltips could relatively easily be implemented
>   with child frames by copying or extracting the non-native tooltip
>   code from x-show-tip or some such. From my POV, that would be best
>   done by refactoring the tooltip code across window systems which I
>   can't do.

Just a gimmick: sort of tooltips for displaying help on ttys,


[-- Attachment #2: tty-tip.el --]
[-- Type: application/emacs-lisp, Size: 5171 bytes --]

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-22  5:29 ` Eli Zaretskii
  2024-10-22  9:58   ` martin rudalics
@ 2024-10-28  4:35   ` Jared Finder
  2024-10-28  5:57     ` Gerd Möllmann
  2024-11-30 11:25     ` Eli Zaretskii
  1 sibling, 2 replies; 80+ messages in thread
From: Jared Finder @ 2024-10-28  4:35 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Gerd Möllmann, emacs-devel, martin rudalics

On 2024-10-21 22:29, Eli Zaretskii wrote:
>> From: Gerd Möllmann <gerd.moellmann@gmail.com>
>> Date: Tue, 22 Oct 2024 06:46:15 +0200
>> 
>> I have (re-)created the scratch/tty-child-frames branch today, which
>> contains the code for child frames on ttys, based on a recent master.
> 
> Thanks.
> 
>> Disclaimer: As I mentioned already in other contexts, I don't want to
>> be the maintainer of anything, for personal reasons.
> 
> We have difficulty working with "fire and forget" contributions of
> this size and complexity.  Maybe Jared (CC'ed) could take a look and
> clean up the branch, to prepare it for landing.  Or someone else with
> interest in this stuff (Martin?).

I'm happy to be considered here and would be interested in helping out, 
but it would have to wait. I'm extremely busy at my job for the next 
month or so and unfortunately do not have time to take a contribution 
such as this to completion.

I'll check back in when my time clears up. I would certainly be 
interested in helping here if needed. I am certainly interested in 
making TTY Emacs more functional in general.

   -- MJF



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-28  4:35   ` Jared Finder
@ 2024-10-28  5:57     ` Gerd Möllmann
  2024-11-30 11:25     ` Eli Zaretskii
  1 sibling, 0 replies; 80+ messages in thread
From: Gerd Möllmann @ 2024-10-28  5:57 UTC (permalink / raw)
  To: Jared Finder; +Cc: Eli Zaretskii, emacs-devel, martin rudalics

Jared Finder <jared@finder.org> writes:

> On 2024-10-21 22:29, Eli Zaretskii wrote:
>>> From: Gerd Möllmann <gerd.moellmann@gmail.com>
>>> Date: Tue, 22 Oct 2024 06:46:15 +0200
>>> I have (re-)created the scratch/tty-child-frames branch today,
>>> which
>>> contains the code for child frames on ttys, based on a recent master.
>> Thanks.
>> 
>>> Disclaimer: As I mentioned already in other contexts, I don't want to
>>> be the maintainer of anything, for personal reasons.
>> We have difficulty working with "fire and forget" contributions of
>> this size and complexity.  Maybe Jared (CC'ed) could take a look and
>> clean up the branch, to prepare it for landing.  Or someone else with
>> interest in this stuff (Martin?).
>
> I'm happy to be considered here and would be interested in helping
> out, but it would have to wait. I'm extremely busy at my job for the
> next month or so and unfortunately do not have time to take a
> contribution such as this to completion.
>
> I'll check back in when my time clears up. I would certainly be
> interested in helping here if needed. I am certainly interested in
> making TTY Emacs more functional in general.

👍



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-10-28  4:35   ` Jared Finder
  2024-10-28  5:57     ` Gerd Möllmann
@ 2024-11-30 11:25     ` Eli Zaretskii
  2024-12-05  3:49       ` Jared Finder
  1 sibling, 1 reply; 80+ messages in thread
From: Eli Zaretskii @ 2024-11-30 11:25 UTC (permalink / raw)
  To: Jared Finder; +Cc: gerd.moellmann, emacs-devel, rudalics

> Date: Sun, 27 Oct 2024 21:35:29 -0700
> From: Jared Finder <jared@finder.org>
> Cc: Gerd Möllmann <gerd.moellmann@gmail.com>,
>  emacs-devel@gnu.org, martin rudalics <rudalics@gmx.at>
> 
> On 2024-10-21 22:29, Eli Zaretskii wrote:
> >> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> >> Date: Tue, 22 Oct 2024 06:46:15 +0200
> >> 
> >> I have (re-)created the scratch/tty-child-frames branch today, which
> >> contains the code for child frames on ttys, based on a recent master.
> > 
> > Thanks.
> > 
> >> Disclaimer: As I mentioned already in other contexts, I don't want to
> >> be the maintainer of anything, for personal reasons.
> > 
> > We have difficulty working with "fire and forget" contributions of
> > this size and complexity.  Maybe Jared (CC'ed) could take a look and
> > clean up the branch, to prepare it for landing.  Or someone else with
> > interest in this stuff (Martin?).
> 
> I'm happy to be considered here and would be interested in helping out, 
> but it would have to wait. I'm extremely busy at my job for the next 
> month or so and unfortunately do not have time to take a contribution 
> such as this to completion.
> 
> I'll check back in when my time clears up. I would certainly be 
> interested in helping here if needed. I am certainly interested in 
> making TTY Emacs more functional in general.

Jared, if you have time now, please take a look at this branch.  From
where I stand, once it works well with xt-mouse, we can land this
feature on master.

TIA



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-11-30 11:25     ` Eli Zaretskii
@ 2024-12-05  3:49       ` Jared Finder
  2024-12-11  7:31         ` Jared Finder
  0 siblings, 1 reply; 80+ messages in thread
From: Jared Finder @ 2024-12-05  3:49 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gerd.moellmann, emacs-devel, rudalics

On 2024-11-30 03:25, Eli Zaretskii wrote:
>> Date: Sun, 27 Oct 2024 21:35:29 -0700
>> From: Jared Finder <jared@finder.org>
>> Cc: Gerd Möllmann <gerd.moellmann@gmail.com>,
>>  emacs-devel@gnu.org, martin rudalics <rudalics@gmx.at>
>> 
>> On 2024-10-21 22:29, Eli Zaretskii wrote:
>> >> From: Gerd Möllmann <gerd.moellmann@gmail.com>
>> >> Date: Tue, 22 Oct 2024 06:46:15 +0200
>> >>
>> >> I have (re-)created the scratch/tty-child-frames branch today, which
>> >> contains the code for child frames on ttys, based on a recent master.
>> >
>> > Thanks.
>> >
>> >> Disclaimer: As I mentioned already in other contexts, I don't want to
>> >> be the maintainer of anything, for personal reasons.
>> >
>> > We have difficulty working with "fire and forget" contributions of
>> > this size and complexity.  Maybe Jared (CC'ed) could take a look and
>> > clean up the branch, to prepare it for landing.  Or someone else with
>> > interest in this stuff (Martin?).
>> 
>> I'm happy to be considered here and would be interested in helping 
>> out,
>> but it would have to wait. I'm extremely busy at my job for the next
>> month or so and unfortunately do not have time to take a contribution
>> such as this to completion.
>> 
>> I'll check back in when my time clears up. I would certainly be
>> interested in helping here if needed. I am certainly interested in
>> making TTY Emacs more functional in general.
> 
> Jared, if you have time now, please take a look at this branch.  From
> where I stand, once it works well with xt-mouse, we can land this
> feature on master.

Yup, I'll start looking into this starting this weekend.

   -- MJF



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-05  3:49       ` Jared Finder
@ 2024-12-11  7:31         ` Jared Finder
  2024-12-11  7:59           ` Gerd Möllmann
  2024-12-11  9:39           ` martin rudalics
  0 siblings, 2 replies; 80+ messages in thread
From: Jared Finder @ 2024-12-11  7:31 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gerd.moellmann, emacs-devel, rudalics

On 2024-12-04 19:49, Jared Finder wrote:
> On 2024-11-30 03:25, Eli Zaretskii wrote:
>>> Date: Sun, 27 Oct 2024 21:35:29 -0700
>>> From: Jared Finder <jared@finder.org>
>>> Cc: Gerd Möllmann <gerd.moellmann@gmail.com>,
>>>  emacs-devel@gnu.org, martin rudalics <rudalics@gmx.at>
>>> 
>>> On 2024-10-21 22:29, Eli Zaretskii wrote:
>>> >> From: Gerd Möllmann <gerd.moellmann@gmail.com>
>>> >> Date: Tue, 22 Oct 2024 06:46:15 +0200
>>> >>
>>> >> I have (re-)created the scratch/tty-child-frames branch today, which
>>> >> contains the code for child frames on ttys, based on a recent master.
>>> >
>>> > Thanks.
>>> >
>>> >> Disclaimer: As I mentioned already in other contexts, I don't want to
>>> >> be the maintainer of anything, for personal reasons.
>>> >
>>> > We have difficulty working with "fire and forget" contributions of
>>> > this size and complexity.  Maybe Jared (CC'ed) could take a look and
>>> > clean up the branch, to prepare it for landing.  Or someone else with
>>> > interest in this stuff (Martin?).
>>> 
>>> I'm happy to be considered here and would be interested in helping 
>>> out,
>>> but it would have to wait. I'm extremely busy at my job for the next
>>> month or so and unfortunately do not have time to take a contribution
>>> such as this to completion.
>>> 
>>> I'll check back in when my time clears up. I would certainly be
>>> interested in helping here if needed. I am certainly interested in
>>> making TTY Emacs more functional in general.
>> 
>> Jared, if you have time now, please take a look at this branch.  From
>> where I stand, once it works well with xt-mouse, we can land this
>> feature on master.
> 
> Yup, I'll start looking into this starting this weekend.

Spent time just playing with child frames to get familiar with things. A 
few bugs I noticed:

There's something in the C code that makes this branch not work with the 
mini-frame package (https://github.com/muffinmad/emacs-mini-frame). The 
package works fine in graphical mode.

I'm using posframe for iteration. There's a minor thing where the cursor 
position is inconsistent when at the end of a line with a posframe on it 
in the tty. Maybe that's intentional? It's kinda confusing to me.

There's also a major thing where I can click on the child frame in the 
tty and then my cursor is actually in the child frame and I can just 
alter the text there. That behavior is inconsistent with the behavior on 
graphical displays and something I can investigate.

   -- MJF



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-11  7:31         ` Jared Finder
@ 2024-12-11  7:59           ` Gerd Möllmann
  2024-12-12  5:11             ` Jared Finder
  2024-12-11  9:39           ` martin rudalics
  1 sibling, 1 reply; 80+ messages in thread
From: Gerd Möllmann @ 2024-12-11  7:59 UTC (permalink / raw)
  To: Jared Finder; +Cc: Eli Zaretskii, emacs-devel, rudalics

Jared Finder <jared@finder.org> writes:

> Spent time just playing with child frames to get familiar with things.
> A few bugs I noticed:

Nice!

> There's something in the C code that makes this branch not work with
> the mini-frame package
> (https://github.com/muffinmad/emacs-mini-frame). The package works
> fine in graphical mode.

The page says that the package is using mini-buffer-only frames, which
are not implemented, because I wasn't interested. See the #if 0
make_terminal_frame.

> I'm using posframe for iteration. There's a minor thing where the
> cursor position is inconsistent when at the end of a line with a
> posframe on it in the tty. Maybe that's intentional? It's kinda
> confusing to me.

Could you please describe in detail what you are doing? I haven't
understood yet. Posframe and Corfu are BTW the reason I added the
child frames.

Child frames in the more general sense was not so interesting. Always
talking about me personally of course. I think I talked with Martin
about adding stuff as needed if users really want it, or something, and
ISTR he agreed. But Martin can speak for himself :-).

> There's also a major thing where I can click on the child frame in the
> tty and then my cursor is actually in the child frame and I can just
> alter the text there. That behavior is inconsistent with the behavior
> on graphical displays and something I can investigate.

Could you please give a recipe? I don't understand yet what you mean.
Or, if you want to investigate, please do :-). Please don't hesitate to
ask me any question. At least I still remember more stuff about this
than about igc :-).



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-11  7:31         ` Jared Finder
  2024-12-11  7:59           ` Gerd Möllmann
@ 2024-12-11  9:39           ` martin rudalics
  1 sibling, 0 replies; 80+ messages in thread
From: martin rudalics @ 2024-12-11  9:39 UTC (permalink / raw)
  To: Jared Finder, Eli Zaretskii; +Cc: gerd.moellmann, emacs-devel

 > There's something in the C code that makes this branch not work with
 > the mini-frame package
 > (https://github.com/muffinmad/emacs-mini-frame). The package works
 > fine in graphical mode.

IIUC I use something similar to the mini-frame package and it's already
quite hairy to make the minibuffer frame pop up and automatically get
input focus.  The input focus thing will be hard to implement on ttys.

 > I'm using posframe for iteration. There's a minor thing where the
 > cursor position is inconsistent when at the end of a line with a
 > posframe on it in the tty. Maybe that's intentional? It's kinda
 > confusing to me.

Is the cursor in the normal window or in the posframe window?

 > There's also a major thing where I can click on the child frame in the
 > tty and then my cursor is actually in the child frame and I can just
 > alter the text there. That behavior is inconsistent with the behavior
 > on graphical displays and something I can investigate.

Do you mean that you do _not_ want to alter text in the child frame?

If you have the time, please also try to look into two issues I raised
earlier:

   Two further things I noticed: When point in the parent frame is
   effectively hidden by the child frame, its cursor sometimes appears at
   the right of the child frame and sometimes it's not shown.  I have not
   understood the underlying principle for this behavior.  Also when the
   selected region in the parent frame is active, its overlay covers the
   child frame.  That's ugly.

martin



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-11  7:59           ` Gerd Möllmann
@ 2024-12-12  5:11             ` Jared Finder
  2024-12-12  6:20               ` Gerd Möllmann
  2024-12-12  6:30               ` Eli Zaretskii
  0 siblings, 2 replies; 80+ messages in thread
From: Jared Finder @ 2024-12-12  5:11 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Eli Zaretskii, emacs-devel, rudalics

On 2024-12-11 02:59, Gerd Möllmann wrote:
> Jared Finder <jared@finder.org> writes:
> 
>> Spent time just playing with child frames to get familiar with things.
>> A few bugs I noticed:
> 
> Nice!
> 
>> There's something in the C code that makes this branch not work with
>> the mini-frame package
>> (https://github.com/muffinmad/emacs-mini-frame). The package works
>> fine in graphical mode.
> 
> The page says that the package is using mini-buffer-only frames, which
> are not implemented, because I wasn't interested. See the #if 0
> make_terminal_frame.

I think it would be good for unimplemented features to communicate that 
state to the user so users know clearly what is going on.  Right now the 
error a user sees is "Can’t change the ‘minibuffer’ parameter of this 
frame". Wouldn't it be better to have make-terminal-frame (a brand new 
function with no existing clients to support) error with something like 
"Minibuffer only frames are not supported in terminals"?

This also would avoid the bug that currently a minibuffer-only child 
frame gets left around which looks weird and confusing.

Are there any other intentionally unimplemented features?  It'd be nice 
to have similarly clean errors to inform users.

>> I'm using posframe for iteration. There's a minor thing where the
>> cursor position is inconsistent when at the end of a line with a
>> posframe on it in the tty. Maybe that's intentional? It's kinda
>> confusing to me.
> 
> Could you please describe in detail what you are doing? I haven't
> understood yet. Posframe and Corfu are BTW the reason I added the
> child frames.
> 
> Child frames in the more general sense was not so interesting. Always
> talking about me personally of course. I think I talked with Martin
> about adding stuff as needed if users really want it, or something, and
> ISTR he agreed. But Martin can speak for himself :-).

This was from me moving the cursor around (just using arrow keys) the 
text covered up by a child frame.

>> There's also a major thing where I can click on the child frame in the
>> tty and then my cursor is actually in the child frame and I can just
>> alter the text there. That behavior is inconsistent with the behavior
>> on graphical displays and something I can investigate.
> 
> Could you please give a recipe? I don't understand yet what you mean.
> Or, if you want to investigate, please do :-). Please don't hesitate to
> ask me any question. At least I still remember more stuff about this
> than about igc :-).

This is from me using the mouse while in xterm and clicking on the child 
frame.  Or just hovering over the child frame with 
mouse-autoselect-window set to t.  I'm guessing this is just due to the 
frame parameter no-accept-focus not being implemented.  Since this is a 
common path for posframe, I think it could be important to implement. 
Such an implementation probably needs to alter xt-mouse.el's generation 
of select-window events.

To reproduce:

(xterm-mouse-mode 1)
(posframe-show "*scratch*" :poshandler 
'posframe-poshandler-frame-center)
Click on the posframe.

   -- MJF



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-12  5:11             ` Jared Finder
@ 2024-12-12  6:20               ` Gerd Möllmann
  2024-12-12  6:48                 ` Gerd Möllmann
  2024-12-12  6:30               ` Eli Zaretskii
  1 sibling, 1 reply; 80+ messages in thread
From: Gerd Möllmann @ 2024-12-12  6:20 UTC (permalink / raw)
  To: Jared Finder; +Cc: Eli Zaretskii, emacs-devel, rudalics

Jared Finder <jared@finder.org> writes:

> On 2024-12-11 02:59, Gerd Möllmann wrote:
>> Jared Finder <jared@finder.org> writes:
>> 
>>> Spent time just playing with child frames to get familiar with things.
>>> A few bugs I noticed:
>> Nice!
>> 
>>> There's something in the C code that makes this branch not work with
>>> the mini-frame package
>>> (https://github.com/muffinmad/emacs-mini-frame). The package works
>>> fine in graphical mode.
>> The page says that the package is using mini-buffer-only frames,
>> which
>> are not implemented, because I wasn't interested. See the #if 0
>> make_terminal_frame.
>
> I think it would be good for unimplemented features to communicate
> that state to the user so users know clearly what is going on.  Right
> now the error a user sees is "Can’t change the ‘minibuffer’ parameter
> of this frame". Wouldn't it be better to have make-terminal-frame (a
> brand new function with no existing clients to support) error with
> something like "Minibuffer only frames are not supported in
> terminals"?
>
> This also would avoid the bug that currently a minibuffer-only child
> frame gets left around which looks weird and confusing.

Sure, we could do that, or someone implements minibuffer-only frames,
someone knowing how these work and are used on GUI. Or IOW, I feel
things are still a bit in undecided state, except on my side.

> Are there any other intentionally unimplemented features?  It'd be
> nice to have similarly clean errors to inform users.

Open things should all be marked them all with FIXME/tty. Frame
re-stacking (tty-frame-restack) is an example, or specifying frame
borders (what a WM would do).

>>> I'm using posframe for iteration. There's a minor thing where the
>>> cursor position is inconsistent when at the end of a line with a
>>> posframe on it in the tty. Maybe that's intentional? It's kinda
>>> confusing to me.
>> Could you please describe in detail what you are doing? I haven't
>> understood yet. Posframe and Corfu are BTW the reason I added the
>> child frames.
>> Child frames in the more general sense was not so interesting.
>> Always
>> talking about me personally of course. I think I talked with Martin
>> about adding stuff as needed if users really want it, or something, and
>> ISTR he agreed. But Martin can speak for himself :-).
>
> This was from me moving the cursor around (just using arrow keys) the
> text covered up by a child frame.

So you have a child frame open covering a line of text in the parent
frame. And then move the cursor in the parent through obscured line? And
what do you mean with the cursor position being inconsistent?

(That would BTW be a child frame use-case which is not so interesting
for me personally, and something GNU has to decide if it's
important/essential or whatever. When is write GNU I mean whoever
decides that at the end.)

>>> There's also a major thing where I can click on the child frame in the
>>> tty and then my cursor is actually in the child frame and I can just
>>> alter the text there. That behavior is inconsistent with the behavior
>>> on graphical displays and something I can investigate.
>> Could you please give a recipe? I don't understand yet what you
>> mean.
>> Or, if you want to investigate, please do :-). Please don't hesitate to
>> ask me any question. At least I still remember more stuff about this
>> than about igc :-).
>
> This is from me using the mouse while in xterm and clicking on the
> child frame.  Or just hovering over the child frame with
> mouse-autoselect-window set to t.  I'm guessing this is just due to
> the frame parameter no-accept-focus not being implemented.  Since this
> is a common path for posframe, I think it could be important to
> implement. Such an implementation probably needs to alter
> xt-mouse.el's generation of select-window events.
>
> To reproduce:
>
> (xterm-mouse-mode 1)
> (posframe-show "*scratch*" :poshandler
> 'posframe-poshandler-frame-center)
> Click on the posframe.
>
>   -- MJF

Could be no-accept-focus, I don't know. If so, and IIRC how that words,
this is another case where not having a window manager comes into play,
and one would have to decide If one wants to implement part of the
functionality of a WM in Emacs on ttys.

(One WM feature I've added because I think one cannot do without is
frame decoration/border. With the caveat that I could not get things to
work by using border-width and so on, with the result that git reset
--hard in a rage in the end (which doesn't happen often), and I don't
think I'll try again soon :-))



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-12  5:11             ` Jared Finder
  2024-12-12  6:20               ` Gerd Möllmann
@ 2024-12-12  6:30               ` Eli Zaretskii
  2024-12-12  7:04                 ` Gerd Möllmann
  1 sibling, 1 reply; 80+ messages in thread
From: Eli Zaretskii @ 2024-12-12  6:30 UTC (permalink / raw)
  To: Jared Finder; +Cc: gerd.moellmann, emacs-devel, rudalics

> Date: Thu, 12 Dec 2024 00:11:01 -0500
> From: Jared Finder <jared@finder.org>
> Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org, rudalics@gmx.at
> 
> I think it would be good for unimplemented features to communicate that 
> state to the user so users know clearly what is going on.  Right now the 
> error a user sees is "Can’t change the ‘minibuffer’ parameter of this 
> frame". Wouldn't it be better to have make-terminal-frame (a brand new 
> function with no existing clients to support) error with something like 
> "Minibuffer only frames are not supported in terminals"?

I think minibuffer-only frames _are_ implemented on TTYs (albeit not
very useful there).  They are not implemented as child frames, I
think.

But yes, emitting an explicit error message about something not
implemented would be definitely better.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-12  6:20               ` Gerd Möllmann
@ 2024-12-12  6:48                 ` Gerd Möllmann
  0 siblings, 0 replies; 80+ messages in thread
From: Gerd Möllmann @ 2024-12-12  6:48 UTC (permalink / raw)
  To: Jared Finder; +Cc: Eli Zaretskii, emacs-devel, rudalics

Gerd Möllmann <gerd.moellmann@gmail.com> writes:

>> Are there any other intentionally unimplemented features?  It'd be
>> nice to have similarly clean errors to inform users.

Forgot something:

"Real" tooltip frames are not implemented. At the time I added toolips,
Emacs didn't have child frames, which led to the horrible special
handling of tooltips frames for GUIs.

And the tooltip code is spread and repeated over different GUI source
files.

I couldn't be bothered to untangle this to add tooltips for ttys, 
but there is tty-tip.el which does something very close using child
frames. Just use tty-tip-mode and move the mouse over mode-line items,
for example. Or over flymake errors, and so on.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-12  6:30               ` Eli Zaretskii
@ 2024-12-12  7:04                 ` Gerd Möllmann
  2024-12-18  5:35                   ` Jared Finder
  0 siblings, 1 reply; 80+ messages in thread
From: Gerd Möllmann @ 2024-12-12  7:04 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Jared Finder, emacs-devel, rudalics

Eli Zaretskii <eliz@gnu.org> writes:

>> Date: Thu, 12 Dec 2024 00:11:01 -0500
>> From: Jared Finder <jared@finder.org>
>> Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org, rudalics@gmx.at
>> 
>> I think it would be good for unimplemented features to communicate that 
>> state to the user so users know clearly what is going on.  Right now the 
>> error a user sees is "Can’t change the ‘minibuffer’ parameter of this 
>> frame". Wouldn't it be better to have make-terminal-frame (a brand new 
>> function with no existing clients to support) error with something like 
>> "Minibuffer only frames are not supported in terminals"?
>
> I think minibuffer-only frames _are_ implemented on TTYs (albeit not
> very useful there).  They are not implemented as child frames, I
> think.
>
> But yes, emitting an explicit error message about something not
> implemented would be definitely better.

Pushed something like that to the branch.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-12  7:04                 ` Gerd Möllmann
@ 2024-12-18  5:35                   ` Jared Finder
  2024-12-18  6:25                     ` Gerd Möllmann
  2024-12-18 13:54                     ` Eli Zaretskii
  0 siblings, 2 replies; 80+ messages in thread
From: Jared Finder @ 2024-12-18  5:35 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Eli Zaretskii, emacs-devel, rudalics

On 2024-12-11 23:04, Gerd Möllmann wrote:
> Eli Zaretskii <eliz@gnu.org> writes:
> 
>>> Date: Thu, 12 Dec 2024 00:11:01 -0500
>>> From: Jared Finder <jared@finder.org>
>>> Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org, 
>>> rudalics@gmx.at
>>> 
>>> I think it would be good for unimplemented features to communicate 
>>> that
>>> state to the user so users know clearly what is going on.  Right now 
>>> the
>>> error a user sees is "Can’t change the ‘minibuffer’ parameter of this
>>> frame". Wouldn't it be better to have make-terminal-frame (a brand 
>>> new
>>> function with no existing clients to support) error with something 
>>> like
>>> "Minibuffer only frames are not supported in terminals"?
>> 
>> I think minibuffer-only frames _are_ implemented on TTYs (albeit not
>> very useful there).  They are not implemented as child frames, I
>> think.
>> 
>> But yes, emitting an explicit error message about something not
>> implemented would be definitely better.
> 
> Pushed something like that to the branch.

Thanks.

Things otherwise seem fine with tty child frames. There's certainly 
oddness with mouse interaction, but it's not fundamentally broken in any 
way, just more things that don't work. In particular:

With xterm-mouse, as I highlighted earlier, I can select the child frame 
even if it is set as not selectable. Once a window in a child frame is 
selected, I can type there normally.

With gpm mouse, I have the opposite problem. I can never select the 
child frame and in fact the mouse behaves as if the child frame isn't 
there. Clicking and tooltip text both pay no attention to the child 
frame and just act on whatever is behind the child frame.

For both of these, I couldn't get mouse-face or clicking to work on 
child frames. I was doing the following:

(setq button (buttonize "[Click me]" (lambda (&rest _) (message 
"Clicked!"))))
(posframe-show " *buffer*" :string (concat "A\n" button "\nB"))

The posframe would show, but the mouse can't interact with the 
buttonized text. This may be a limitation of posframe though, it also 
didn't work in graphical mode.

That's really it. I don't see any major issues with child frames. As 
long as we're ok with saying that mouse support is not mature, it seems 
fine to me.

   -- MJF



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-18  5:35                   ` Jared Finder
@ 2024-12-18  6:25                     ` Gerd Möllmann
  2024-12-18 13:54                     ` Eli Zaretskii
  1 sibling, 0 replies; 80+ messages in thread
From: Gerd Möllmann @ 2024-12-18  6:25 UTC (permalink / raw)
  To: Jared Finder; +Cc: Eli Zaretskii, emacs-devel, rudalics

Jared Finder <jared@finder.org> writes:

> On 2024-12-11 23:04, Gerd Möllmann wrote:
>> Eli Zaretskii <eliz@gnu.org> writes:
>> 
>>>> Date: Thu, 12 Dec 2024 00:11:01 -0500
>>>> From: Jared Finder <jared@finder.org>
>>>> Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org,
>>>> rudalics@gmx.at
>>>> I think it would be good for unimplemented features to communicate
>>>> that
>>>> state to the user so users know clearly what is going on.  Right
>>>> now the
>>>> error a user sees is "Can’t change the ‘minibuffer’ parameter of this
>>>> frame". Wouldn't it be better to have make-terminal-frame (a brand
>>>> new
>>>> function with no existing clients to support) error with something
>>>> like
>>>> "Minibuffer only frames are not supported in terminals"?
>>> I think minibuffer-only frames _are_ implemented on TTYs (albeit
>>> not
>>> very useful there).  They are not implemented as child frames, I
>>> think.
>>> But yes, emitting an explicit error message about something not
>>> implemented would be definitely better.
>> Pushed something like that to the branch.
>
> Thanks.
>
> Things otherwise seem fine with tty child frames. There's certainly
> oddness with mouse interaction, but it's not fundamentally broken in
> any way, just more things that don't work. In particular:
>
> With xterm-mouse, as I highlighted earlier, I can select the child
> frame even if it is set as not selectable. Once a window in a child
> frame is selected, I can type there normally.
>
> With gpm mouse, I have the opposite problem. I can never select the
> child frame and in fact the mouse behaves as if the child frame isn't
> there. Clicking and tooltip text both pay no attention to the child
> frame and just act on whatever is behind the child frame.
>
> For both of these, I couldn't get mouse-face or clicking to work on
> child frames. I was doing the following:
>
> (setq button (buttonize "[Click me]" (lambda (&rest _) (message
> "Clicked!"))))
> (posframe-show " *buffer*" :string (concat "A\n" button "\nB"))
>
> The posframe would show, but the mouse can't interact with the
> buttonized text. This may be a limitation of posframe though, it also
> didn't work in graphical mode.
>
> That's really it. I don't see any major issues with child frames. As
> long as we're ok with saying that mouse support is not mature, it
> seems fine to me.
>
>   -- MJF

Hi Jared, thanks for testing!

And what you write is certainly true - if one wants to have child frames
like on GUI, with all bells and whistles, there are probably a lot of
things that await implementation :-).

WRT GPM: Sounds to me like this is because GPM hasn't been changes to
act analogous to xt-mouse. I think there only two commits to xt-mouse.el
in the branch. It's not much, if you look at the diffs, basically only

  - determine the frame F under (x, y) as reported by the terminal
  
  - Give Emacs (F, x', y'), where x' and y' are the coordinates
    relative to F

That was all I needed, in principle, at least for xterm-mouse, to make
things work. Don't know if that also fits for GPm.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-18  5:35                   ` Jared Finder
  2024-12-18  6:25                     ` Gerd Möllmann
@ 2024-12-18 13:54                     ` Eli Zaretskii
  2024-12-18 16:01                       ` Gerd Möllmann
                                         ` (2 more replies)
  1 sibling, 3 replies; 80+ messages in thread
From: Eli Zaretskii @ 2024-12-18 13:54 UTC (permalink / raw)
  To: Jared Finder, Stefan Kangas, Andrea Corallo
  Cc: gerd.moellmann, emacs-devel, rudalics

> Date: Tue, 17 Dec 2024 21:35:35 -0800
> From: Jared Finder <jared@finder.org>
> Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org, rudalics@gmx.at
> 
> Things otherwise seem fine with tty child frames. There's certainly 
> oddness with mouse interaction, but it's not fundamentally broken in any 
> way, just more things that don't work. In particular:
> 
> With xterm-mouse, as I highlighted earlier, I can select the child frame 
> even if it is set as not selectable. Once a window in a child frame is 
> selected, I can type there normally.
> 
> With gpm mouse, I have the opposite problem. I can never select the 
> child frame and in fact the mouse behaves as if the child frame isn't 
> there. Clicking and tooltip text both pay no attention to the child 
> frame and just act on whatever is behind the child frame.
> 
> For both of these, I couldn't get mouse-face or clicking to work on 
> child frames. I was doing the following:
> 
> (setq button (buttonize "[Click me]" (lambda (&rest _) (message 
> "Clicked!"))))
> (posframe-show " *buffer*" :string (concat "A\n" button "\nB"))
> 
> The posframe would show, but the mouse can't interact with the 
> buttonized text. This may be a limitation of posframe though, it also 
> didn't work in graphical mode.
> 
> That's really it. I don't see any major issues with child frames. As 
> long as we're ok with saying that mouse support is not mature, it seems 
> fine to me.

If we don't see immediate ways of fixing some of these issues, I think
it should be okay to land this on master, if Stefan and Andrea agree.

Thanks.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-18 13:54                     ` Eli Zaretskii
@ 2024-12-18 16:01                       ` Gerd Möllmann
  2024-12-18 16:29                         ` Eli Zaretskii
  2024-12-18 21:06                       ` Stefan Kangas
  2024-12-19  8:00                       ` Andrea Corallo
  2 siblings, 1 reply; 80+ messages in thread
From: Gerd Möllmann @ 2024-12-18 16:01 UTC (permalink / raw)
  To: Eli Zaretskii
  Cc: Jared Finder, Stefan Kangas, Andrea Corallo, emacs-devel,
	rudalics

Eli Zaretskii <eliz@gnu.org> writes:

> If we don't see immediate ways of fixing some of these issues, I think
> it should be okay to land this on master, if Stefan and Andrea agree.

In preparation, can I ask a few questions wrt landing?

What I seem to remember is that it entails a normal merge from
scratch/tty-child-frames to master. No squashing, no interactive
rebasing to fix commit messages first, or anything else complicated.

The merge commit message should have a certain form, IIRC. It should
probably contain some introductory text like "This adds tty child
frames...", and then ChangeLog-style entries.

For a new file it's basically sufficient to say "New file".

The rest gets a bit complicated, and I'm unsure how much detail is
required. Say I've changed function parameters of an existing function
plus I'm calling other functions whose API has changed plus added new
code. Does that all have to appear in ChangeLog-style? That could take
some time to produce.

Another question: I can produce a list of commit IDs for changes that
happened on the branch. Do we perhaps have some tool that produces these
change log entries automatically? Something akin to
magit-add-change-log-entry maybe?



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-18 16:01                       ` Gerd Möllmann
@ 2024-12-18 16:29                         ` Eli Zaretskii
  2024-12-18 16:39                           ` Gerd Möllmann
  0 siblings, 1 reply; 80+ messages in thread
From: Eli Zaretskii @ 2024-12-18 16:29 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: jared, stefankangas, acorallo, emacs-devel, rudalics

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: Jared Finder <jared@finder.org>,  Stefan Kangas
>  <stefankangas@gmail.com>,  Andrea Corallo <acorallo@gnu.org>,
>   emacs-devel@gnu.org,  rudalics@gmx.at
> Date: Wed, 18 Dec 2024 17:01:35 +0100
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > If we don't see immediate ways of fixing some of these issues, I think
> > it should be okay to land this on master, if Stefan and Andrea agree.
> 
> In preparation, can I ask a few questions wrt landing?
> 
> What I seem to remember is that it entails a normal merge from
> scratch/tty-child-frames to master. No squashing, no interactive
> rebasing to fix commit messages first, or anything else complicated.

Yes, that's the preference.

> The merge commit message should have a certain form, IIRC. It should
> probably contain some introductory text like "This adds tty child
> frames...", and then ChangeLog-style entries.
> 
> For a new file it's basically sufficient to say "New file".
> 
> The rest gets a bit complicated, and I'm unsure how much detail is
> required. Say I've changed function parameters of an existing function
> plus I'm calling other functions whose API has changed plus added new
> code. Does that all have to appear in ChangeLog-style? That could take
> some time to produce.

Preferably yes.  However, for changing the function's signature you
can say something like

  * foo.c (bar): Accept additional argument FOOBAR.  All callers
    changed.

(This is not different from the rules for any commit, not only
merge-commit.)

> Another question: I can produce a list of commit IDs for changes that
> happened on the branch. Do we perhaps have some tool that produces these
> change log entries automatically? Something akin to
> magit-add-change-log-entry maybe?

How would a tool know what to say in the description of the change?
Changes on feature branches usually don't have informative log
messages, they are usually minimal ("Fix crash in foobar" or
somesuch).

What I usually do is produce diffs for the merge, then use "C-x 4 a"
to generate the file/function names, and add a description.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-18 16:29                         ` Eli Zaretskii
@ 2024-12-18 16:39                           ` Gerd Möllmann
  2024-12-18 17:02                             ` Eli Zaretskii
  0 siblings, 1 reply; 80+ messages in thread
From: Gerd Möllmann @ 2024-12-18 16:39 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: jared, stefankangas, acorallo, emacs-devel, rudalics

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Gerd Möllmann <gerd.moellmann@gmail.com>
>> Cc: Jared Finder <jared@finder.org>,  Stefan Kangas
>>  <stefankangas@gmail.com>,  Andrea Corallo <acorallo@gnu.org>,
>>   emacs-devel@gnu.org,  rudalics@gmx.at
>> Date: Wed, 18 Dec 2024 17:01:35 +0100
>> 
>> Eli Zaretskii <eliz@gnu.org> writes:
>> 
>> > If we don't see immediate ways of fixing some of these issues, I think
>> > it should be okay to land this on master, if Stefan and Andrea agree.
>> 
>> In preparation, can I ask a few questions wrt landing?
>> 
>> What I seem to remember is that it entails a normal merge from
>> scratch/tty-child-frames to master. No squashing, no interactive
>> rebasing to fix commit messages first, or anything else complicated.
>
> Yes, that's the preference.
>
>> The merge commit message should have a certain form, IIRC. It should
>> probably contain some introductory text like "This adds tty child
>> frames...", and then ChangeLog-style entries.
>> 
>> For a new file it's basically sufficient to say "New file".
>> 
>> The rest gets a bit complicated, and I'm unsure how much detail is
>> required. Say I've changed function parameters of an existing function
>> plus I'm calling other functions whose API has changed plus added new
>> code. Does that all have to appear in ChangeLog-style? That could take
>> some time to produce.
>
> Preferably yes.  However, for changing the function's signature you
> can say something like
>
>   * foo.c (bar): Accept additional argument FOOBAR.  All callers
>     changed.
>
> (This is not different from the rules for any commit, not only
> merge-commit.)

Ok, thanks.

>
>> Another question: I can produce a list of commit IDs for changes that
>> happened on the branch. Do we perhaps have some tool that produces these
>> change log entries automatically? Something akin to
>> magit-add-change-log-entry maybe?
>
> How would a tool know what to say in the description of the change?
> Changes on feature branches usually don't have informative log
> messages, they are usually minimal ("Fix crash in foobar" or
> somesuch).

That's right, magit-add-change-log-entry also only generates the
skeleton. Didn't think of that :-).

> What I usually do is produce diffs for the merge, then use "C-x 4 a"
> to generate the file/function names, and add a description.

The diff I have, so I guess I'll give it a try, when the others agree.
Will take a bit, probably. Should I post the result when I have it?



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-18 16:39                           ` Gerd Möllmann
@ 2024-12-18 17:02                             ` Eli Zaretskii
  2024-12-18 17:22                               ` Gerd Möllmann
  0 siblings, 1 reply; 80+ messages in thread
From: Eli Zaretskii @ 2024-12-18 17:02 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: jared, stefankangas, acorallo, emacs-devel, rudalics

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: jared@finder.org,  stefankangas@gmail.com,  acorallo@gnu.org,
>   emacs-devel@gnu.org,  rudalics@gmx.at
> Date: Wed, 18 Dec 2024 17:39:07 +0100
> 
> > What I usually do is produce diffs for the merge, then use "C-x 4 a"
> > to generate the file/function names, and add a description.
> 
> The diff I have, so I guess I'll give it a try, when the others agree.
> Will take a bit, probably. Should I post the result when I have it?

Feel free, it will probably flash out a few more issues.  I actually
suggest to post some small portion first, to avoid the situation where
you do all of the job, only to discover that something's amiss or you
labored more than you had to.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-18 17:02                             ` Eli Zaretskii
@ 2024-12-18 17:22                               ` Gerd Möllmann
  2024-12-19  5:17                                 ` Jared Finder
  0 siblings, 1 reply; 80+ messages in thread
From: Gerd Möllmann @ 2024-12-18 17:22 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: jared, stefankangas, acorallo, emacs-devel, rudalics

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Gerd Möllmann <gerd.moellmann@gmail.com>
>> Cc: jared@finder.org,  stefankangas@gmail.com,  acorallo@gnu.org,
>>   emacs-devel@gnu.org,  rudalics@gmx.at
>> Date: Wed, 18 Dec 2024 17:39:07 +0100
>> 
>> > What I usually do is produce diffs for the merge, then use "C-x 4 a"
>> > to generate the file/function names, and add a description.
>> 
>> The diff I have, so I guess I'll give it a try, when the others agree.
>> Will take a bit, probably. Should I post the result when I have it?
>
> Feel free, it will probably flash out a few more issues.  I actually
> suggest to post some small portion first, to avoid the situation where
> you do all of the job, only to discover that something's amiss or you
> labored more than you had to.

Thanks, will do that.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-18 13:54                     ` Eli Zaretskii
  2024-12-18 16:01                       ` Gerd Möllmann
@ 2024-12-18 21:06                       ` Stefan Kangas
  2024-12-19  8:00                       ` Andrea Corallo
  2 siblings, 0 replies; 80+ messages in thread
From: Stefan Kangas @ 2024-12-18 21:06 UTC (permalink / raw)
  To: Eli Zaretskii, Jared Finder, Andrea Corallo
  Cc: gerd.moellmann, emacs-devel, rudalics

Eli Zaretskii <eliz@gnu.org> writes:

> If we don't see immediate ways of fixing some of these issues, I think
> it should be okay to land this on master, if Stefan and Andrea agree.

Sounds good to me.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-18 17:22                               ` Gerd Möllmann
@ 2024-12-19  5:17                                 ` Jared Finder
  2024-12-19  5:30                                   ` Gerd Möllmann
  0 siblings, 1 reply; 80+ messages in thread
From: Jared Finder @ 2024-12-19  5:17 UTC (permalink / raw)
  To: Gerd Möllmann
  Cc: Eli Zaretskii, stefankangas, acorallo, emacs-devel, rudalics

On 2024-12-18 09:22, Gerd Möllmann wrote:
> Eli Zaretskii <eliz@gnu.org> writes:
> 
>>> From: Gerd Möllmann <gerd.moellmann@gmail.com>
>>> Cc: jared@finder.org,  stefankangas@gmail.com,  acorallo@gnu.org,
>>>   emacs-devel@gnu.org,  rudalics@gmx.at
>>> Date: Wed, 18 Dec 2024 17:39:07 +0100
>>> 
>>> > What I usually do is produce diffs for the merge, then use "C-x 4 a"
>>> > to generate the file/function names, and add a description.
>>> 
>>> The diff I have, so I guess I'll give it a try, when the others 
>>> agree.
>>> Will take a bit, probably. Should I post the result when I have it?
>> 
>> Feel free, it will probably flash out a few more issues.  I actually
>> suggest to post some small portion first, to avoid the situation where
>> you do all of the job, only to discover that something's amiss or you
>> labored more than you had to.
> 
> Thanks, will do that.

I'll also spend a bit of time to get GPM's mouse input to make the same 
transformations that Gerd added to xt-mouse.el.  It looks likely very 
straightforward to do.

   -- MJF



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-19  5:17                                 ` Jared Finder
@ 2024-12-19  5:30                                   ` Gerd Möllmann
  2024-12-19  7:44                                     ` Gerd Möllmann
  2024-12-19  8:36                                     ` Eli Zaretskii
  0 siblings, 2 replies; 80+ messages in thread
From: Gerd Möllmann @ 2024-12-19  5:30 UTC (permalink / raw)
  To: Jared Finder; +Cc: Eli Zaretskii, stefankangas, acorallo, emacs-devel, rudalics

[-- Attachment #1: Type: text/plain, Size: 1719 bytes --]

Jared Finder <jared@finder.org> writes:

> On 2024-12-18 09:22, Gerd Möllmann wrote:
>> Eli Zaretskii <eliz@gnu.org> writes:
>> 
>>>> From: Gerd Möllmann <gerd.moellmann@gmail.com>
>>>> Cc: jared@finder.org,  stefankangas@gmail.com,  acorallo@gnu.org,
>>>>   emacs-devel@gnu.org,  rudalics@gmx.at
>>>> Date: Wed, 18 Dec 2024 17:39:07 +0100
>>>>  > What I usually do is produce diffs for the merge, then use "C-x
>>>> 4 a"
>>>> > to generate the file/function names, and add a description.
>>>> The diff I have, so I guess I'll give it a try, when the others
>>>> agree.
>>>> Will take a bit, probably. Should I post the result when I have it?
>>> Feel free, it will probably flash out a few more issues.  I
>>> actually
>>> suggest to post some small portion first, to avoid the situation where
>>> you do all of the job, only to discover that something's amiss or you
>>> labored more than you had to.
>> Thanks, will do that.
>
> I'll also spend a bit of time to get GPM's mouse input to make the
> same transformations that Gerd added to xt-mouse.el.  It looks likely
> very straightforward to do.
>
>   -- MJF

Thanks!

And attached is what I came up today, with the help of a lot of coffee.
The changelog is a page worth of changelog entries, the diff is from
what I created these. As introductory text I'd use

  This changeset adds support for child frames on ttys.

  The redisplay part is complete.  The frame-handling part supports
  use-cases like Posframe, Corfu, and child frames acting like tooltips.
  Other use-cases of child frames are not currently supported.  In
  particular, trying to create minibuffer-only child frames on ttys will
  signal an error.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: diff --]
[-- Type: text/x-patch, Size: 164804 bytes --]

27 files changed, 2126 insertions(+), 687 deletions(-)
.gitignore         |    1 -
lisp/disp-table.el |   54 ++-
lisp/frame.el      |   46 +-
lisp/paren.el      |    4 +-
lisp/tty-tip.el    |  208 +++++++++
lisp/xt-mouse.el   |   24 +-
src/alloc.c        |    8 +-
src/chartab.c      |    2 -
src/dispextern.h   |   40 +-
src/dispnew.c      | 1321 ++++++++++++++++++++++++++++++++++++++--------------
src/disptab.h      |   12 +-
src/frame.c        |  508 +++++++++++++++-----
src/frame.h        |   83 ++--
src/keyboard.c     |    2 +-
src/minibuf.c      |    6 +-
src/nsfns.m        |    4 +-
src/nsterm.m       |    4 +-
src/scroll.c       |    4 +-
src/term.c         |  303 ++++++++++--
src/termhooks.h    |    3 +
src/terminal.c     |    6 +-
src/treesit.c      |    2 +-
src/w32console.c   |   15 +-
src/w32inevt.c     |   11 +-
src/w32term.c      |   25 +-
src/xdisp.c        |  116 +++--
src/xfaces.c       |    1 -

modified   .gitignore
@@ -323,7 +323,6 @@ gnustmp*
 *~
 \#*\#
 ChangeLog
-[0-9]*.patch
 [0-9]*.txt
 /vc-dwim-log-*
 
modified   lisp/disp-table.el
@@ -28,7 +28,7 @@
 
 ;;; Code:
 
-(put 'display-table 'char-table-extra-slots 6)
+(put 'display-table 'char-table-extra-slots 12)
 
 ;;;###autoload
 (defun make-display-table ()
@@ -46,13 +46,21 @@ make-display-table
 (put 'control 'display-table-slot 3)
 (put 'selective-display 'display-table-slot 4)
 (put 'vertical-border 'display-table-slot 5)
+(put 'box-vertical 'display-table-slot 6)
+(put 'box-horizontal 'display-table-slot 7)
+(put 'box-down-right 'display-table-slot 8)
+(put 'box-down-left 'display-table-slot 9)
+(put 'box-up-right 'display-table-slot 10)
+(put 'box-up-left 'display-table-slot 11)
 
 ;;;###autoload
 (defun display-table-slot (display-table slot)
   "Return the value of the extra slot in DISPLAY-TABLE named SLOT.
-SLOT may be a number from 0 to 5 inclusive, or a slot name (symbol).
+SLOT may be a number from 0 to 11 inclusive, or a slot name (symbol).
 Valid symbols are `truncation', `wrap', `escape', `control',
-`selective-display', and `vertical-border'."
+`selective-display', `vertical-border', `box-vertical',
+`box-horizontal', `box-down-right', `box-down-left', `box-up-right',
+and `box-up-left'."
   (let ((slot-number
 	 (if (numberp slot) slot
 	   (or (get slot 'display-table-slot)
@@ -62,9 +70,11 @@ display-table-slot
 ;;;###autoload
 (defun set-display-table-slot (display-table slot value)
   "Set the value of the extra slot in DISPLAY-TABLE named SLOT to VALUE.
-SLOT may be a number from 0 to 5 inclusive, or a name (symbol).
+SLOT may be a number from 0 to 11 inclusive, or a name (symbol).
 Valid symbols are `truncation', `wrap', `escape', `control',
-`selective-display', and `vertical-border'."
+`selective-display',  `vertical-border', `box-vertical',
+`box-horizontal', `box-down-right', `box-down-left', `box-up-right',
+and `box-up-left'."
   (let ((slot-number
 	 (if (numberp slot) slot
 	   (or (get slot 'display-table-slot)
@@ -87,6 +97,18 @@ describe-display-table
     (prin1 (display-table-slot dt 'selective-display))
     (princ "\nVertical window border glyph: ")
     (prin1 (display-table-slot dt 'vertical-border))
+    (princ "\nBox vertical line glyph: ")
+    (prin1 (display-table-slot dt 'box-vertical))
+    (princ "\nBox horizonal line glyph: ")
+    (prin1 (display-table-slot dt 'box-horizontal))
+    (princ "\nBox upper left corner glyph: ")
+    (prin1 (display-table-slot dt 'box-down-right))
+    (princ "\nBox upper right corner glyph: ")
+    (prin1 (display-table-slot dt 'box-down-left))
+    (princ "\nBox lower left corner glyph: ")
+    (prin1 (display-table-slot dt 'box-up-right))
+    (princ "\nBox lower right corner glyph: ")
+    (prin1 (display-table-slot dt 'box-up-left))
     (princ "\nCharacter display glyph sequences:\n")
     (with-current-buffer standard-output
       (let ((vector (make-vector 256 nil))
@@ -126,6 +148,28 @@ describe-current-display-table
 	(describe-display-table disptab)
       (message "No display table"))))
 
+;;;###autoload
+(defun standard-display-unicode-special-glyphs ()
+  "Display some glyps using Unicode characters.
+The glyphs being changed by this function are `vertical-border',
+`box-vertical', `box-horizontal', `box-down-right', `box-down-left',
+`box-up-right', and `box-up-left'."
+  (interactive)
+  (set-display-table-slot standard-display-table
+			  'vertical-border (make-glyph-code #x2502))
+  (set-display-table-slot standard-display-table
+			  'box-vertical (make-glyph-code #x2502))
+  (set-display-table-slot standard-display-table
+			  'box-horizontal (make-glyph-code #x2500))
+  (set-display-table-slot standard-display-table
+			  'box-down-right (make-glyph-code #x250c))
+  (set-display-table-slot standard-display-table
+			  'box-down-left (make-glyph-code #x2510))
+  (set-display-table-slot standard-display-table
+			  'box-up-right (make-glyph-code #x2514))
+  (set-display-table-slot standard-display-table
+			  'box-up-left (make-glyph-code #x2518)))
+
 ;;;###autoload
 (defun standard-display-8bit (l h)
   "Display characters representing raw bytes in the range L to H literally.
modified   lisp/frame.el
@@ -1495,6 +1495,13 @@ frame-outer-height
   (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 @@ frame-current-scroll-bars
 (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 @@ frame-geometry
      ((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 @@ frame--size-history
 (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-edges
      ((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 @@ frame-monitor-workarea
 ;; (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 @@ frame-list-z-order
      ((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 @@ frame-list-z-order
 (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.
@@ -2158,7 +2154,9 @@ frame-restack
          ((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 +2309,7 @@ display-screens
       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 +2327,10 @@ display-pixel-height
      ((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 +2348,7 @@ display-pixel-width
      ((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.
modified   lisp/paren.el
@@ -522,9 +522,7 @@ show-paren-function
                               openparen))
                     (message-log-max nil))
                 (cond
-                 ((and
-                   (eq show-paren-context-when-offscreen 'child-frame)
-                   (display-graphic-p))
+                 ((eq show-paren-context-when-offscreen 'child-frame)
                   (show-paren--show-context-in-child-frame context))
                  ((eq show-paren-context-when-offscreen 'overlay)
                   (show-paren--show-context-in-overlay context))
new file   lisp/tty-tip.el
@@ -0,0 +1,208 @@
+;;; -*- lexical-binding: t; symbol-packages: t; -*-
+;;; tty-tip.el --- Display help in kind of tooltips on ttys
+
+;; Copyright (C) 2024 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This uses tty child frames to display help which looks and feels much
+;; like using tooltips (but they really aren't).
+
+;; Use `tty-tip-mode' to activate or toggle this feature.
+;;
+;; You can customize face `tooltip', `tooltip-short-delay',
+;; `tooltip-delay', `tooltip-recent-seconds'.
+
+(require 'tooltip)
+
+(defvar tty-tip--frame nil)
+
+(defun tty-tip--make-buffer (text)
+  (with-current-buffer
+      (get-buffer-create " *tty-tip*")
+    ;; Redirect focus to parent.
+    (add-hook 'pre-command-hook #'tty-tip--delete-frame nil t)
+    ;; Use an empty keymap.
+    (use-local-map (make-keymap))
+    (dolist (var '((mode-line-format . nil)
+                   (header-line-format . nil)
+                   (tab-line-format . nil)
+                   (tab-bar-format . nil) ;; Emacs 28 tab-bar-format
+                   (frame-title-format . "")
+                   (truncate-lines . t)
+                   (cursor-in-non-selected-windows . nil)
+                   (cursor-type . nil)
+                   (show-trailing-whitespace . nil)
+                   (display-line-numbers . nil)
+                   (left-fringe-width . nil)
+                   (right-fringe-width . nil)
+                   (left-margin-width . 0)
+                   (right-margin-width . 0)
+                   (fringes-outside-margins . 0)
+                   (buffer-read-only . t)))
+      (set (make-local-variable (car var)) (cdr var)))
+    (let ((inhibit-modification-hooks t)
+          (inhibit-read-only t))
+      (erase-buffer)
+      (insert text)
+      (goto-char (point-min)))
+    (current-buffer)))
+
+(defvar tty-tip-frame-parameters
+  `((visibility . nil)
+    (background-color . "lightyellow")
+    (foreground-color . "black")
+    (width . 0) (height . 0)
+    (min-width . t) (min-height . t)
+    (no-accept-focus . t)
+    (no-focus-on-map . t)
+    (border-width . 0)
+    (child-frame-border-width . 1)
+    (left-fringe . 0)
+    (right-fringe . 0)
+    (vertical-scroll-bars . nil)
+    (horizontal-scroll-bars . nil)
+    (menu-bar-lines . 0)
+    (tool-bar-lines . 0)
+    (tab-bar-lines . 0)
+    (no-other-frame . t)
+    (no-other-window . t)
+    (no-delete-other-windows . t)
+    (unsplittable . t)
+    (undecorated . t)
+    (cursor-type . nil)
+    (no-special-glyphs . t)
+    (desktop-dont-save . t)))
+
+(defun tty-tip--frame-parameters ()
+  (let ((params (copy-sequence tty-tip-frame-parameters))
+        (fg (face-attribute 'tooltip :foreground))
+        (bg (face-attribute 'tooltip :background)))
+    (when (stringp fg)
+      (setf (alist-get 'foreground-color params) fg))
+    (when (stringp bg)
+      (setf (alist-get 'background-color params) bg))
+    params))
+
+(defvar tty-tip--help-message nil)
+(defvar tty-tip--hide-time nil)
+(defvar tty-tip--show-timer nil)
+(defvar tty-tip--hide-timer nil)
+
+(defun tty-tip--delete-frame ()
+  (when tty-tip--frame
+    (when tty-tip--hide-timer
+      (cancel-timer tty-tip--hide-timer)
+      (setq tty-tip--hide-timer nil))
+    (delete-frame tty-tip--frame)
+    (setq tty-tip--frame nil)
+    t))
+
+(defun tty-tip--compute-position ()
+  (let* ((pos (mouse-position))
+         (mouse-x (car (cdr pos)))
+         (mouse-y (cdr (cdr pos)))
+	 (x (+ mouse-x 1))
+	 (y (+ mouse-y 1))
+	 (tip-width (frame-width tty-tip--frame))
+	 (tip-height (frame-height tty-tip--frame))
+	 (tty-width (display-pixel-width))
+	 (tty-height (display-pixel-height)))
+    (when (> (+ x tip-width) tty-width)
+      (setq x (max 0 (- x tip-width 1))))
+    (when (> (+ y tip-height) tty-height)
+      (setq y (max 0 (- y tip-height 1))))
+    (cons x y)))
+
+(defun tty-tip--create-frame (text)
+  (let* ((minibuffer (minibuffer-window (window-frame)))
+         (buffer (tty-tip--make-buffer text))
+         (window-min-height 1)
+         (window-min-width 1)
+         after-make-frame-functions
+	 (text-lines (string-lines text)))
+    (setq tty-tip--frame
+          (make-frame
+           `((parent-frame . ,(car (mouse-position)))
+             (minibuffer . ,minibuffer)
+             ,@(tty-tip--frame-parameters))))
+    (let ((win (frame-root-window tty-tip--frame)))
+      (set-window-buffer win buffer)
+      (set-window-dedicated-p win t)
+      (set-frame-size tty-tip--frame
+                      (apply #'max (mapcar #'string-width text-lines))
+                      (length text-lines))
+      (let* ((pos (tty-tip--compute-position))
+             (x (car pos))
+             (y (cdr pos)))
+	(set-frame-position tty-tip--frame x y))
+      (make-frame-visible tty-tip--frame)
+      (setq tty-tip--hide-timer
+            (run-with-timer tooltip-hide-delay nil
+                            #'tty-tip--delete-frame)))))
+
+(defun tty-tip--delay ()
+  (if (and tty-tip--hide-time
+	   (time-less-p (time-since tty-tip--hide-time)
+			tooltip-recent-seconds))
+      tooltip-short-delay
+    tooltip-delay))
+
+(defun tty-tip--cancel-delayed-tip ()
+  (when tty-tip--show-timer
+    (cancel-timer tty-tip--show-timer)
+    (setq tty-tip--show-timer nil)))
+
+(defun tty-tip--start-delayed-tip ()
+  (setq tty-tip--show-timer
+        (run-with-timer (tty-tip--delay) nil
+                        (lambda ()
+                          (tty-tip--create-frame
+                           tty-tip--help-message)))))
+
+(defun tty-tip--hide (&optional _ignored-arg)
+  (tty-tip--cancel-delayed-tip)
+  (when (tty-tip--delete-frame)
+    (setq tty-tip--hide-time (float-time))))
+
+(defun tty-tip--show-help (msg)
+  (let ((previous-help tty-tip--help-message))
+    (setq tty-tip--help-message msg)
+    (cond ((null msg)
+	   (tty-tip--hide))
+	  ((equal previous-help msg)
+	   nil)
+	  (t
+	   (tty-tip--hide)
+	   (tty-tip--start-delayed-tip)))))
+
+;;;###autoload
+(define-minor-mode tty-tip-mode
+  "Global minor mode for displaying help in tty child frames."
+  :global t :group 'help
+  (unless (display-graphic-p)
+    (cond (tty-tip-mode
+	   (setq show-help-function #'tty-tip--show-help)
+           (add-hook 'pre-command-hook #'tty-tip--hide))
+          (t
+           (setq show-help-function nil)
+           (remove-hook 'pre-command-hook #'tty-tip--hide)))))
+
+(provide 'tty-tip)
+
+;;; End
modified   lisp/xt-mouse.el
@@ -133,7 +133,8 @@ xterm-mouse-translate-1
 
 (defun xterm-mouse--handle-mouse-movement ()
   "Handle mouse motion that was just generated for XTerm mouse."
-  (display--update-for-mouse-movement (terminal-parameter nil 'xterm-mouse-x)
+  (display--update-for-mouse-movement (terminal-parameter nil 'xterm-mouse-frame)
+                                      (terminal-parameter nil 'xterm-mouse-x)
                                       (terminal-parameter nil 'xterm-mouse-y)))
 
 ;; These two variables have been converted to terminal parameters.
@@ -150,10 +151,11 @@ xt-mouse-epoch
 
 (defun xterm-mouse-position-function (pos)
   "Bound to `mouse-position-function' in XTerm mouse mode."
-  (when (terminal-parameter nil 'xterm-mouse-x)
-    (setcdr pos (cons (terminal-parameter nil 'xterm-mouse-x)
-		      (terminal-parameter nil 'xterm-mouse-y))))
-  pos)
+  (if (terminal-parameter nil 'xterm-mouse-x)
+      (cons (terminal-parameter nil 'xterm-mouse-frame)
+            (cons (terminal-parameter nil 'xterm-mouse-x)
+		  (terminal-parameter nil 'xterm-mouse-y)))
+    pos))
 
 (define-obsolete-function-alias 'xterm-mouse-truncate-wrap 'truncate "27.1")
 
@@ -293,7 +295,16 @@ xterm-mouse-event
 			    (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))
@@ -345,6 +356,7 @@ xterm-mouse-event
 
         (set-terminal-parameter nil 'xterm-mouse-x x)
         (set-terminal-parameter nil 'xterm-mouse-y y)
+        (set-terminal-parameter nil 'xterm-mouse-frame frame)
         (setq last-input-event event)))))
 
 ;;;###autoload
modified   src/alloc.c
@@ -6853,9 +6853,11 @@ 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 (STRINGP (glyph->object)
+		    && !string_marked_p (XSTRING (glyph->object)))
+		  mark_object (glyph->object);
+	      }
 	  }
       }
 }
modified   src/chartab.c
@@ -122,8 +122,6 @@ DEFUN ("make-char-table", Fmake_char_table, Smake_char_table, 1, 2, 0,
   else
     {
       CHECK_FIXNAT (n);
-      if (XFIXNUM (n) > 10)
-	args_out_of_range (n, Qnil);
       n_extras = XFIXNUM (n);
     }
 
modified   src/dispextern.h
@@ -482,6 +482,11 @@ #define SET_GLYPH_FROM_GLYPH_CODE(glyph, gc)				\
      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 @@ #define FACE_ID_BITS	20
 
 #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 @@ #define GLYPH_EQUAL_P(X, Y)					\
       && (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 @@ #define GLYPH_EQUAL_P(X, Y)					\
 #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 @@ #define SET_CHAR_GLYPH(GLYPH, CODE, FACE_ID, PADDING_P)	\
 /* 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
@@ -3806,7 +3814,7 @@ #define IMAGE_BACKGROUND_TRANSPARENT(img, f, mask)			      \
 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 *);
@@ -3830,7 +3838,7 @@ #define IMAGE_BACKGROUND_TRANSPARENT(img, f, mask)			      \
 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.  */
 
@@ -3912,6 +3920,16 @@ #define IMAGE_BACKGROUND_TRANSPARENT(img, f, mask)			      \
 
 #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);
+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);
+int max_child_z_order (struct frame *parent);
+
 INLINE_HEADER_END
 
 #endif /* not DISPEXTERN_H_INCLUDED */
modified   src/dispnew.c
@@ -42,6 +42,8 @@ Copyright (C) 1985-1988, 1993-1995, 1997-2024 Free Software Foundation,
 #include "tparam.h"
 #include "xwidget.h"
 #include "pdumper.h"
+#include "disptab.h"
+#include "cm.h"
 
 #ifdef HAVE_ANDROID
 #include "android.h"
@@ -71,7 +73,7 @@ Copyright (C) 1985-1988, 1993-1995, 1997-2024 Free Software Foundation,
 \f
 /* 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 +82,9 @@ Copyright (C) 1985-1988, 1993-1995, 1997-2024 Free Software Foundation,
 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,13 +95,27 @@ Copyright (C) 1985-1988, 1993-1995, 1997-2024 Free Software Foundation,
 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 *);
 static void adjust_frame_glyphs_for_frame_redisplay (struct frame *);
 static void set_window_update_flags (struct window *w, bool on_p);
 
+#if 0 /* Please leave this in as a debugging aid.  */
+static void
+check_rows (struct frame *f)
+{
+  for (int y = 0; y < f->desired_matrix->nrows; ++y)
+    if (MATRIX_ROW_ENABLED_P (f->desired_matrix, y))
+      {
+	struct glyph_row *row = MATRIX_ROW (f->desired_matrix, y);
+	for (int x = 0; x < row->used[TEXT_AREA]; ++x)
+	  eassert (row->glyphs[TEXT_AREA][x].frame != 0);
+      }
+}
+#endif
+
 /* True means last display completed.  False means it was preempted.  */
 
 bool display_completed;
@@ -122,11 +138,6 @@ Copyright (C) 1985-1988, 1993-1995, 1997-2024 Free Software Foundation,
 
 #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 +1189,15 @@ line_hash_code (struct frame *f, struct glyph_row *row)
       while (glyph < end)
 	{
 	  int c = glyph->u.ch;
-	  int face_id = glyph->face_id;
+	  unsigned int face_id = glyph->face_id;
+	  /* A given row of a frame glyph matrix could have glyphs
+	     from more than one frame, if child frames are displayed.
+	     Since face_id of a face depends on the frame (it's an
+	     index into the frame's face cache), we need the hash
+	     value to include something specific to the frame, and we
+	     use the frame cache's address for that purpose.  */
+	  if (glyph->frame && glyph->frame != f)
+	    face_id += (uintptr_t) glyph->frame->face_cache;
 	  if (FRAME_MUST_WRITE_SPACES (f))
 	    c -= SPACEGLYPH;
 	  hash = (((hash << 4) + (hash >> 24)) & 0x0fffffff) + c;
@@ -1213,7 +1232,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 +1240,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 +2577,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 +2619,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
@@ -2616,10 +2636,21 @@ build_frame_matrix_from_leaf_window (struct glyph_matrix *frame_matrix, struct w
 
       if (current_row_p)
 	{
-	  /* Copy window row to frame row.  */
-	  memcpy (frame_row->glyphs[TEXT_AREA] + window_matrix->matrix_x,
-		  window_row->glyphs[0],
-		  window_matrix->matrix_w * sizeof (struct glyph));
+	  /* If the desired glyphs for this row haven't been built,
+	     copy from the corresponding current row, but only if it
+	     is enabled, because ottherwise its contents are invalid.  */
+	  struct glyph *to = frame_row->glyphs[TEXT_AREA] + window_matrix->matrix_x;
+	  struct glyph *from = window_row->glyphs[0];
+	  for (int i = 0; i < window_matrix->matrix_w; ++i)
+	    {
+	      if (window_row->enabled_p)
+		to[i] = from[i];
+	      else
+		{
+		  to[i] = space_glyph;
+		  to[i].frame = f;
+		}
+	    }
 	}
       else
 	{
@@ -2638,7 +2669,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 +2732,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 +2744,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 +2766,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 +2786,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 +2795,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 +2825,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 +2890,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 +2924,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 +3223,10 @@ redraw_frame (struct frame *f)
        future.  */
     SET_FRAME_GARBAGED (f);
 
-  clear_frame (f);
+  /* clear_frame is actually a "clear_terminal", i.e.
+     it clears the entire screen.  */
+  if (!FRAME_PARENT_FRAME (f))
+    clear_frame (f);
   clear_current_matrices (f);
   update_end (f);
   fset_redisplay (f);
@@ -3229,145 +3261,797 @@ DEFUN ("redraw-display", Fredraw_display, Sredraw_display, 0, 0, "",
   return Qnil;
 }
 
-
 \f
-/***********************************************************************
-			     Frame Update
- ***********************************************************************/
+/**********************************************************************
+			    TTY Child Frames
+ **********************************************************************/
 
-/* Update frame F based on the data in desired matrices.
+/* Child frames on ttys break the assumption that frames on a tty
+   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 are required 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
+
+   - 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's current
+   matrices are updated by copying glyph contents of the current
+   matrix of the root frames to the current matrices of child
+   frames.  This implicitly also updates the glyph contents of their
+   windows' current matrices.  */
+
+struct rect
+{
+  int x, y, w, h;
+};
+
+/* 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 absolute position of frame F in *X and *Y.  */
+
+static void
+frame_pos_abs (struct frame *f, int *x, int *y)
+{
+  *x = *y = 0;
+  for (; f; f = FRAME_PARENT_FRAME (f))
+    {
+      *x += f->left_pos;
+      *y += f->top_pos;
+    }
+}
+
+/* Return the rectangle frame F occupies.  X and Y are in absolute
+   coordinates.  */
+
+static struct rect
+frame_rect_abs (struct frame *f)
+{
+  int x, y;
+  frame_pos_abs (f, &x, &y);
+  return (struct rect) { x, y, f->total_cols, 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;
+}
+
+int
+max_child_z_order (struct frame *parent)
+{
+  Lisp_Object tail, frame;
+  int z_order = 0;
+  FOR_EACH_FRAME (tail, frame)
+    {
+      struct frame *f = XFRAME (frame);
+      if (FRAME_PARENT_FRAME (f) == parent)
+	z_order = max (z_order, f->z_order);
+    }
+  return z_order;
+}
+
+/* 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.
+   If VISIBLE_ONLY is true, return only visible frames.  */
+
+static Lisp_Object
+frames_with_root (struct frame *root, bool visible_only)
+{
+  Lisp_Object list = Qnil;
+  Lisp_Object tail, frame;
+  FOR_EACH_FRAME (tail, frame)
+    {
+      struct frame *f = XFRAME (frame);
+      if (root_frame (f) == root
+	  && (!visible_only || FRAME_VISIBLE_P (f)))
+	list = Fcons (frame, list);
+    }
+  return list;
+}
+
+/* Return a list of frames having parent frame PARENT.
+   If VISIBLE_ONLY is true, return only visible frames.  */
+
+static Lisp_Object
+frames_with_parent (struct frame *parent, bool visible_only)
+{
+  Lisp_Object list = Qnil;
+  Lisp_Object tail, frame;
+  FOR_EACH_FRAME (tail, frame)
+    {
+      struct frame *f = XFRAME (frame);
+      if (FRAME_PARENT_FRAME (f) == parent
+	  && (!visible_only || FRAME_VISIBLE_P (f)))
+	list = Fcons (frame, list);
+    }
+  return list;
+}
+
+/* Compare frames F1 and F2 for z-order.  Value is like strcmp.  */
+
+static int
+frame_z_order_cmp (struct frame *f1, struct frame *f2)
+{
+  if (f1 == f2)
+    return 0;
+  if (is_frame_ancestor (f1, f2))
+    return -1;
+  if (is_frame_ancestor (f2, f1))
+    return 1;
+  return f1->z_order - f2->z_order;
+}
+
+DEFUN ("frame--z-order-lessp", Fframe__z_order_lessp, Sframe__z_order_lessp,
+       2, 2, 0, doc: /* Internal frame sorting function A < B.  */)
+  (Lisp_Object a, Lisp_Object b)
+{
+  eassert (FRAMEP (a) && FRAMEP (b));
+  return frame_z_order_cmp (XFRAME (a), XFRAME (b)) < 0 ? Qt : Qnil;
+}
+
+/* Return a z-order list of frames with the same root as F.  The list
+   is ordered topmost frame last.  Note that this list contains
+   the root frame of F itself as first element.  */
+
+Lisp_Object
+frames_in_reverse_z_order (struct frame *f, bool visible_only)
+{
+  struct frame *root = root_frame (f);
+  Lisp_Object frames = frames_with_root (root, visible_only);
+  frames = CALLN (Fsort, frames, QClessp, Qframe__z_order_lessp);
+  eassert (FRAMEP (XCAR (frames)));
+  eassert (XFRAME (XCAR (frames)) == root);
+  return frames;
+}
+
+/* Raise of lower frame F in z-order.  If RAISE is true, raise F, else
+   lower f.  */
+
+void
+tty_raise_lower_frame (struct frame *f, bool raise)
+{
+  struct frame *parent = FRAME_PARENT_FRAME (f);
+  if (parent == NULL)
+    return;
+
+  Lisp_Object siblings = frames_with_parent (parent, false);
+  siblings = CALLN (Fsort, siblings, QClessp, Qframe__z_order_lessp);
+
+  int i = 0;
+  for (Lisp_Object tail = siblings; CONSP (tail); tail = XCDR (tail))
+    {
+      struct frame *child = XFRAME (XCAR (tail));
+      if (child != f)
+	child->z_order = i++;
+    }
+  f->z_order = raise ? i : 0;
+}
+
+/* Return true if frame F is a tty frame.  */
 
 bool
-update_frame (struct frame *f, bool force_p, bool inhibit_hairy_id_p)
+is_tty_frame (struct frame *f)
 {
-  /* True means display has been paused because of pending input.  */
-  bool paused_p;
-  struct window *root_window = XWINDOW (f->root_window);
+  return FRAME_TERMCAP_P (f);
+}
 
-  if (redisplay_dont_pause)
-    force_p = true;
-  else if (!force_p && detect_input_pending_ignore_squeezables ())
+/* 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);
+}
+
+/* Prepare ROOT's desired row at index Y for copying child frame
+   contents to it.  Value is the prepared desired row or NULL if we
+   don't have, and can't contruct a desired row.  */
+
+static struct glyph_row *
+prepare_desired_root_row (struct frame *root, int y)
+{
+  /* If we have a desired row that has been displayed, use that.  */
+  struct glyph_row *desired_row = MATRIX_ROW (root->desired_matrix, y);
+  if (desired_row->enabled_p)
+    return desired_row;
+
+  /* If we have a current row that is up to date, copy that to the
+     desired row and use that.  */
+  /* Don't copy rows that aren't enabled, in particuler because they
+     might not have the 'frame' member of glyphs set.  */
+  struct glyph_row *current_row = MATRIX_ROW (root->current_matrix, y);
+  if (current_row->enabled_p)
     {
-      paused_p = true;
-      goto do_pause;
+      memcpy (desired_row->glyphs[0], current_row->glyphs[0],
+	      root->current_matrix->matrix_w * sizeof (struct glyph));
+      desired_row->enabled_p = true;
+      return desired_row;
     }
 
-  if (FRAME_WINDOW_P (f))
+  return NULL;
+}
+
+/* Change GLYPH to be a space glyph.  */
+
+static void
+make_glyph_space (struct glyph *glyph)
+{
+  glyph->u.ch = ' ';
+  glyph->pixel_width = 1;
+  glyph->padding_p = 0;
+}
+
+/* On root frame ROOT, if the glyph in ROW at position X is part of a
+   sequence of glyphs for a wide character, change every glyph belonging
+   to the sequence to a space.  If X is outside of ROOT, do nothing.  */
+
+static void
+neutralize_wide_char (struct frame *root, struct glyph_row *row, int x)
+{
+  if (x < 0 || x >= root->desired_matrix->matrix_w)
+    return;
+
+  struct glyph *glyph = row->glyphs[TEXT_AREA] + x;
+  if (glyph->type == CHAR_GLYPH && CHARACTER_WIDTH (glyph->u.ch) > 1)
     {
-      /* We are working on window matrix basis.  All windows whose
-	 flag must_be_updated_p is set have to be updated.  */
+      /* Glyph is somewhere in a sequence of glyphs for a wide
+	 character, find the start.  */
+      struct glyph *row_start = row->glyphs[TEXT_AREA];
+      while (glyph > row_start && glyph->padding_p)
+	--glyph;
 
-      /* Record that we are not working on frame matrices.  */
-      set_frame_matrix_frame (NULL);
+      /* Make everything in the sequence a space glyph.  */
+      eassert (!glyph->padding_p);
+      make_glyph_space (glyph);
+      struct glyph *row_limit = row_start + row->used[TEXT_AREA];
+      for (++glyph; glyph < row_limit && glyph->padding_p; ++glyph)
+	make_glyph_space (glyph);
+    }
+}
 
-      /* Update all windows in the window tree of F, maybe stopping
-	 when pending input is detected.  */
-      update_begin (f);
+/* Produce glyphs for box character BOX in ROW.  X is the position in
+   ROW where to start producing glyphs.  N is the number of glyphs to
+   produce.  CHILD is the frame to use for the face of the glyphs.  */
 
-#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
+static void
+produce_box_glyphs (enum box box, struct glyph_row *row, int x, int n,
+		    struct frame *child)
+{
+  int dflt;
+  switch (box)
+    {
+    case BOX_VERTICAL:
+      dflt = '|';
+      break;
+    case BOX_HORIZONTAL:
+      dflt = '-';
+      break;
+    case BOX_DOWN_RIGHT:
+    case BOX_DOWN_LEFT:
+    case BOX_UP_RIGHT:
+    case BOX_UP_LEFT:
+      dflt = '+';
+      break;
+    }
 
-#if defined (HAVE_WINDOW_SYSTEM)
-      /* Update the tab-bar window, if present.  */
-      if (WINDOWP (f->tab_bar_window))
+  /* FIXME/tty: some face for the border.  */
+  int face_id = BORDER_FACE_ID;
+  GLYPH g;
+  SET_GLYPH (g, dflt, face_id);
+
+  if (DISP_TABLE_P (Vstandard_display_table))
+    {
+      struct Lisp_Char_Table *dp = XCHAR_TABLE (Vstandard_display_table);
+      Lisp_Object gc = dp->extras[box];
+      if (GLYPH_CODE_P (gc))
 	{
-	  struct window *w = XWINDOW (f->tab_bar_window);
+	  SET_GLYPH_FROM_GLYPH_CODE (g, gc);
+	  /* Sorry, but I really don't care if the glyph has a face :-).  */
+	}
+    }
 
-	  /* Update tab-bar window.  */
-	  if (w->must_be_updated_p)
-	    {
-	      Lisp_Object tem;
+  struct glyph *glyph = row->glyphs[0] + x;
+  for (int i = 0; i < n; ++i, ++glyph)
+    {
+      glyph->type = CHAR_GLYPH;
+      glyph->u.ch = GLYPH_CHAR (g);
+      glyph->charpos = -1;
+      glyph->pixel_width = 1;
+      glyph->multibyte_p = 1;
+      glyph->face_id = GLYPH_FACE (g);
+      glyph->frame = child;
+      glyph->padding_p = 0;
+      glyph->object = Qnil;
+      glyph->padding_p = 0;
+    }
+}
 
-	      update_window (w, true);
-	      w->must_be_updated_p = false;
+/* Produce box glyphs LEFT and RIGHT in ROOT_ROW.  X and W are the start
+   and width of a range in ROOT_ROW before and after which to put the
+   box glyphs, if they fit.  ROOT and CHILD are root and child frame we
+   are working on.  ROOT is the root frame whose matrix dimensions
+   determines if the box glyphs fit.  CHILD is the frame whose faces to
+   use for the box glyphs.  */
 
-	      /* 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);
-	    }
+static void
+produce_box_sides (enum box left, enum box right, struct glyph_row *root_row, int x,
+		   int w, struct frame *root, struct frame *child)
+{
+  if (x > 0)
+    {
+      neutralize_wide_char (root, root_row, x - 1);
+      produce_box_glyphs (left, root_row, x - 1, 1, child);
+    }
+
+  if (x + w < root->desired_matrix->matrix_w)
+    {
+      neutralize_wide_char (root, root_row, x + w);
+      produce_box_glyphs (right, root_row, x + w, 1, child);
+    }
+}
+
+static void
+produce_box_line (struct frame *root, struct frame *child, int x, int y, int w,
+		  bool first)
+{
+  struct glyph_row *root_row = prepare_desired_root_row (root, y);
+  if (root_row == NULL)
+    return;
+  if (first)
+    produce_box_sides (BOX_DOWN_RIGHT, BOX_DOWN_LEFT, root_row, x, w, root, child);
+  else
+    produce_box_sides (BOX_UP_RIGHT, BOX_UP_LEFT, root_row, x, w, root, child);
+  produce_box_glyphs (BOX_HORIZONTAL, root_row, x, w, child);
+  root_row->hash = row_hash (root_row);
+}
+
+/* Copy to ROOT's desired matrix what we need from CHILD.  */
+
+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_abs (root), frame_rect_abs (child)))
+    return;
+
+  /* Build CHILD's current matrix which we need to copy from it.  */
+  make_matrix_current (child);
+
+  /* Draw borders around the child frame.  */
+  if (!FRAME_UNDECORATED (child))
+    {
+      /* Horizontal line above.  */
+      if (r.y > 0)
+	produce_box_line (root, child, r.x, r.y - 1, r.w, true);
+
+      for (int y = r.y; y < r.y + r.h; ++y)
+	{
+	  struct glyph_row *root_row = prepare_desired_root_row (root, y);
+	  if (root_row)
+	    produce_box_sides (BOX_VERTICAL, BOX_VERTICAL, root_row, r.x, r.w,
+			       root, child);
 	}
-#endif
 
-#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
-      /* Update the tool-bar window, if present.  */
-      if (WINDOWP (f->tool_bar_window))
+      /* Horizontal line below.  */
+      if (r.y + r.h < root->desired_matrix->matrix_h)
+	produce_box_line (root, child, r.x, r.y + r.h, r.w, false);
+    }
+
+  /* First visible row/col, relative to the child frame.  */
+  int child_x = child->left_pos < 0 ? - child->left_pos : 0;
+  int child_y = child->top_pos < 0 ? - child->top_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
+     if they aren't already.  */
+  for (int y = r.y; y < r.y + r.h; ++y, ++child_y)
+    {
+      struct glyph_row *root_row = prepare_desired_root_row (root, y);
+      if (root_row == NULL)
+	continue;
+
+      /* Deal with wide characters unless already done as part of
+	 drawing a box around the child frame.  */
+      if (FRAME_UNDECORATED (child))
 	{
-	  struct window *w = XWINDOW (f->tool_bar_window);
+	  neutralize_wide_char (root, root_row, r.x - 1);
+	  neutralize_wide_char (root, root_row, r.x + r.w);
+	}
 
-	  /* Update tool-bar window.  */
-	  if (w->must_be_updated_p)
-	    {
-	      Lisp_Object tem;
+      /* Copy what's visible from the child's current row.  If that row
+	 is not enabled_p, we can't copy anything that makes sense.  */
+      struct glyph_row *child_row = MATRIX_ROW (child->current_matrix, child_y);
+      if (child_row->enabled_p)
+	memcpy (root_row->glyphs[0] + r.x, child_row->glyphs[0] + child_x,
+		r.w * sizeof (struct glyph));
 
-	      update_window (w, true);
-	      w->must_be_updated_p = false;
+      /* Compute a new hash since we changed glyphs.  */
+      root_row->hash = row_hash (root_row);
+    }
+}
 
-	      /* 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);
-	    }
+/***********************************************************************
+			     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
+}
+
+#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)
+	{
+	  update_window (w, true);
+	  w->must_be_updated_p = false;
+	  Lisp_Object tem = *current;
+	  *current = *desired;
+	  *desired = tem;
 	}
+    }
+}
 #endif
 
-      /* Update windows.  */
-      paused_p = update_window_tree (root_window, force_p);
-      update_end (f);
+/* Update the tab-bar window of frame F, if present.  */
+
+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
+}
+
+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
+}
+
+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;
+}
+
+/* Return the cursor position of the selected window of frame F, in
+   absolute coordinates in *X and *Y.  Note that if F is a child frame,
+   its cursor may be clipped, i.e. outside of the bounds of the terminal
+   window.  Value is false if the selected window of F doesn't have
+   valid cursor position info.  */
+
+static bool
+abs_cursor_pos (struct frame *f, int *x, int *y)
+{
+  struct window *w = XWINDOW (f->selected_window);
+  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 wx = WINDOW_TO_FRAME_HPOS (w, w->cursor.hpos);
+      int wy = WINDOW_TO_FRAME_VPOS (w, w->cursor.vpos);
+
+      wx += max (0, w->left_margin_cols);
+
+      int fx, fy;
+      frame_pos_abs (f, &fx, &fy);
+      *x = fx + wx;
+      *y = fy + wy;
+      return true;
     }
-  else
+
+  *x = *y = 0;
+  return false;
+}
+
+static bool
+is_in_matrix (struct frame *f, int x, int y)
+{
+  struct frame *root = root_frame (f);
+  if (x < 0 || x >= root->current_matrix->matrix_w || y < 0
+      || y >= root->current_matrix->matrix_h)
+    return false;
+  return true;
+}
+
+/* Is the terminal cursor of the selected frame obscured by a child
+   frame?  */
+
+static bool
+is_cursor_obscured (void)
+{
+  /* Give up if we can't tell where the cursor currently is.  */
+  int x, y;
+  if (!abs_cursor_pos (SELECTED_FRAME (), &x, &y))
+    return false;
+
+  /* (x, y) may be outside of the root frame in case the selected frame is a
+     child frame which is clipped.  */
+  struct frame *root = root_frame (SELECTED_FRAME ());
+  if (!is_in_matrix (root, x, y))
+    return true;
+
+  struct glyph_row *cursor_row = MATRIX_ROW (root->current_matrix, y);
+  struct glyph *cursor_glyph = cursor_row->glyphs[0] + x;
+  return cursor_glyph->frame != SELECTED_FRAME ();
+}
+
+/* Decide where to show the cursor, and whether to hide it.
+
+   This works very well for Vertico-Posframe, Transient-Posframe and
+   Corfu, but it's debatable if it's the right thing for a general use
+   of child frames of all sorts, nested and so on.  But it is also
+   debatable if that's a realistic use case from my POV.  */
+
+static void
+terminal_cursor_magic (struct frame *root, struct frame *topmost_child)
+{
+  /* By default, prevent the cursor "shining through" child frames.  */
+  if (is_cursor_obscured ())
+    tty_hide_cursor (FRAME_TTY (root));
+
+  /* If the terminal cursor is not in the topmost child, the topmost
+     child's tty-cursor-if-topmost determines what to do.  If it is
+     non-nil, display the cursor in this "non-selected" topmost child
+     frame to compensate for the fact that we can't display a
+     non-selected cursor like on a window system frame.  */
+  if (topmost_child != SELECTED_FRAME ())
     {
-      /* We are working on frame matrix basis.  Set the frame on whose
-	 frame matrix we operate.  */
-      set_frame_matrix_frame (f);
+      Lisp_Object frame;
+      XSETFRAME (frame, topmost_child);
 
-      /* Build F's desired matrix from window matrices.  */
-      build_frame_matrix (f);
+      int x, y;
+      Lisp_Object cursor = Fframe_parameter (frame, Qtty_non_selected_cursor);
+      if (!NILP (cursor) && abs_cursor_pos (topmost_child, &x, &y))
+	{
+	  if (is_in_matrix (root, x, y))
+	    {
+	      cursor_to (root, y, x);
+	      tty_show_cursor (FRAME_TTY (topmost_child));
+	    }
+	  else
+	    tty_hide_cursor (FRAME_TTY (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.  */
+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);
+  struct frame *topmost_child = NULL;
+  for (Lisp_Object tail = XCDR (z_order); CONSP (tail); tail = XCDR (tail))
+    {
+      topmost_child = XFRAME (XCAR (tail));
+      copy_child_glyphs (root, topmost_child);
+    }
+
+  update_begin (root);
+  bool paused = write_matrix (root, force_p, inhibit_scrolling, 1, false);
+  if (!paused)
+    make_matrix_current (root);
+  update_end (root);
+
+  /* If a child is displayed, and the cursor is displayed in another
+     frame, the child might lay above the cursor, so that it appers to
+     "shine through" the child.  Avoid that because it's confusing.  */
+  if (topmost_child)
+    terminal_cursor_magic (root, topmost_child);
+  flush_terminal (root);
+
+  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)
+{
+  if (redisplay_dont_pause)
+    force_p = true;
+
+  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 +4068,25 @@ update_frame (struct frame *f, bool force_p, bool inhibit_hairy_id_p)
 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 +4095,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
@@ -3453,19 +4133,20 @@ update_mouse_position (struct frame *f, int x, int y)
 }
 
 DEFUN ("display--update-for-mouse-movement", Fdisplay__update_for_mouse_movement,
-       Sdisplay__update_for_mouse_movement, 2, 2, 0,
+       Sdisplay__update_for_mouse_movement, 3, 3, 0,
        doc: /* Handle mouse movement detected by Lisp code.
 
 This function should be called when Lisp code detects the mouse has
 moved, even if `track-mouse' is nil.  This handles updates that do not
 rely on input events such as updating display for mouse-face
 properties or updating the help echo text.  */)
-  (Lisp_Object mouse_x, Lisp_Object mouse_y)
+  (Lisp_Object mouse_frame, Lisp_Object mouse_x, Lisp_Object mouse_y)
 {
+  CHECK_FRAME (mouse_frame);
   CHECK_FIXNUM (mouse_x);
   CHECK_FIXNUM (mouse_y);
 
-  update_mouse_position (SELECTED_FRAME (), XFIXNUM (mouse_x),
+  update_mouse_position (XFRAME (mouse_frame), XFIXNUM (mouse_x),
                          XFIXNUM (mouse_y));
   return Qnil;
 }
@@ -3507,9 +4188,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 +4366,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 +5001,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 +5611,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 +5705,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;
-	    }
-
-	  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);
+    tty_set_cursor ();
 
-	      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 +5865,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 +5906,7 @@ #define char_ins_del_cost(f) (&char_ins_del_vector[FRAME_TOTAL_COLS (f)])
 /* 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 +5920,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 +5938,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 +5967,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 +5989,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 +6032,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 +6045,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 +6053,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 +6069,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 +6210,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 +6608,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 +7176,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 +7450,7 @@ syms_of_display (void)
   defsubr (&Ssend_string_to_terminal);
   defsubr (&Sinternal_show_cursor);
   defsubr (&Sinternal_show_cursor_p);
+  defsubr (&Sframe__z_order_lessp);
 
 #ifdef GLYPH_DEBUG
   defsubr (&Sdump_redisplay_history);
@@ -6818,8 +7461,10 @@ syms_of_display (void)
 
   /* This is the "purpose" slot of a display table.  */
   DEFSYM (Qdisplay_table, "display-table");
+  DEFSYM (Qframe__z_order_lessp, "frame--z-order-lessp");
 
   DEFSYM (Qredisplay_dont_pause, "redisplay-dont-pause");
+  DEFSYM (Qtty_non_selected_cursor, "tty-non-selected-cursor");
 
   DEFVAR_INT ("baud-rate", baud_rate,
 	      doc: /* The output baud rate of the terminal.
@@ -6921,6 +7566,8 @@ syms_of_display (void)
 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
modified   src/disptab.h
@@ -28,7 +28,7 @@ #define DISP_TABLE_P(obj)						    \
    && EQ (XCHAR_TABLE (obj)->purpose, Qdisplay_table)			    \
    && CHAR_TABLE_EXTRA_SLOTS (XCHAR_TABLE (obj)) == DISP_TABLE_EXTRA_SLOTS)
 
-#define DISP_TABLE_EXTRA_SLOTS 6
+#define DISP_TABLE_EXTRA_SLOTS 12
 #define DISP_TRUNC_GLYPH(dp) ((dp)->extras[0])
 #define DISP_CONTINUE_GLYPH(dp) ((dp)->extras[1])
 #define DISP_ESCAPE_GLYPH(dp) ((dp)->extras[2])
@@ -36,6 +36,16 @@ #define DISP_CTRL_GLYPH(dp) ((dp)->extras[3])
 #define DISP_INVIS_VECTOR(dp) ((dp)->extras[4])
 #define DISP_BORDER_GLYPH(dp) ((dp)->extras[5])
 
+enum box
+{
+  BOX_VERTICAL = 6,
+  BOX_HORIZONTAL,
+  BOX_DOWN_RIGHT,
+  BOX_DOWN_LEFT,
+  BOX_UP_RIGHT,
+  BOX_UP_LEFT
+};
+
 extern Lisp_Object disp_char_vector (struct Lisp_Char_Table *, int);
 
 #define DISP_CHAR_VECTOR(dp, c)				\
modified   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;
 
+  /* 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 probaly not 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
@@ -370,17 +396,17 @@ frame_windows_min_size (Lisp_Object frame, Lisp_Object horizontal,
     }
   else
     retval = XFIXNUM (call4 (Qframe_windows_min_size, frame, horizontal,
-			  ignore, pixelwise));
+			     ignore, pixelwise));
 
   /* Don't allow too small height of text-mode frames, or else cm.c
      might abort in cmcheckmagic.  */
   if ((FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)) && NILP (horizontal))
     {
-      int min_height = (FRAME_MENU_BAR_LINES (f)
-			+ FRAME_TAB_BAR_LINES (f)
+      int min_height = (FRAME_MENU_BAR_LINES (f) + FRAME_TAB_BAR_LINES (f)
 			+ FRAME_WANTS_MODELINE_P (f)
-			+ 2);	/* one text line and one echo-area line */
-
+			+ FRAME_HAS_MINIBUF_P (f));
+      if (min_height == 0)
+	min_height = 1;
       if (retval < min_height)
 	retval = min_height;
     }
@@ -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
@@ -833,8 +857,9 @@ adjust_frame_size (struct frame *f, int new_text_width, int new_text_height,
 
       /* 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;
+      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 +891,10 @@ 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. */
+      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);
@@ -898,6 +924,9 @@ adjust_frame_size (struct frame *f, int new_text_width, int new_text_height,
   adjust_frame_glyphs (f);
   calculate_costs (f);
   SET_FRAME_GARBAGED (f);
+  if (is_tty_child_frame (f))
+    SET_FRAME_GARBAGED (root_frame (f));
+
   /* We now say here that F was resized instead of using the old
      condition below.  Some resizing must have taken place and if it was
      only shifting the root window's position (paranoia?).  */
@@ -910,7 +939,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 +948,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 +994,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 +1116,6 @@ make_frame (bool mini_p)
   return f;
 }
 \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 +1209,7 @@ make_minibuffer_frame (void)
 		      : Fcar (Vminibuffer_list)), 0, 0);
   return f;
 }
-#endif /* HAVE_WINDOW_SYSTEM */
+
 \f
 /* Construct a frame that refers to a terminal.  */
 
@@ -1209,7 +1235,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 +1272,48 @@ 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_LIVE_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))
+	    error ("minibuffer-only child frames are not implemented");
+	  else if (WINDOWP (mini))
+	    f = make_frame_without_minibuffer (mini, kb, Qnil);
+	}
 
+      if (f == NULL)
+	f = make_frame (true);
+      f->parent_frame = parent;
+      f->z_order = 1 + max_child_z_order (XFRAME (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 +1338,15 @@ make_terminal_frame (struct terminal *terminal)
   f->horizontal_scroll_bars = false;
 #endif
 
-  FRAME_MENU_BAR_LINES (f) = NILP (Vmenu_bar_mode) ? 0 : 1;
+  /* 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 +1355,18 @@ 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.  */
-  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 */
+  /* Mark current topmost frame obscured if we make a new root frame.
+     Child frames don't completely obscure other frames.  */
+  if (NILP (parent) && FRAMEP (FRAME_TTY (f)->top_frame))
+    {
+      struct frame *top = XFRAME (FRAME_TTY (f)->top_frame);
+      struct frame *root = root_frame (top);
+      if (FRAME_LIVE_P (root))
+	SET_FRAME_VISIBLE (root, false);
+    }
 
+  /* Set the top frame to the newly created frame.  */
   FRAME_TTY (f)->top_frame = frame;
-
-  if (!noninteractive)
-    init_frame_faces (f);
-
   return f;
 }
 
@@ -1335,6 +1396,68 @@ 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);
+	  eassert (parent);	/* the caller ensures this, but... */
+	  if (parent)
+	    {
+	      int sz = (EQ (key, Qwidth) ? FRAME_TOTAL_COLS (parent)
+			: FRAME_TOTAL_LINES (parent));
+	      val = make_fixnum (XFLOAT_DATA (val) * sz);
+	    }
+	  else
+	    val = Qnil;
+	}
+
+      if (FIXNATP (val))
+	return XFIXNUM (val);
+    }
+  return dflt;
+}
+
+static void
+tty_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 +1481,7 @@ DEFUN ("make-terminal-frame", Fmake_terminal_frame, Smake_terminal_frame,
   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 +1511,7 @@ DEFUN ("make-terminal-frame", Fmake_terminal_frame, Smake_terminal_frame,
       error ("Multiple terminals are not supported on this platform");
     if (!t)
       t = the_only_display_info.terminal;
-#endif
+# endif
   }
 
   if (!t)
@@ -1417,19 +1538,64 @@ DEFUN ("make-terminal-frame", Fmake_terminal_frame, Smake_terminal_frame,
       SAFE_FREE ();
     }
 
-  f = make_terminal_frame (t);
+  /* Make a new frame.  We need to know up front 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: The only way, for now, to get borders on a tty is
+	 to allow decorations.  */
+      Lisp_Object undecorated = Fassq (Qundecorated, parms);
+      if (CONSP (undecorated) && !NILP (XCDR (undecorated)))
+	f->undecorated = true;
+
+      /* Unused at present.  */
+      Lisp_Object no_focus = Fassq (Qno_accept_focus, parms);
+      if (CONSP (no_focus) && !NILP (XCDR (no_focus)))
+	f->no_accept_focus = true;
+
+      Lisp_Object no_split = Fassq (Qunsplittable, parms);
+      if (CONSP (no_split) && !NILP (XCDR (no_split)))
+	f->no_split = true;
+    }
 
+  /* 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))
+    tty_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 +1617,11 @@ DEFUN ("make-terminal-frame", Fmake_terminal_frame, Smake_terminal_frame,
   /* 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;
@@ -1480,8 +1650,6 @@ DEFUN ("make-terminal-frame", Fmake_terminal_frame, Smake_terminal_frame,
 Lisp_Object
 do_switch_frame (Lisp_Object frame, int track, int for_deletion, Lisp_Object norecord)
 {
-  struct frame *sf = SELECTED_FRAME (), *f;
-
   /* If FRAME is a switch-frame event, extract the frame we should
      switch to.  */
   if (CONSP (frame)
@@ -1493,7 +1661,9 @@ do_switch_frame (Lisp_Object frame, int track, int for_deletion, Lisp_Object nor
      a switch-frame event to arrive after a frame is no longer live,
      especially when deleting the initial frame during startup.  */
   CHECK_FRAME (frame);
-  f = XFRAME (frame);
+  struct frame *f = XFRAME (frame);
+  struct frame *sf = SELECTED_FRAME ();
+
   /* Silently ignore dead and tooltip frames (Bug#47207).  */
   if (!FRAME_LIVE_P (f) || FRAME_TOOLTIP_P (f))
     return Qnil;
@@ -1546,24 +1716,37 @@ 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))
 	{
+	  struct frame *new_root = root_frame (f);
+	  SET_FRAME_VISIBLE (new_root, true);
+	  SET_FRAME_VISIBLE (f, true);
+
+	  /* Mark previously displayed frame as no longer visible.  */
 	  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);
+	    {
+	      struct frame *top = XFRAME (top_frame);
+	      struct frame *old_root = root_frame (top);
+	      if (old_root != new_root)
+		SET_FRAME_VISIBLE (old_root, false);
+	    }
+
+	  tty->top_frame = frame;
+
+	  /* FIXME: Why is it correct to set FrameCols/Rows?  */
+	  if (!FRAME_PARENT_FRAME (f))
+	    {
+	      /* 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);
+	    }
 	}
-      tty->top_frame = frame;
     }
 
   sf->select_mini_window_flag = MINI_WINDOW_P (XWINDOW (sf->selected_window));
@@ -1605,10 +1788,36 @@ 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;
+
+  /* FIXME/tty: I don't understand this.  (The comment above is from
+     Jim BLandy 1993 BTW, and the frame_ancestor_p from 2017.)
+
+     Setting the last event frame to nil leads to switch-frame events
+     being generated even if they normally wouldn't be because the frame
+     in question equals selected-frame.  See the places in keyboard.c
+     where make_lispy_switch_frame is called.
+
+     This leads to problems at least on ttys.
+
+     Imagine that we have functions in post-command-hook that use
+     select-frame in some way (e.g., with-selected-window).  Let these
+     functions select different frames during the execution of
+     post-command-hook in command_loop_1.  Setting
+     internal_last_event_frame to nil here makes these select-frame
+     calls (potentially and in reality) generate switch-frame events.
+     (But only in one direction (frame_ancestor_p), which I also don't
+     understand).
+
+     These switch-frame events form an endless loop in
+     command_loop_1.  It runs post-command-hook, which generates
+     switch-frame events, which command_loop_1 finds (bound to '#ignore)
+     and executes, which again runs post-command-hook etc., ad
+     infinitum.
+
+     Let's not do that for now on ttys.  */
+  if (!is_tty_frame (f))
+    if (!frame_ancestor_p (f, sf))
+      internal_last_event_frame = Qnil;
 
   return frame;
 }
@@ -1725,7 +1934,6 @@ DEFUN ("frame-parent", Fframe_parent, Sframe_parent,
     return Qnil;
 }
 
-#ifdef HAVE_WINDOW_SYSTEM
 bool
 frame_ancestor_p (struct frame *af, struct frame *df)
 {
@@ -1741,7 +1949,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 +1959,10 @@ DEFUN ("frame-ancestor-p", Fframe_ancestor_p, Sframe_ancestor_p,
 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.
@@ -2021,7 +2223,9 @@ other_frames (struct frame *f, bool invisible, bool force)
 	      && (invisible || NILP (get_frame_param (f1, Qdelete_before)))
 	      /* For invisibility and normal deletions, at least one
 		 visible or iconified frame must remain (Bug#26682).  */
-	      && (FRAME_VISIBLE_P (f1) || FRAME_ICONIFIED_P (f1)
+	      && (FRAME_VISIBLE_P (f1)
+		  || is_tty_frame (f1)
+		  || FRAME_ICONIFIED_P (f1)
 		  || (!invisible
 		      && (force
 			  /* Allow deleting the terminal frame when at
@@ -2282,7 +2486,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 +3072,12 @@ DEFUN ("make-frame-visible", Fmake_frame_visible, Smake_frame_visible,
   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);
+      tty_raise_lower_frame (f, true);
+    }
+
   make_frame_visible_1 (f->root_window);
 
   /* Make menu bar update for the Buffers and Frames menus.  */
@@ -2918,6 +3128,12 @@ DEFUN ("make-frame-invisible", Fmake_frame_invisible, Smake_frame_invisible,
   if (FRAME_WINDOW_P (f) && FRAME_TERMINAL (f)->frame_visible_invisible_hook)
     FRAME_TERMINAL (f)->frame_visible_invisible_hook (f, false);
 
+  /* The ELisp 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;
 
@@ -2977,10 +3193,13 @@ DEFUN ("frame-visible-p", Fframe_visible_p, Sframe_visible_p,
   (Lisp_Object frame)
 {
   CHECK_LIVE_FRAME (frame);
+  struct frame *f = XFRAME (frame);
 
-  if (FRAME_VISIBLE_P (XFRAME (frame)))
+  if (FRAME_VISIBLE_P (f))
+    return Qt;
+  else if (is_tty_root_frame (f))
     return Qt;
-  if (FRAME_ICONIFIED_P (XFRAME (frame)))
+  if (FRAME_ICONIFIED_P (f))
     return Qicon;
   return Qnil;
 }
@@ -3012,12 +3231,7 @@ DEFUN ("raise-frame", Fraise_frame, Sraise_frame, 0, 1, "",
 
   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 +3532,15 @@ store_frame_param (struct frame *f, Lisp_Object prop, Lisp_Object val)
       val = old_val;
     }
 
+  /* Re-parenting is currently not implemented when changing a root
+     frame to a child frame or vice versa.  */
+  if (is_tty_frame (f) && EQ (prop, Qparent_frame))
+    {
+      if (NILP (f->parent_frame) != NILP (val))
+	error ("Making a root frame a child or vice versa is not supported");
+      f->parent_frame = val;
+    }
+
   /* 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 +3664,10 @@ DEFUN ("frame-parameters", Fframe_parameters, Sframe_parameters, 0, 1, 0,
   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);
+      store_in_alist (&alist, Qno_accept_focus, FRAME_NO_ACCEPT_FOCUS (f) ? Qt : Qnil);
     }
 
   return alist;
@@ -3525,7 +3745,6 @@ DEFUN ("frame-parameter", Fframe_parameter, Sframe_parameter, 2, 2, 0,
   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 +3782,7 @@ DEFUN ("modify-frame-parameters", Fmodify_frame_parameters,
       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 +3808,31 @@ DEFUN ("modify-frame-parameters", Fmodify_frame_parameters,
 	    update_face_from_frame_parameter (f, prop, val);
 	}
 
+      if (is_tty_child_frame (f))
+	{
+	  int x = tty_child_pos_param (f, Qleft, params, f->left_pos);
+	  int y = tty_child_pos_param (f, Qtop, params, f->top_pos);
+	  if (x != f->left_pos || y != f->top_pos)
+	    {
+	      f->left_pos = x;
+	      f->top_pos = y;
+	      SET_FRAME_GARBAGED (root_frame (f));
+	    }
+
+	  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 +4180,11 @@ DEFUN ("set-frame-position", Fset_frame_position,
       (void) yval;
 #endif
     }
+  else if (is_tty_child_frame (f))
+    {
+      f->left_pos = xval;
+      f->top_pos = yval;
+    }
 
   return Qt;
 }
@@ -3992,9 +4242,9 @@ DEFUN ("frame-scale-factor", Fframe_scale_factor, Sframe_scale_factor,
 /* Connect the frame-parameter names for frames to the ways of passing
    the parameter values to the window system.
 
-   The name of a parameter, as a Lisp symbol, has a
-   `frame-parameter-pos' property which is an integer in Lisp that is
-   an index in this table.  */
+   The name of a parameter, a Lisp symbol, has an `x-frame-parameter'
+   property which is its index in this table.  This is initialized in
+   syms_of_frame.  */
 
 struct frame_parm_table {
   const char *name;
@@ -4005,13 +4255,13 @@ DEFUN ("frame-scale-factor", Fframe_scale_factor, Sframe_scale_factor,
 {
   {"auto-raise",		SYMBOL_INDEX (Qauto_raise)},
   {"auto-lower",		SYMBOL_INDEX (Qauto_lower)},
-  {"background-color",		-1},
+  {"background-color",		SYMBOL_INDEX (Qbackground_color)},
   {"border-color",		SYMBOL_INDEX (Qborder_color)},
   {"border-width",		SYMBOL_INDEX (Qborder_width)},
   {"cursor-color",		SYMBOL_INDEX (Qcursor_color)},
   {"cursor-type",		SYMBOL_INDEX (Qcursor_type)},
-  {"font",			-1},
-  {"foreground-color",		-1},
+  {"font",			SYMBOL_INDEX (Qfont)},
+  {"foreground-color",		SYMBOL_INDEX (Qforeground_color)},
   {"icon-name",			SYMBOL_INDEX (Qicon_name)},
   {"icon-type",			SYMBOL_INDEX (Qicon_type)},
   {"child-frame-border-width",	SYMBOL_INDEX (Qchild_frame_border_width)},
@@ -4246,6 +4496,29 @@ 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 and the 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 +4660,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 +4981,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);
@@ -6455,17 +6713,13 @@ syms_of_frame (void)
   DEFSYM (Quse_frame_synchronization, "use-frame-synchronization");
   DEFSYM (Qfont_parameter, "font-parameter");
 
-  {
-    int i;
-
-    for (i = 0; i < ARRAYELTS (frame_parms); i++)
-      {
-	Lisp_Object v = (frame_parms[i].sym < 0
-			 ? intern_c_string (frame_parms[i].name)
-			 : builtin_lisp_symbol (frame_parms[i].sym));
-	Fput (v, Qx_frame_parameter, make_fixnum (i));
-      }
-  }
+  for (int i = 0; i < ARRAYELTS (frame_parms); i++)
+    {
+      int sym = frame_parms[i].sym;
+      eassert (sym >= 0 && sym < ARRAYELTS (lispsym));
+      Lisp_Object v = builtin_lisp_symbol (sym);
+      Fput (v, Qx_frame_parameter, make_fixnum (i));
+    }
 
 #ifdef HAVE_WINDOW_SYSTEM
   DEFVAR_LISP ("x-resource-name", Vx_resource_name,
modified   src/frame.h
@@ -161,10 +161,8 @@ #define EMACS_FRAME_H
      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 @@ #define EMACS_FRAME_H
      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.  */
@@ -451,6 +442,13 @@ #define EMACS_FRAME_H
      This must be the same as the terminal->type. */
   ENUM_BF (output_method) output_method : 4;
 
+  /* True if this is an undecorated frame.  */
+  bool_bf undecorated : 1;
+
+  /* Nonzero if this frame's window does not want to receive input focus
+     via mouse clicks or by moving the mouse into it.  */
+  bool_bf no_accept_focus : 1;
+
 #ifdef HAVE_WINDOW_SYSTEM
   /* True if this frame is a tooltip frame.  */
   bool_bf tooltip : 1;
@@ -465,10 +463,7 @@ #define EMACS_FRAME_H
   /* Nonzero if we should actually display horizontal scroll bars on this frame.  */
   bool_bf horizontal_scroll_bars : 1;
 
-  /* True if this is an undecorated frame.  */
-  bool_bf undecorated : 1;
-
-#ifndef HAVE_NTGUI
+# ifndef HAVE_NTGUI
   /* True if this is an override_redirect frame.  */
   bool_bf override_redirect : 1;
 #endif
@@ -480,17 +475,13 @@ #define EMACS_FRAME_H
      receive input focus when it is mapped.  */
   bool_bf no_focus_on_map : 1;
 
-  /* Nonzero if this frame's window does not want to receive input focus
-     via mouse clicks or by moving the mouse into it.  */
-  bool_bf no_accept_focus : 1;
-
   /* 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 @@ #define EMACS_FRAME_H
 #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,9 @@ #define FRAME_RES(f)	default_pixels_per_inch_y ()
    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_DISPLAY_INFO (F)->mouse_highlight			\
    : &(F)->output_data.tty->display_info->mouse_highlight)
 #else
 /* There is no "struct tty_output" on Android at all.  */
@@ -1176,9 +1170,6 @@ #define FRAME_REDISPLAY_P(f) (FRAME_VISIBLE_P (f)		\
 				  && 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,21 +1234,23 @@ #define FRAME_HAS_VERTICAL_SCROLL_BARS_ON_LEFT(f) ((void) (f), 0)
 #define FRAME_HAS_VERTICAL_SCROLL_BARS_ON_RIGHT(f) ((void) (f), 0)
 #endif /* HAVE_WINDOW_SYSTEM */
 
-#if defined (HAVE_WINDOW_SYSTEM)
+INLINE struct frame *
+FRAME_PARENT_FRAME (struct frame *f)
+{
+  return NILP (f->parent_frame) ? NULL : XFRAME (f->parent_frame);
+}
+
 #define FRAME_UNDECORATED(f) ((f)->undecorated)
+
+#if defined (HAVE_WINDOW_SYSTEM)
 #ifdef HAVE_NTGUI
 #define FRAME_OVERRIDE_REDIRECT(f) ((void) (f), 0)
 #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)
@@ -1270,13 +1263,10 @@ #define FRAME_NS_APPEARANCE(f) ((f)->ns_appearance)
 #define FRAME_NS_TRANSPARENT_TITLEBAR(f) ((f)->ns_transparent_titlebar)
 #endif
 #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 +1274,8 @@ #define FRAME_Z_GROUP_BELOW(f) ((void) (f), false)
 #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 +1437,8 @@ #define AUTO_FRAME_ARG(name, parameter, value)		\
    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 +1495,14 @@ SET_FRAME_ICONIFIED (struct frame *f, int i)
 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 +1514,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 +1611,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 +1870,6 @@ #define EMACS_CLASS "Emacs"
 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 +1896,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)
 {
modified   src/keyboard.c
@@ -5415,7 +5415,7 @@ #define FUNCTION_KEY_OFFSET 0xff00
 
 /* 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
modified   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
modified   src/nsfns.m
@@ -3351,7 +3351,7 @@ internalBorderWidth or internalBorder (which is what xterm calls
 	      [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 @@ internalBorderWidth or internalBorder (which is what xterm calls
       [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 ();
modified   src/nsterm.m
@@ -1505,7 +1505,7 @@ -(void)remove
       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 @@ Hide the window (X11 semantics)
   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);
 }
 
modified   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)
modified   src/term.c
@@ -65,11 +65,9 @@
 #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 *);
 static void tty_background_highlight (struct tty_display_info *tty);
 static void clear_tty_hooks (struct terminal *terminal);
 static void set_tty_hooks (struct terminal *terminal);
@@ -336,7 +334,7 @@ tty_toggle_highlight (struct tty_display_info *tty)
 
 /* Make cursor invisible.  */
 
-static void
+void
 tty_hide_cursor (struct tty_display_info *tty)
 {
   if (tty->cursor_hidden == 0)
@@ -353,7 +351,7 @@ tty_hide_cursor (struct tty_display_info *tty)
 
 /* Ensure that cursor is visible.  */
 
-static void
+void
 tty_show_cursor (struct tty_display_info *tty)
 {
   if (tty->cursor_hidden)
@@ -753,18 +751,12 @@ encode_terminal_code (struct glyph *src, int src_len,
 static void
 tty_write_glyphs (struct frame *f, struct glyph *string, int len)
 {
-  unsigned char *conversion_buffer;
-  struct coding_system *coding;
-  int n, stringlen;
-
   struct tty_display_info *tty = FRAME_TTY (f);
-
   tty_turn_off_insert (tty);
   tty_hide_cursor (tty);
 
   /* Don't dare write in last column of bottom line, if Auto-Wrap,
      since that would scroll the whole frame on some terminals.  */
-
   if (AutoWrap (tty)
       && curY (tty) + 1 == FRAME_TOTAL_LINES (f)
       && (curX (tty) + len) == FRAME_COLS (f))
@@ -777,29 +769,34 @@ tty_write_glyphs (struct frame *f, struct glyph *string, int len)
   /* If terminal_coding does any conversion, use it, otherwise use
      safe_terminal_coding.  We can't use CODING_REQUIRE_ENCODING here
      because it always return 1 if the member src_multibyte is 1.  */
-  coding = (FRAME_TERMINAL_CODING (f)->common_flags & CODING_REQUIRE_ENCODING_MASK
-	    ? FRAME_TERMINAL_CODING (f) : &safe_terminal_coding);
+  struct coding_system *coding
+    = (FRAME_TERMINAL_CODING (f)->common_flags & CODING_REQUIRE_ENCODING_MASK
+       ? FRAME_TERMINAL_CODING (f) : &safe_terminal_coding);
+
   /* The mode bit CODING_MODE_LAST_BLOCK should be set to 1 only at
      the tail.  */
   coding->mode &= ~CODING_MODE_LAST_BLOCK;
 
-  for (stringlen = len; stringlen != 0; stringlen -= n)
+  for (int stringlen = len, n; stringlen; stringlen -= n, string += n)
     {
       /* Identify a run of glyphs with the same face.  */
       int face_id = string->face_id;
+      struct frame *face_id_frame = string->frame;
 
       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.  */
 	coding->mode |= CODING_MODE_LAST_BLOCK;
-      conversion_buffer = encode_terminal_code (string, n, coding);
+      unsigned char *conversion_buffer
+	= encode_terminal_code (string, n, coding);
       if (coding->produced > 0)
 	{
 	  block_input ();
@@ -809,10 +806,9 @@ tty_write_glyphs (struct frame *f, struct glyph *string, int len)
 	    fwrite (conversion_buffer, 1, coding->produced, tty->termscript);
 	  unblock_input ();
 	}
-      string += n;
 
       /* Turn appearance modes off.  */
-      turn_off_face (f, face_id);
+      turn_off_face (f, face);
       tty_turn_off_highlight (tty);
     }
 
@@ -822,8 +818,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 +852,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 +867,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 +915,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 +925,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 +957,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 +1542,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 +1770,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 +1857,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 +1984,8 @@ #define MAY_USE_WITH_COLORS_P(tty, ATTR)                \
    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 +2066,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,8 +2400,10 @@ DEFUN ("suspend-tty", Fsuspend_tty, Ssuspend_tty, 0, 1, 0,
       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);
-
+	{
+	  struct frame *top = XFRAME (t->display_info.tty->top_frame);
+	  SET_FRAME_VISIBLE (root_frame (top), false);
+	}
     }
 
   /* Clear display hooks to prevent further output.  */
@@ -2472,7 +2475,8 @@ DEFUN ("resume-tty", Fresume_tty, Sresume_tty, 0, 1, 0,
 
       if (FRAMEP (t->display_info.tty->top_frame))
 	{
-	  struct frame *f = XFRAME (t->display_info.tty->top_frame);
+	  struct frame *top = XFRAME (t->display_info.tty->top_frame);
+	  struct frame *f = root_frame (top);
 	  int width, height;
 	  int old_height = FRAME_COLS (f);
 	  int old_width = FRAME_TOTAL_LINES (f);
@@ -2482,7 +2486,7 @@ DEFUN ("resume-tty", Fresume_tty, Sresume_tty, 0, 1, 0,
 	  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 (f, true);
 	}
 
       set_tty_hooks (t);
@@ -2563,22 +2567,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 (f, 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);
 
@@ -3115,9 +3121,7 @@ save_and_enable_current_matrix (struct frame *f)
 static void
 restore_desired_matrix (struct frame *f, struct glyph_matrix *saved)
 {
-  int i;
-
-  for (i = 0; i < saved->nrows; ++i)
+  for (int i = 0; i < saved->nrows; ++i)
     {
       struct glyph_row *from = saved->rows + i;
       struct glyph_row *to = f->desired_matrix->rows + i;
@@ -3127,7 +3131,23 @@ restore_desired_matrix (struct frame *f, struct glyph_matrix *saved)
       memcpy (to->glyphs[TEXT_AREA], from->glyphs[TEXT_AREA], nbytes);
       to->used[TEXT_AREA] = from->used[TEXT_AREA];
       to->enabled_p = from->enabled_p;
-      to->hash = from->hash;
+
+      bool need_new_hash = false;
+      for (int x = 0; x < f->desired_matrix->matrix_w; ++x)
+	{
+	  struct glyph *glyph = to->glyphs[0] + x;
+	  if (!FRAME_LIVE_P (glyph->frame))
+	    {
+	      glyph->frame = f;
+	      glyph->face_id = DEFAULT_FACE_ID;
+	      need_new_hash = true;
+	    }
+	}
+
+      if (need_new_hash)
+	to->hash = row_hash (to);
+      else
+	to->hash = from->hash;
     }
 }
 
@@ -3969,7 +3989,7 @@ tty_free_frame_resources (struct frame *f)
 
 #endif
 
-\f
+
 
 #ifndef HAVE_ANDROID
 
@@ -4044,6 +4064,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 +4736,186 @@ 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: tty-frame-restack implementation.  */
+  error ("tty-frame-restack is not implemented");
+  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:
+    default:
+      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 +4972,13 @@ syms_of_term (void)
   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;
modified   src/termhooks.h
@@ -974,6 +974,9 @@ #define cursorX(t)  curX(t)
 #define cursorY(t)  curY(t)
 #endif
 
+void tty_hide_cursor (struct tty_display_info *tty);
+void tty_show_cursor (struct tty_display_info *tty);
+
 INLINE_HEADER_END
 
 #endif /* EMACS_TERMHOOKS_H */
modified   src/terminal.c
@@ -111,7 +111,8 @@ set_terminal_window (struct frame *f, int size)
 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 @@ cursor_to (struct frame *f, int vpos, int hpos)
 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.  */
modified   src/treesit.c
@@ -651,7 +651,7 @@ treesit_load_language (Lisp_Object language_symbol,
 
   /* Override the library name and C name, if appropriate.  */
   Lisp_Object override_name;
-  Lisp_Object override_c_name;
+  Lisp_Object override_c_name UNINIT;
   bool found_override = treesit_find_override_name (language_symbol,
 						    &override_name,
 						    &override_c_name);
modified   src/w32console.c
@@ -167,6 +167,7 @@ w32con_clear_end_of_line (struct frame *f, int end)
       for (i = 0; i < glyphs_len; i++)
         {
 	  memcpy (&glyphs[i], &space_glyph, sizeof (struct glyph));
+	  glyphs[i].frame = f;
         }
       ceol_initialized = TRUE;
     }
@@ -327,14 +328,19 @@ w32con_write_glyphs (struct frame *f, register struct glyph *string,
     {
       /* Identify a run of glyphs with the same face.  */
       int face_id = string->face_id;
+      /* Since this is called to deliver the frame glyph matrix to the
+	 glass, some of the glyphs might be from a child frame, which
+	 affects the interpretation of face ID.  */
+      struct frame *face_id_frame = string->frame;
       int n;
 
       for (n = 1; n < len; ++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.  */
-      char_attr = w32_face_attributes (f, face_id);
+      char_attr = w32_face_attributes (face_id_frame, face_id);
 
       if (n == len)
 	/* This is the last run.  */
@@ -530,6 +536,11 @@ w32con_update_begin (struct frame * f)
 w32con_update_end (struct frame * f)
 {
   SetConsoleCursorPosition (cur_screen, cursor_coords);
+  if (!XWINDOW (selected_window)->cursor_off_p
+      && cursor_coords.X < FRAME_COLS (f))
+    w32con_show_cursor ();
+  else
+    w32con_hide_cursor ();
 }
 
 /***********************************************************************
modified   src/w32inevt.c
@@ -471,8 +471,13 @@ do_mouse_event (MOUSE_EVENT_RECORD *event,
   DWORD but_change, mask, flags = event->dwEventFlags;
   int i;
 
-  /* Mouse didn't move unless MOUSE_MOVED says it did.  */
   struct frame *f = get_frame ();
+
+  /* For now, mouse events on child frames are ignored, because the
+     coordinate conversion is not in place; FIXME.  */
+  if (FRAMEP (f->parent_frame))
+    return 0;
+  /* Mouse didn't move unless MOUSE_MOVED says it did.  */
   f->mouse_moved = 0;
 
   switch (flags)
@@ -619,6 +624,10 @@ maybe_generate_resize_event (void)
   CONSOLE_SCREEN_BUFFER_INFO info;
   struct frame *f = get_frame ();
 
+  /* Only resize the root frame.  */
+  if (FRAMEP (f->parent_frame))
+    return;
+
   GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &info);
 
   /* It is okay to call this unconditionally, since it will do nothing
modified   src/w32term.c
@@ -6405,14 +6405,13 @@ w32_read_socket (struct terminal *terminal,
 	if (FRAME_TOOLTIP_P (f))
 	  continue;
 
-	/* Check "visible" frames and mark each as obscured or not.
+	/* Check "visible" frames and mark each as visible or not.
 	   Note that visible is nonzero for unobscured and obscured
 	   frames, but zero for hidden and iconified frames.  */
 	if (FRAME_W32_P (f) && FRAME_VISIBLE_P (f))
 	  {
 	    RECT clipbox;
 	    HDC  hdc;
-	    bool obscured;
 
 	    enter_crit ();
 	    /* Query clipping rectangle for the entire window area
@@ -6426,29 +6425,11 @@ w32_read_socket (struct terminal *terminal,
 	    ReleaseDC (FRAME_W32_WINDOW (f), hdc);
 	    leave_crit ();
 
-	    obscured = FRAME_OBSCURED_P (f);
-
-	    if (clipbox.right == clipbox.left || clipbox.bottom == clipbox.top)
-	      {
-		/* Frame has become completely obscured so mark as such (we
-		   do this by setting visible to 2 so that FRAME_VISIBLE_P
-		   is still true, but redisplay will skip it).  */
-		SET_FRAME_VISIBLE (f, 2);
-
-		if (!obscured)
-		  DebPrint (("frame %p (%s) obscured\n", f, SDATA (f->name)));
-	      }
-	    else
+	    if (!(clipbox.right == clipbox.left
+		  || clipbox.bottom == clipbox.top))
 	      {
 		/* Frame is not obscured, so mark it as such.  */
 		SET_FRAME_VISIBLE (f, 1);
-
-		if (obscured)
-		  {
-		    SET_FRAME_GARBAGED (f);
-		    DebPrint (("obscured frame %p (%s) found to be visible\n",
-			       f, SDATA (f->name)));
-		  }
 	      }
 	  }
       }
modified   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)
 	{
@@ -13415,18 +13413,22 @@ clear_message (bool current_p, bool last_displayed_p)
   message_buf_print = false;
 }
 
-/* Clear garbaged frames.
+/* Clear garbaged frames.  Value is true if current matrices have been
+   cleared on at least one tty frame.  This information is needed to
+   determine if more than one window has to be updated on ttys, whose
+   update requires building a frame matrix from window matrices.
 
    This function is used where the old redisplay called
    redraw_garbaged_frames which in turn called redraw_frame which in
    turn called clear_frame.  The call to clear_frame was a source of
    flickering.  I believe a clear_frame is not necessary.  It should
    suffice in the new redisplay to invalidate all current matrices,
-   and ensure a complete redisplay of all windows.  */
+   and ensure a complete redisplay of all windows. */
 
-static void
+static bool
 clear_garbaged_frames (void)
 {
+  bool current_matrices_cleared = false;
   if (frame_garbaged)
     {
       Lisp_Object tail, frame;
@@ -13448,6 +13450,8 @@ clear_garbaged_frames (void)
 		redraw_frame (f);
 	      else
 		clear_current_matrices (f);
+	      if (is_tty_frame (f))
+		current_matrices_cleared = true;
 
 #ifdef HAVE_WINDOW_SYSTEM
               if (FRAME_WINDOW_P (f)
@@ -13462,6 +13466,8 @@ clear_garbaged_frames (void)
 
       frame_garbaged = false;
     }
+
+  return current_matrices_cleared;
 }
 
 
@@ -13554,7 +13560,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,16 +17043,22 @@ redisplay_internal (void)
   if (face_change)
     windows_or_buffers_changed = 47;
 
+  struct frame *previous_frame;
   if ((FRAME_TERMCAP_P (sf) || FRAME_MSDOS_P (sf))
-      && FRAME_TTY (sf)->previous_frame != sf)
+      && (previous_frame = FRAME_TTY (sf)->previous_frame,
+	  previous_frame != sf))
     {
-      /* Since frames on a single ASCII terminal share the same
-	 display area, displaying a different frame means redisplay
-	 the whole thing.  */
-      SET_FRAME_GARBAGED (sf);
+      if (previous_frame == NULL
+	  || root_frame (previous_frame) != root_frame (sf))
+	{
+	  /* Since frames on a single terminal share the same display
+	     area, displaying a different frame means redisplay the
+	     whole thing.  */
+	  SET_FRAME_GARBAGED (sf);
 #if !defined DOS_NT && !defined HAVE_ANDROID
-      set_tty_color_mode (FRAME_TTY (sf), sf);
+	  set_tty_color_mode (FRAME_TTY (sf), sf);
 #endif
+	}
       FRAME_TTY (sf)->previous_frame = sf;
     }
 
@@ -17055,6 +17071,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;
@@ -17083,7 +17100,7 @@ redisplay_internal (void)
   do_pending_window_change (true);
 
   /* Clear frames marked as garbaged.  */
-  clear_garbaged_frames ();
+  bool current_matrices_cleared = clear_garbaged_frames ();
 
   /* Build menubar and tool-bar items.  */
   if (NILP (Vmemory_full))
@@ -17174,7 +17191,8 @@ redisplay_internal (void)
   overlay_arrows_changed_p (true);
 
   consider_all_windows_p = (update_mode_lines
-			    || windows_or_buffers_changed);
+			    || windows_or_buffers_changed
+			    || current_matrices_cleared);
 
 #define AINC(a,i)							\
   {									\
@@ -17198,7 +17216,6 @@ #define AINC(a,i)							\
       && !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 +17464,28 @@ #define AINC(a,i)							\
 
       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_frame (f))
+	    {
+	      /* Ignore all invisble tty frames, children or root.  */
+	      if (!FRAME_VISIBLE_P (root_frame (f)))
+		continue;
+
+	      /* Remember tty root frames which we've seen.  */
+	      if (!FRAME_PARENT_FRAME (f)
+		  && NILP (assq_no_quit (frame, tty_root_frames)))
+		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 +17502,7 @@ #define AINC(a,i)							\
 	      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 +17528,7 @@ #define AINC(a,i)							\
 	      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))
+	      if (FRAME_REDISPLAY_P (f))
 		{
 		  /* If fonts changed on visible frame, display again.  */
 		  if (f->fonts_changed)
@@ -17590,6 +17613,9 @@ #define AINC(a,i)							\
 	    }
 	}
 
+      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 +17637,7 @@ #define AINC(a,i)							\
 	    }
 	}
     }
-  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 +17688,7 @@ #define AINC(a,i)							\
 	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 +17699,10 @@ #define AINC(a,i)							\
 
 	  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 +17715,12 @@ #define AINC(a,i)							\
       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))
+      if (mini_frame != sf)
 	{
 	  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 +24007,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 +24027,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 +24036,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 +24401,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 +24414,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 +27270,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 +27291,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 +27340,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;
modified   src/xfaces.c
@@ -696,7 +696,6 @@ init_frame_faces (struct frame *f)
 free_frame_faces (struct frame *f)
 {
   struct face_cache *face_cache = FRAME_FACE_CACHE (f);
-
   if (face_cache)
     {
       free_face_cache (face_cache);


[-- Attachment #3: changelog --]
[-- Type: text/plain, Size: 2398 bytes --]

2024-12-19  Gerd Möllmann  <gerd.moellmann@gmail.com>

	* src/dispnew.c (check_rows): New function, #if 0.
	(frame_matrix_frame): Variable removed.
	(line_hash_code): Take glyph's frame into account.
	(build_frame_matrix_from_leaf_window): Do not copy glyphs from
	rows that aren't enabled.
	(fill_up_glyph_row_with_spaces): Add frame parameter, uses
	changed.
	(fill_up_glyph_row_area_with_spaces): Add frame parameter. Set
	glyph's frame to it.
	(fill_up_frame_row_with_spaces): Ditto.
	(set_frame_matrix_frame): Function removed.

	* src/dispextern.h (struct glyph): Add member 'frame'.
	(CHAR_GLYPH_SPACE_P): Add FRAME parameter. All uses changed.
	(GLYPH_EQUAL_P): Compare glyphs' frame.
	(SET_CHAR_GLYPH): Add parameter FRAME.
	(SET_CHAR_GLYPH_FROM_GLYPH): Ditto.

	* src/chartab.c (Fmake_char_table): Allow more than 10 display
	table slots.

	* lisp/xt-mouse.el (xterm-mouse--handle-mouse-movement): Use new
	terminal parameter xterm-mouse-frame.
	(xterm-mouse-position-function): Ditto.
	(xterm-mouse-event): Determine frame under mouse and compute
	frame-relative coordinates. Set terminal parameter
	xterm-mouse-frame.

	* lisp/tty-tip.el: New file implementing tooltip for ttys.

	* lisp/paren.el (show-paren-function): Don't check if
	display-graphics-p when using child frames.

	* lisp/frame.el (frame-at): New function.
	(tty-frame-geometry): Declare C function.
	(frame-geometry): Use tty-frame-geometry.
	(tty-frame-edges): Declare C function.
	(frame-edges): Use tty-frame-edges.
	(tty-frame-list-z-order): Declare C function.
	(frame-list-z-order): Use tty-frame-list-z-order.
	(tty-frame-restack): Declare C function.
	(frame-restack): Use tty-frame-restack.
	(tty-display-pixel-height): Declare C function.
	(display-pixel-height): Use tty-display-pixel-height.
	(tty-display-pixel-width): Declare C function.
	(display-pixel-width): Use tty-display-pixel-width.

	* lisp/disp-table.el (display-table): Increase size to 12.
	(box-horizontal, box-vertical, box-down-right, box-down-left)
	(box-up-right, box-up-left): New display table slot names for
	box-drawing characters.
	(display-table-slot, set-display-table-slot): Extend doc string.
	(describe-display-table): Display new display table slots.
	(standard-display-unicode-special-glyphs): New function setting
	up Unicode characters for display table entries.


^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-19  5:30                                   ` Gerd Möllmann
@ 2024-12-19  7:44                                     ` Gerd Möllmann
  2024-12-19  8:36                                     ` Eli Zaretskii
  1 sibling, 0 replies; 80+ messages in thread
From: Gerd Möllmann @ 2024-12-19  7:44 UTC (permalink / raw)
  To: Jared Finder; +Cc: Eli Zaretskii, stefankangas, acorallo, emacs-devel, rudalics

[-- Attachment #1: Type: text/plain, Size: 1875 bytes --]

Gerd Möllmann <gerd.moellmann@gmail.com> writes:

> Jared Finder <jared@finder.org> writes:
>
>> On 2024-12-18 09:22, Gerd Möllmann wrote:
>>> Eli Zaretskii <eliz@gnu.org> writes:
>>> 
>>>>> From: Gerd Möllmann <gerd.moellmann@gmail.com>
>>>>> Cc: jared@finder.org,  stefankangas@gmail.com,  acorallo@gnu.org,
>>>>>   emacs-devel@gnu.org,  rudalics@gmx.at
>>>>> Date: Wed, 18 Dec 2024 17:39:07 +0100
>>>>>  > What I usually do is produce diffs for the merge, then use "C-x
>>>>> 4 a"
>>>>> > to generate the file/function names, and add a description.
>>>>> The diff I have, so I guess I'll give it a try, when the others
>>>>> agree.
>>>>> Will take a bit, probably. Should I post the result when I have it?
>>>> Feel free, it will probably flash out a few more issues.  I
>>>> actually
>>>> suggest to post some small portion first, to avoid the situation where
>>>> you do all of the job, only to discover that something's amiss or you
>>>> labored more than you had to.
>>> Thanks, will do that.
>>
>> I'll also spend a bit of time to get GPM's mouse input to make the
>> same transformations that Gerd added to xt-mouse.el.  It looks likely
>> very straightforward to do.
>>
>>   -- MJF
>
> Thanks!
>
> And attached is what I came up today, with the help of a lot of coffee.
> The changelog is a page worth of changelog entries, the diff is from
> what I created these. As introductory text I'd use
>
>   This changeset adds support for child frames on ttys.
>
>   The redisplay part is complete.  The frame-handling part supports
>   use-cases like Posframe, Corfu, and child frames acting like tooltips.
>   Other use-cases of child frames are not currently supported.  In
>   particular, trying to create minibuffer-only child frames on ttys will
>   signal an error.

Here's actually a change log for the whole diff


[-- Attachment #2: whole change log --]
[-- Type: text/plain, Size: 7432 bytes --]

2024-12-19  Gerd Möllmann  <gerd.moellmann@gmail.com>

	* src/xfaces.c (free_frame_faces): Change formatting slightly.

	* src/xdisp.c (redisplay_trace, move_tracxe): Print to stderr because
	stdout screws up terminal display.
	(init_iterator): Remove a #ifdef HAVE_WINDOW_SYSTEM.
	(clear_garbaged_frames): Return a bool telling if we cleared matrix.
	(echo_area_display): Use combine_updates on tty frames.
	(redisplay_internal): Changes for redisplay of tty child windows.
	(deep_copy_glyph_row): Take a frame parameter.
	(display_tty_menu_item): Changes because of function signature changes.

	* src/w32term.c (w32_read_socket): Don't use FRAME_OBSCRURED_P,
	which has been removed.

	* src/w32inevt.c (do_mouse_event): Workaround for mouse events on
	child frafmes.

	* src/w32console.c (w32con_write_glyphs, w32con_update_end):
	Use glyphs' frame for faces.

	* src/treesit.c (treesit_load_language): Pacify a warning.

	* src/w32console.c (w32con_clear_end_of_line): Set glyph's frame.

	* src/terminal.c (cursor_to, raw_cursor_to): Handle case that
	frame is a child frame.

	* src/termhooks.h: Declare formerly static functions.

	* src/term.c (tty_hide_cursor, tty_show_cursor): Make externally
	visible.
	(tty_write_glyphs): Determine faces based on a glyph's frame.
	(tty_write_glyphs_with_face): Take a struct face argument instead
	of a face id. Callers changed.
	(tty_insert_glyphs): Use faces, not face ids.
	(append_glyph, append_composite_glyph, append_glyphless_glyph):
	Set glyph's frame.
	(turn_on_face, turn_off_face): Take face argument instead of face
	id. Callers adapted.
	(Fresume_tty): Act on root frame.
	(tty_draw_row_with_mouse_face): Handle child frames.
	(restore_desired_matrix): Make sure glyphs' is live.
	(set_tty_hooks): Set terminal's frame_raise_lower_hook.
	(tty_frame_geometry, Ftty_frame_geometry, Ftty_frame_edges)
	(Ftty_frame_list_z_order, Ftty_frame_restack)
	(tty_display_dimension, Ftty_display_pixel_width)
	(Ftty_display_pixel_height): New functions.
	(syms_of_term): Defsubr new Lisp functions.

	* src/minibuf.c (read_minibuf): Use combine_updates for tty
	frames.

	* src/frame.h (struct frame): Always define parent_frame.  Change
	'visible' to be a boolean. Always define 'undecorated' and
	'no_accept_focus'. Add 'z_order'.
	(FRAME_OBSCURED_P): Removed.
	(FRAME_PARENT_FRAME): Make it a function.
	(SET_FRAME_VISIBLE): Take a bool parameter, not an int.
	(FRAME_INTERNAL_BORDER_WIDTH): Don't special-base HAVE_WINDOW_SYSTEM.

	* src/frame.c (decode_tty_frame): New function.
	(set_menu_bar_lines): Set menu bar lines and height to 0 for tty
	child frames. Compute min height differently.
	(adjust_frame_size): Set FrameCols/Rows only for root tty frames.
	Mark tty root frame garbaged if child frame is adjusted. Run some
	code even if not HAVE_WINDOW_SYSTEM.
	(make_frame): Run some code even if not HAVE_WINDOW_SYSTEM.
	(make_terminal_frame): Implement child frame creation.
	(tty_child_pos_param, tty_child_size_param)
	(tty_child_frame_rect): New functions.
	(Fmake_terminal_frame): Parts rewritten for child frames.
	(do_switch_frame): Add child frame support.
	(Fframe_ancestor_p): Define if not HAVE_WINDOW_SYSTEM.
	(Fmake_frame_visible, Fmake_frame_invisible)
	(Fframe_visible_p, Fraise_frame):
	Handle tty frames differently.
	(store_frame_param): Signal error if trying to re-parent a tty
	child frame.
	(Fframe_parameters): Report some additional tty frame parameters.
	(Fmodify_frame_parameters): Handle tty child frames.
	(Fset_frame_position): Ditto.
	(frame_parms): Define index for additional frame parameters.
	(handle_frame_param): New function.
	(gui_set_frame_parameters_1): Use handle_frame_param.

	* src/disptab.h (DISP_TABLE_EXTRA_SLOTS): Change to 12.
	(enum box): New enumeration.

	* src/dispnew.c (check_rows): New function, #if 0.
	(frame_matrix_frame): Variable removed.
	(line_hash_code): Take glyph's frame into account.
	(build_frame_matrix_from_leaf_window): Do not copy glyphs from
	rows that aren't enabled.
	(fill_up_glyph_row_with_spaces): Add frame parameter, uses
	changed.
	(fill_up_glyph_row_area_with_spaces): Add frame parameter. Set
	glyph's frame to it.
	(fill_up_frame_row_with_spaces): Ditto.
	(set_frame_matrix_frame): Function removed.
	(make_current): Change signature. Callers changed.
	(mirrored_line_dance): Take a frame argument, not a matrix.
	(redraw_frame): Don't clear_frame a child frame.
	(struct rect): New.
	(rect_intersect, frame_pos_abs, frame_rect_abs, root_frame)
	(max_child_z_order, is_frame_ancestor, frames_with_root)
	(frames_with_parent, frame_z_order_cmp, Fframe__z_order_lessp)
	(frames_in_reverse_z_order, tty_raise_lower_frame, is_tty_frame)
	(is_tty_child_frame, is_tty_root_frame, first_enabled_row)
	(make_matrix_current, prepare_desired_root_row)
	(make_glyph_space, neutralize_wide_char, produce_box_glyphs)
	(produce_box_sides, produce_box_line, copy_child_glyphs)
	(update_window_frame, update_initial_frame, flush_terminal)
	(abs_cursor_pos, is_in_matrix, is_cursor_obscured)
	(terminal_cursor_magic, combine_updates_for_frame)
	(combine_updates): New functions.
	(update_frame): Rewritten.
	(Fdisplay__update_for_mouse_movement): Take a MOUSE_FRAME param.
	(syms_of_display): New symbol frame--z-order--lessp, tty-non-selected-cursor.
	New subr Sframe__z_order_lessp. Provide tty-child-frames.

	* src/dispextern.h (struct glyph): Add member 'frame'.
	(CHAR_GLYPH_SPACE_P): Add FRAME parameter. All uses changed.
	(GLYPH_EQUAL_P): Compare glyphs' frame.
	(SET_CHAR_GLYPH): Add parameter FRAME.
	(SET_CHAR_GLYPH_FROM_GLYPH): Ditto.

	* src/chartab.c (Fmake_char_table): Allow more than 10 display
	table slots.

	* lisp/xt-mouse.el (xterm-mouse--handle-mouse-movement): Use new
	terminal parameter xterm-mouse-frame.
	(xterm-mouse-position-function): Ditto.
	(xterm-mouse-event): Determine frame under mouse and compute
	frame-relative coordinates. Set terminal parameter
	xterm-mouse-frame.

	* lisp/tty-tip.el: New file implementing tooltip for ttys.

	* lisp/paren.el (show-paren-function): Don't check if
	display-graphics-p when using child frames.

	* lisp/frame.el (frame-at): New function.
	(tty-frame-geometry): Declare C function.
	(frame-geometry): Use tty-frame-geometry.
	(tty-frame-edges): Declare C function.
	(frame-edges): Use tty-frame-edges.
	(tty-frame-list-z-order): Declare C function.
	(frame-list-z-order): Use tty-frame-list-z-order.
	(tty-frame-restack): Declare C function.
	(frame-restack): Use tty-frame-restack.
	(tty-display-pixel-height): Declare C function.
	(display-pixel-height): Use tty-display-pixel-height.
	(tty-display-pixel-width): Declare C function.
	(display-pixel-width): Use tty-display-pixel-width.

	* lisp/disp-table.el (display-table): Increase size to 12.
	(box-horizontal, box-vertical, box-down-right, box-down-left)
	(box-up-right, box-up-left): New display table slot names for
	box-drawing characters.
	(display-table-slot, set-display-table-slot): Extend doc string.
	(describe-display-table): Display new display table slots.
	(standard-display-unicode-special-glyphs): New function setting
	up Unicode characters for display table entries.

	* .gitignore: Don't ignore patch files, they are useful to see in
	Magit status buffer when applying patches (git am).

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-18 13:54                     ` Eli Zaretskii
  2024-12-18 16:01                       ` Gerd Möllmann
  2024-12-18 21:06                       ` Stefan Kangas
@ 2024-12-19  8:00                       ` Andrea Corallo
  2 siblings, 0 replies; 80+ messages in thread
From: Andrea Corallo @ 2024-12-19  8:00 UTC (permalink / raw)
  To: Eli Zaretskii
  Cc: Jared Finder, Stefan Kangas, gerd.moellmann, emacs-devel,
	rudalics

Eli Zaretskii <eliz@gnu.org> writes:

>> Date: Tue, 17 Dec 2024 21:35:35 -0800
>> From: Jared Finder <jared@finder.org>
>> Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org, rudalics@gmx.at
>> 
>> Things otherwise seem fine with tty child frames. There's certainly 
>> oddness with mouse interaction, but it's not fundamentally broken in any 
>> way, just more things that don't work. In particular:
>> 
>> With xterm-mouse, as I highlighted earlier, I can select the child frame 
>> even if it is set as not selectable. Once a window in a child frame is 
>> selected, I can type there normally.
>> 
>> With gpm mouse, I have the opposite problem. I can never select the 
>> child frame and in fact the mouse behaves as if the child frame isn't 
>> there. Clicking and tooltip text both pay no attention to the child 
>> frame and just act on whatever is behind the child frame.
>> 
>> For both of these, I couldn't get mouse-face or clicking to work on 
>> child frames. I was doing the following:
>> 
>> (setq button (buttonize "[Click me]" (lambda (&rest _) (message 
>> "Clicked!"))))
>> (posframe-show " *buffer*" :string (concat "A\n" button "\nB"))
>> 
>> The posframe would show, but the mouse can't interact with the 
>> buttonized text. This may be a limitation of posframe though, it also 
>> didn't work in graphical mode.
>> 
>> That's really it. I don't see any major issues with child frames. As 
>> long as we're ok with saying that mouse support is not mature, it seems 
>> fine to me.
>
> If we don't see immediate ways of fixing some of these issues, I think
> it should be okay to land this on master, if Stefan and Andrea agree.

Okay for me.

  Andrea



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-19  5:30                                   ` Gerd Möllmann
  2024-12-19  7:44                                     ` Gerd Möllmann
@ 2024-12-19  8:36                                     ` Eli Zaretskii
  2024-12-19  9:04                                       ` Gerd Möllmann
  1 sibling, 1 reply; 80+ messages in thread
From: Eli Zaretskii @ 2024-12-19  8:36 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: jared, stefankangas, acorallo, emacs-devel, rudalics

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: Eli Zaretskii <eliz@gnu.org>,  stefankangas@gmail.com,
>   acorallo@gnu.org,  emacs-devel@gnu.org,  rudalics@gmx.at
> Date: Thu, 19 Dec 2024 06:30:23 +0100
> 
> And attached is what I came up today, with the help of a lot of coffee.
> The changelog is a page worth of changelog entries, the diff is from
> what I created these. As introductory text I'd use
> 
>   This changeset adds support for child frames on ttys.

I'd say

  Add support for child frames on ttys

(without the period).

The ChangeLog part looks reasonable.  My only comment is that you
could collect changes with the same description together.  For
example, instead of

	* lisp/frame.el (frame-at): New function.
	(tty-frame-geometry): Declare C function.
	(tty-frame-edges): Declare C function.
	(tty-frame-list-z-order): Declare C function.
	(tty-frame-restack): Declare C function.
	(tty-display-pixel-height): Declare C function.
	(tty-display-pixel-width): Declare C function.

you could use

	* lisp/frame.el (frame-at): New function.
	(tty-frame-geometry, tty-frame-edges, tty-frame-list-z-order)
	(tty-frame-restack, tty-display-pixel-height)
	(tty-display-pixel-width): Declare C function.

But that's an optimization; what you have is perfectly okay.

Thanks.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-19  8:36                                     ` Eli Zaretskii
@ 2024-12-19  9:04                                       ` Gerd Möllmann
  2024-12-19  9:17                                         ` Gerd Möllmann
  0 siblings, 1 reply; 80+ messages in thread
From: Gerd Möllmann @ 2024-12-19  9:04 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: jared, stefankangas, acorallo, emacs-devel, rudalics

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Gerd Möllmann <gerd.moellmann@gmail.com>
>> Cc: Eli Zaretskii <eliz@gnu.org>,  stefankangas@gmail.com,
>>   acorallo@gnu.org,  emacs-devel@gnu.org,  rudalics@gmx.at
>> Date: Thu, 19 Dec 2024 06:30:23 +0100
>> 
>> And attached is what I came up today, with the help of a lot of coffee.
>> The changelog is a page worth of changelog entries, the diff is from
>> what I created these. As introductory text I'd use
>> 
>>   This changeset adds support for child frames on ttys.
>
> I'd say
>
>   Add support for child frames on ttys
>
> (without the period).

like a caption, okay.

>
> The ChangeLog part looks reasonable.  My only comment is that you
> could collect changes with the same description together.  For
> example, instead of
>
> 	* lisp/frame.el (frame-at): New function.
> 	(tty-frame-geometry): Declare C function.
> 	(tty-frame-edges): Declare C function.
> 	(tty-frame-list-z-order): Declare C function.
> 	(tty-frame-restack): Declare C function.
> 	(tty-display-pixel-height): Declare C function.
> 	(tty-display-pixel-width): Declare C function.
>
> you could use
>
> 	* lisp/frame.el (frame-at): New function.
> 	(tty-frame-geometry, tty-frame-edges, tty-frame-list-z-order)
> 	(tty-frame-restack, tty-display-pixel-height)
> 	(tty-display-pixel-width): Declare C function.
>
> But that's an optimization; what you have is perfectly okay.

Yes, that came out of the order in which stuff appeared in the diff.
I'll do that.


>
> Thanks.

Very good. Thanks for the review! I'll merge when I've made these changes.



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-19  9:04                                       ` Gerd Möllmann
@ 2024-12-19  9:17                                         ` Gerd Möllmann
  2024-12-19 10:34                                           ` Robert Pluim
  0 siblings, 1 reply; 80+ messages in thread
From: Gerd Möllmann @ 2024-12-19  9:17 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: jared, stefankangas, acorallo, emacs-devel, rudalics

Gerd Möllmann <gerd.moellmann@gmail.com> writes:

> Very good. Thanks for the review! I'll merge when I've made these changes.

Done.

And Magit is the best thing since the invention of sliced bread :-).
Thanks to Jonas!



^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-19  9:17                                         ` Gerd Möllmann
@ 2024-12-19 10:34                                           ` Robert Pluim
  2024-12-19 10:40                                             ` Gerd Möllmann
  0 siblings, 1 reply; 80+ messages in thread
From: Robert Pluim @ 2024-12-19 10:34 UTC (permalink / raw)
  To: Gerd Möllmann
  Cc: Eli Zaretskii, jared, stefankangas, acorallo, emacs-devel,
	rudalics

[-- Attachment #1: Type: text/plain, Size: 478 bytes --]

>>>>> On Thu, 19 Dec 2024 10:17:37 +0100, Gerd Möllmann <gerd.moellmann@gmail.com> said:

    Gerd> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
    >> Very good. Thanks for the review! I'll merge when I've made these changes.

    Gerd> Done.

    Gerd> And Magit is the best thing since the invention of sliced bread :-).
    Gerd> Thanks to Jonas!

Iʼve only built it and run 'make check'. Looks like the xt-mouse tests
need some adjusting:

Robert
-- 

[-- Attachment #2: xt-mouse-tests.log --]
[-- Type: application/octet-stream, Size: 4178 bytes --]

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: "Final" version of tty child frames
  2024-12-19 10:34                                           ` Robert Pluim
@ 2024-12-19 10:40                                             ` Gerd Möllmann
  0 siblings, 0 replies; 80+ messages in thread
From: Gerd Möllmann @ 2024-12-19 10:40 UTC (permalink / raw)
  To: Robert Pluim
  Cc: Eli Zaretskii, jared, Stefan Kangas, Andrea Corallo, Emacs Devel,
	Martin Rudalics



> On 19. Dec 2024, at 11:34, Robert Pluim <rpluim@gmail.com> wrote:
> 
>>>>>> On Thu, 19 Dec 2024 10:17:37 +0100, Gerd Möllmann <gerd.moellmann@gmail.com> said:
> 
>    Gerd> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
>>> Very good. Thanks for the review! I'll merge when I've made these changes.
> 
>    Gerd> Done.
> 
>    Gerd> And Magit is the best thing since the invention of sliced bread :-).
>    Gerd> Thanks to Jonas!
> 
> Iʼve only built it and run 'make check'. Looks like the xt-mouse tests
> need some adjusting:
> 
> Robert
> -- 
> <xt-mouse-tests.log>

Thansks, I'll have a look.


^ permalink raw reply	[flat|nested] 80+ messages in thread

end of thread, other threads:[~2024-12-19 10:40 UTC | newest]

Thread overview: 80+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-22  4:46 "Final" version of tty child frames Gerd Möllmann
2024-10-22  5:29 ` Eli Zaretskii
2024-10-22  9:58   ` martin rudalics
2024-10-22 10:20     ` Eli Zaretskii
2024-10-22 14:01       ` martin rudalics
2024-10-22 14:23         ` Eli Zaretskii
2024-10-22 10:40     ` Gerd Möllmann
2024-10-22 11:43       ` Po Lu
2024-10-22 13:44       ` Eli Zaretskii
2024-10-22 14:01         ` Gerd Möllmann
2024-10-22 14:02       ` martin rudalics
2024-10-28  4:35   ` Jared Finder
2024-10-28  5:57     ` Gerd Möllmann
2024-11-30 11:25     ` Eli Zaretskii
2024-12-05  3:49       ` Jared Finder
2024-12-11  7:31         ` Jared Finder
2024-12-11  7:59           ` Gerd Möllmann
2024-12-12  5:11             ` Jared Finder
2024-12-12  6:20               ` Gerd Möllmann
2024-12-12  6:48                 ` Gerd Möllmann
2024-12-12  6:30               ` Eli Zaretskii
2024-12-12  7:04                 ` Gerd Möllmann
2024-12-18  5:35                   ` Jared Finder
2024-12-18  6:25                     ` Gerd Möllmann
2024-12-18 13:54                     ` Eli Zaretskii
2024-12-18 16:01                       ` Gerd Möllmann
2024-12-18 16:29                         ` Eli Zaretskii
2024-12-18 16:39                           ` Gerd Möllmann
2024-12-18 17:02                             ` Eli Zaretskii
2024-12-18 17:22                               ` Gerd Möllmann
2024-12-19  5:17                                 ` Jared Finder
2024-12-19  5:30                                   ` Gerd Möllmann
2024-12-19  7:44                                     ` Gerd Möllmann
2024-12-19  8:36                                     ` Eli Zaretskii
2024-12-19  9:04                                       ` Gerd Möllmann
2024-12-19  9:17                                         ` Gerd Möllmann
2024-12-19 10:34                                           ` Robert Pluim
2024-12-19 10:40                                             ` Gerd Möllmann
2024-12-18 21:06                       ` Stefan Kangas
2024-12-19  8:00                       ` Andrea Corallo
2024-12-11  9:39           ` martin rudalics
2024-10-22  7:34 ` Dr. Arne Babenhauserheide
2024-10-22  7:49   ` Gerd Möllmann
2024-10-22  7:49   ` Eli Zaretskii
2024-10-22  8:01 ` Eli Zaretskii
2024-10-22  8:21   ` Gerd Möllmann
2024-10-22  8:57     ` Eli Zaretskii
2024-10-22  9:42     ` Eli Zaretskii
2024-10-22 10:23       ` Gerd Möllmann
2024-10-22 13:35         ` Eli Zaretskii
2024-10-22 13:43           ` Gerd Möllmann
2024-10-22 13:55             ` Eli Zaretskii
2024-10-22 14:02               ` Gerd Möllmann
2024-10-22 14:40                 ` Eli Zaretskii
2024-10-22 19:19                   ` Paul Eggert
2024-10-23  3:18                     ` Gerd Möllmann
2024-10-22 10:43       ` Gerd Möllmann
2024-10-23  3:05 ` Feng Shu
2024-10-23  3:13   ` Gerd Möllmann
2024-10-23  3:25     ` Feng Shu
2024-10-23  3:36       ` Gerd Möllmann
2024-10-23  3:44         ` Feng Shu
2024-10-23  4:09           ` Gerd Möllmann
2024-10-23  4:40             ` Gerd Möllmann
2024-10-23  5:00               ` Gerd Möllmann
2024-10-23  7:49                 ` Eli Zaretskii
2024-10-23  8:12                   ` Gerd Möllmann
2024-10-23 11:04                   ` Gerd Möllmann
2024-10-23 17:23                     ` Eli Zaretskii
2024-10-23 17:52                       ` Gerd Möllmann
2024-10-23  6:54               ` Feng Shu
2024-10-23  7:25                 ` Gerd Möllmann
2024-10-23  7:28               ` Eli Zaretskii
2024-10-23  7:37                 ` Gerd Möllmann
2024-10-23  7:52                   ` Feng Shu
2024-10-23  8:07                     ` Gerd Möllmann
2024-10-23  9:07                       ` Feng Shu
2024-10-23  9:58                         ` Gerd Möllmann
2024-10-23  7:11   ` Eli Zaretskii
2024-10-26  8:15 ` Gerd Möllmann

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).