all messages for Emacs-related lists mirrored at yhetil.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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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
  2025-01-04 22:09             ` Jared Finder
  1 sibling, 1 reply; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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
  2025-01-04 22:12                       ` Jared Finder
  2024-12-18 13:54                     ` Eli Zaretskii
  1 sibling, 1 reply; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ 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; 194+ 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] 194+ messages in thread

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

On 2024-12-11 01:39, martin rudalics wrote:

> 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.

TTY child frame cursors behave even worse under GPM and a TERM=linux 
terminal.  In this case, the cursor ends up on some lines always 
appearing at the end of lines (ignoring separate frames or windows) and 
sometimes in the right place.  The point position is correct though and 
the cursor does appear correctly once I start pressing simple keys like 
'A'.

Unfortunately, the redisplay code in Emacs is super hard for me to 
understand and I can't figure out where or how the cursor gets 
positioned.  If someone could give me some pointers here, I'd love to 
investigate further.

   -- MJF



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

* Re: "Final" version of tty child frames
  2024-12-18  6:25                     ` Gerd Möllmann
@ 2025-01-04 22:12                       ` Jared Finder
  2025-01-05  4:03                         ` Gerd Möllmann
  2025-01-05  7:07                         ` Eli Zaretskii
  0 siblings, 2 replies; 194+ messages in thread
From: Jared Finder @ 2025-01-04 22:12 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Eli Zaretskii, emacs-devel, rudalics

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

On 2024-12-17 22:25, Gerd Möllmann wrote:

> 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.

It was that easy to do.  Attached is a patch that adds support for TTY 
child frames to a GPM mouse.

   -- MJF

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Support-TTY-child-frames-with-GPM-mouse.patch --]
[-- Type: text/x-diff; name=0001-Support-TTY-child-frames-with-GPM-mouse.patch, Size: 4948 bytes --]

From 4d17c5ab27be6adb8437d7a685b1a9f8fee0ade5 Mon Sep 17 00:00:00 2001
From: Jared Finder <jared@finder.org>
Date: Sat, 4 Jan 2025 13:55:05 -0800
Subject: [PATCH] Support TTY child frames with GPM mouse

* src/term.c (frame_at): New C function, wraps Lisp `frame-at'.
(handle_one_term_event): Call frame_at to get frame under mouse,
store it in last_mouse_frame.  Alter event coordinates based on
mouse frame.
(term_mouse_position): Use last_mouse_frame when it is set.
(syms_of_term): Add Qframe_at.
* src/termhooks.h: Make Gpm_Event parameter const.
---
 src/term.c      | 52 +++++++++++++++++++++++++++++++++++++------------
 src/termhooks.h |  2 +-
 2 files changed, 41 insertions(+), 13 deletions(-)

diff --git a/src/term.c b/src/term.c
index 368e20803e1..bbf0bafb93f 100644
--- a/src/term.c
+++ b/src/term.c
@@ -141,6 +141,7 @@ #define OUTPUT1_IF(tty, a) \
 struct tty_display_info *gpm_tty = NULL;
 
 /* Last recorded mouse coordinates.  */
+static Lisp_Object last_mouse_frame;
 static int last_mouse_x, last_mouse_y;
 #endif /* HAVE_GPM */
 
@@ -2638,7 +2639,11 @@ term_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window,
 		     enum scroll_bar_part *part, Lisp_Object *x,
 		     Lisp_Object *y, Time *timeptr)
 {
-  *fp = SELECTED_FRAME ();
+  /* This code runs even when `gpm-mouse-mode' was never active, like
+     inside an xterm.  In such cases, last_mouse_frame will never be
+     set, so fallback to SELECTED_FRAME. */
+  *fp = FRAMEP (last_mouse_frame) ? XFRAME (last_mouse_frame)
+    : SELECTED_FRAME ();
   (*fp)->mouse_moved = 0;
 
   *bar_window = Qnil;
@@ -2712,10 +2717,28 @@ term_mouse_click (struct input_event *result, Gpm_Event *event,
   return Qnil;
 }
 
+static Lisp_Object
+frame_at(int x, int y)
+{
+  Lisp_Object frame = CALLN (Ffuncall, Qframe_at,
+			     make_fixnum (x), make_fixnum (y));
+  if (NILP (frame))
+    {
+      frame = selected_frame;
+    }
+
+  return frame;
+}
+
 int
-handle_one_term_event (struct tty_display_info *tty, Gpm_Event *event)
+handle_one_term_event (struct tty_display_info *tty, const Gpm_Event *event_in)
 {
-  struct frame *f = XFRAME (tty->top_frame);
+  Lisp_Object frame = frame_at(event_in->x, event_in->y);
+  struct frame *f = XFRAME (frame);
+  Gpm_Event event = *event_in;
+  event.x -= f->left_pos;
+  event.y -= f->top_pos;
+
   struct input_event ie;
   int count = 0;
 
@@ -2723,30 +2746,34 @@ handle_one_term_event (struct tty_display_info *tty, Gpm_Event *event)
   ie.kind = NO_EVENT;
   ie.arg = Qnil;
 
-  if (event->type & (GPM_MOVE | GPM_DRAG))
+  if (event.type & (GPM_MOVE | GPM_DRAG))
     {
-      Gpm_DrawPointer (event->x, event->y, fileno (tty->output));
+      /* The pointer must be drawn using screen coordinates (x,y), not
+	 frame coordinates. Use event_in which has an unmodified event
+	 directly from GPM. */
+      Gpm_DrawPointer (event_in->x, event_in->y, fileno (tty->output));
 
       /* Has the mouse moved off the glyph it was on at the last
          sighting?  */
-      if (event->x != last_mouse_x || event->y != last_mouse_y)
+      if (event.x != last_mouse_x || event.y != last_mouse_y)
         {
-          /* FIXME: These three lines can not be moved into
+          /* FIXME: These four lines can not be moved into
              update_mouse_position unless xterm-mouse gets updated to
              generate mouse events via C code.  See
              https://lists.gnu.org/archive/html/emacs-devel/2020-11/msg00163.html */
-          last_mouse_x = event->x;
-          last_mouse_y = event->y;
+          last_mouse_frame = frame;
+          last_mouse_x = event.x;
+          last_mouse_y = event.y;
           f->mouse_moved = 1;
 
-          count += update_mouse_position (f, event->x, event->y);
+          count += update_mouse_position (f, event.x, event.y);
         }
     }
   else
     {
       f->mouse_moved = 0;
-      term_mouse_click (&ie, event, f);
-      ie.arg = tty_handle_tab_bar_click (f, event->x, event->y,
+      term_mouse_click (&ie, &event, f);
+      ie.arg = tty_handle_tab_bar_click (f, event.x, event.y,
 					 (ie.modifiers & down_modifier) != 0, &ie);
       kbd_buffer_store_event (&ie);
       count++;
@@ -4970,6 +4997,7 @@ syms_of_term (void)
 #ifdef HAVE_GPM
   defsubr (&Sgpm_mouse_start);
   defsubr (&Sgpm_mouse_stop);
+  DEFSYM (Qframe_at, "frame-at");
 #endif /* HAVE_GPM */
 
   defsubr (&Stty_frame_geometry);
diff --git a/src/termhooks.h b/src/termhooks.h
index b32804a57b3..0795148f1af 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -458,7 +458,7 @@ #define EVENT_INIT(event) (memset (&(event), 0, sizeof (struct input_event)), \
 
 #ifdef HAVE_GPM
 #include <gpm.h>
-extern int handle_one_term_event (struct tty_display_info *, Gpm_Event *);
+extern int handle_one_term_event (struct tty_display_info *, const Gpm_Event *);
 extern void term_mouse_moveto (int, int);
 
 /* The device for which we have enabled gpm support.  */
-- 
2.39.5


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

* Re: "Final" version of tty child frames
  2025-01-04 22:09             ` Jared Finder
@ 2025-01-05  3:48               ` Gerd Möllmann
  2025-01-13  6:32                 ` Jared Finder
  2025-01-05  6:46               ` Eli Zaretskii
  2025-01-08  9:56               ` martin rudalics
  2 siblings, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-05  3:48 UTC (permalink / raw)
  To: Jared Finder; +Cc: martin rudalics, Eli Zaretskii, emacs-devel

Jared Finder <jared@finder.org> writes:

> Unfortunately, the redisplay code in Emacs is super hard for me to
> understand and I can't figure out where or how the cursor gets
> positioned.  If someone could give me some pointers here, I'd love to
> investigate further.

I think you want to take a look at dispnew.c, functions
combine_updates_for_frame, and there at this part

  /* If a child is displayed, and the cursor is displayed in another
     frame, the child might lay above the cursor, so that it appears to
     "shine through" the child.  Avoid that because it's confusing.  */
  if (topmost_child)
    terminal_cursor_magic (root, topmost_child);
  flush_terminal (root




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

* Re: "Final" version of tty child frames
  2025-01-04 22:12                       ` Jared Finder
@ 2025-01-05  4:03                         ` Gerd Möllmann
  2025-01-05  7:07                         ` Eli Zaretskii
  1 sibling, 0 replies; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-05  4:03 UTC (permalink / raw)
  To: Jared Finder; +Cc: Eli Zaretskii, emacs-devel, rudalics

Jared Finder <jared@finder.org> writes:

> It was that easy to do.  Attached is a patch that adds support for TTY
> child frames to a GPM mouse.

Thanks!

> diff --git a/src/term.c b/src/term.c
> index 368e20803e1..bbf0bafb93f 100644
> --- a/src/term.c
> +++ b/src/term.c
> @@ -141,6 +141,7 @@ #define OUTPUT1_IF(tty, a) \
>  struct tty_display_info *gpm_tty = NULL;
>  
>  /* Last recorded mouse coordinates.  */
> +static Lisp_Object last_mouse_frame;
>  static int last_mouse_x, last_mouse_y;
>  #endif /* HAVE_GPM */

I think last_mouse_frame needs a staticpro (&last_mouse_frame) in
syms_of_term. Can't say anything else about GPM code, of course :-).



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

* Re: "Final" version of tty child frames
  2025-01-04 22:09             ` Jared Finder
  2025-01-05  3:48               ` Gerd Möllmann
@ 2025-01-05  6:46               ` Eli Zaretskii
  2025-01-06  0:18                 ` Jared Finder
  2025-01-08  9:56               ` martin rudalics
  2 siblings, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-05  6:46 UTC (permalink / raw)
  To: Jared Finder; +Cc: rudalics, gerd.moellmann, emacs-devel

> Date: Sat, 04 Jan 2025 14:09:55 -0800
> From: Jared Finder <jared@finder.org>
> Cc: Eli Zaretskii <eliz@gnu.org>, gerd.moellmann@gmail.com,
>  emacs-devel@gnu.org
> 
> TTY child frame cursors behave even worse under GPM and a TERM=linux 
> terminal.  In this case, the cursor ends up on some lines always 
> appearing at the end of lines (ignoring separate frames or windows) and 
> sometimes in the right place.  The point position is correct though and 
> the cursor does appear correctly once I start pressing simple keys like 
> 'A'.
> 
> Unfortunately, the redisplay code in Emacs is super hard for me to 
> understand and I can't figure out where or how the cursor gets 
> positioned.  If someone could give me some pointers here, I'd love to 
> investigate further.

If you mean the text cursor, then it is positioned by calling cmgoto,
which is called from tty_cursor_to and tty_raw_cursor_to.

Frankly, I don't understand how the mouse could affect the cursor
positioning.  Do you mean the cursor position is wrong when you click
the mouse?



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

* Re: "Final" version of tty child frames
  2025-01-04 22:12                       ` Jared Finder
  2025-01-05  4:03                         ` Gerd Möllmann
@ 2025-01-05  7:07                         ` Eli Zaretskii
  2025-01-06  0:05                           ` Jared Finder
  1 sibling, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-05  7:07 UTC (permalink / raw)
  To: Jared Finder; +Cc: gerd.moellmann, emacs-devel, rudalics

> Date: Sat, 04 Jan 2025 14:12:00 -0800
> From: Jared Finder <jared@finder.org>
> Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org, rudalics@gmx.at
> 
> It was that easy to do.  Attached is a patch that adds support for TTY 
> child frames to a GPM mouse.

Thanks.

> @@ -2638,7 +2639,11 @@ term_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window,
>  		     enum scroll_bar_part *part, Lisp_Object *x,
>  		     Lisp_Object *y, Time *timeptr)
>  {
> -  *fp = SELECTED_FRAME ();
> +  /* This code runs even when `gpm-mouse-mode' was never active, like
> +     inside an xterm.  In such cases, last_mouse_frame will never be
> +     set, so fallback to SELECTED_FRAME. */
> +  *fp = FRAMEP (last_mouse_frame) ? XFRAME (last_mouse_frame)
> +    : SELECTED_FRAME ();

Does this mean child frames on xterm will have the selected-frame set
to a child frame, and thus do not need this trick with
last_mouse_frame?  Or what other difference between xterm and the
Linux console hides behind this "fallback"?

> +static Lisp_Object
> +frame_at(int x, int y)
           ^
Missing space there.

> +{
> +  Lisp_Object frame = CALLN (Ffuncall, Qframe_at,
> +			     make_fixnum (x), make_fixnum (y));

I'd refrain from calling into Lisp here: frame-at will call
tty-frame-list-z-order, which is implemented in C, and the rest of
frame-at can be trivially done in C.

> +  if (NILP (frame))
> +    {
> +      frame = selected_frame;
> +    }

Redundant braces.

> +handle_one_term_event (struct tty_display_info *tty, const Gpm_Event *event_in)
>  {
> -  struct frame *f = XFRAME (tty->top_frame);
> +  Lisp_Object frame = frame_at(event_in->x, event_in->y);
                                 ^
Missing space.

> +      /* The pointer must be drawn using screen coordinates (x,y), not
> +	 frame coordinates. Use event_in which has an unmodified event
                          ^^
Two spaces between sentences.

> +	 directly from GPM. */
                          ^^
Same at the end of a comment.



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

* Re: "Final" version of tty child frames
  2025-01-05  7:07                         ` Eli Zaretskii
@ 2025-01-06  0:05                           ` Jared Finder
  2025-01-06  4:27                             ` Gerd Möllmann
  2025-01-06 13:30                             ` Eli Zaretskii
  0 siblings, 2 replies; 194+ messages in thread
From: Jared Finder @ 2025-01-06  0:05 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gerd.moellmann, emacs-devel, rudalics

On 2025-01-04 23:07, Eli Zaretskii wrote:
>> Date: Sat, 04 Jan 2025 14:12:00 -0800
>> From: Jared Finder <jared@finder.org>
>> Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org, rudalics@gmx.at
>> 
>> It was that easy to do.  Attached is a patch that adds support for TTY
>> child frames to a GPM mouse.
> 
> Thanks.

Style comments addressed, thank you! Someday, I will internalize Emacs' 
unique C style conventions.  Probably once I only code for Emacs and not 
other projects. :)

>> @@ -2638,7 +2639,11 @@ term_mouse_position (struct frame **fp, int 
>> insist, Lisp_Object *bar_window,
>>  		     enum scroll_bar_part *part, Lisp_Object *x,
>>  		     Lisp_Object *y, Time *timeptr)
>>  {
>> -  *fp = SELECTED_FRAME ();
>> +  /* This code runs even when `gpm-mouse-mode' was never active, like
>> +     inside an xterm.  In such cases, last_mouse_frame will never be
>> +     set, so fallback to SELECTED_FRAME. */
>> +  *fp = FRAMEP (last_mouse_frame) ? XFRAME (last_mouse_frame)
>> +    : SELECTED_FRAME ();
> 
> Does this mean child frames on xterm will have the selected-frame set
> to a child frame, and thus do not need this trick with
> last_mouse_frame?  Or what other difference between xterm and the
> Linux console hides behind this "fallback"?

This is just keeping the behavior that already exists today.  I was 
surprised by the current behavior: that term_mouse_position which only 
works for a GPM enabled mouse gets called even when GPM could not be 
used.  It could make sense to change this behavior however it would be a 
bigger change as gpm-mouse-mode is enabled by default even for non-GPM 
supporting terminals.  I suspect that GPM support is equivalent to 
TERM=linux, but I may be wrong.

This works when xterm-mouse-mode is enabled because mouse_position_hook 
(set to term_mouse_position) is called first, followed by 
mouse-position-function (set to xterm-mouse-position-function).  On ttys 
with xterm-mouse-mode enabled the return value from term_mouse_position 
is ignored.  See existing definitions in Fmouse_pixel_position and 
mouse_position in frame.c.

>> +{
>> +  Lisp_Object frame = CALLN (Ffuncall, Qframe_at,
>> +			     make_fixnum (x), make_fixnum (y));
> 
> I'd refrain from calling into Lisp here: frame-at will call
> tty-frame-list-z-order, which is implemented in C, and the rest of
> frame-at can be trivially done in C.

Would it make sense to delete the Lisp implementation of frame-at and 
make it implemented in C only? The only other existing client of 
frame-at is xterm-mouse-mode for the same use case.  Looking forward, I 
expect that getting a window by screen pixel coordinate will be less 
available over time.  Wayland and Android specifically do not support 
this for arbitrary pixels for security reasons.

   -- MJF



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

* Re: "Final" version of tty child frames
  2025-01-05  6:46               ` Eli Zaretskii
@ 2025-01-06  0:18                 ` Jared Finder
  2025-01-06 13:35                   ` Eli Zaretskii
  0 siblings, 1 reply; 194+ messages in thread
From: Jared Finder @ 2025-01-06  0:18 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: rudalics, gerd.moellmann, emacs-devel

On 2025-01-04 22:46, Eli Zaretskii wrote:
>> Date: Sat, 04 Jan 2025 14:09:55 -0800
>> From: Jared Finder <jared@finder.org>
>> Cc: Eli Zaretskii <eliz@gnu.org>, gerd.moellmann@gmail.com,
>>  emacs-devel@gnu.org
>> 
>> TTY child frame cursors behave even worse under GPM and a TERM=linux
>> terminal.  In this case, the cursor ends up on some lines always
>> appearing at the end of lines (ignoring separate frames or windows) 
>> and
>> sometimes in the right place.  The point position is correct though 
>> and
>> the cursor does appear correctly once I start pressing simple keys 
>> like
>> 'A'.
>> 
>> Unfortunately, the redisplay code in Emacs is super hard for me to
>> understand and I can't figure out where or how the cursor gets
>> positioned.  If someone could give me some pointers here, I'd love to
>> investigate further.
> 
> If you mean the text cursor, then it is positioned by calling cmgoto,
> which is called from tty_cursor_to and tty_raw_cursor_to.
> 
> Frankly, I don't understand how the mouse could affect the cursor
> positioning.  Do you mean the cursor position is wrong when you click
> the mouse?

Emacs understanding of point ends up correct when I click the mouse, but 
the cursor is incorrect.  This only applies to some lines.  I get 
similar issues when using arrow keys for moving the point around as 
well.  I do not see the same behavior in an xterm.  This predates my 
change.  It only happens when tty child frames are visible with 
TERM=linux, as far as I can tell.

   -- MJF



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

* Re: "Final" version of tty child frames
  2025-01-06  0:05                           ` Jared Finder
@ 2025-01-06  4:27                             ` Gerd Möllmann
  2025-01-06 13:30                             ` Eli Zaretskii
  1 sibling, 0 replies; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-06  4:27 UTC (permalink / raw)
  To: Jared Finder; +Cc: Eli Zaretskii, emacs-devel, rudalics

Jared Finder <jared@finder.org> writes:

> Would it make sense to delete the Lisp implementation of frame-at and
> make it implemented in C only? 

Hi Jared. That makes a lot of sense to me, at least.



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

* Re: "Final" version of tty child frames
  2025-01-06  0:05                           ` Jared Finder
  2025-01-06  4:27                             ` Gerd Möllmann
@ 2025-01-06 13:30                             ` Eli Zaretskii
  2025-01-07  5:40                               ` Jared Finder
  1 sibling, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-06 13:30 UTC (permalink / raw)
  To: Jared Finder; +Cc: gerd.moellmann, emacs-devel, rudalics

> Date: Sun, 05 Jan 2025 16:05:59 -0800
> From: Jared Finder <jared@finder.org>
> Cc: gerd.moellmann@gmail.com, emacs-devel@gnu.org, rudalics@gmx.at
> 
> Style comments addressed, thank you! Someday, I will internalize Emacs' 
> unique C style conventions.  Probably once I only code for Emacs and not 
> other projects. :)

FTR, they are not Emacs conventions, they are GNU conventions.

> > Does this mean child frames on xterm will have the selected-frame set
> > to a child frame, and thus do not need this trick with
> > last_mouse_frame?  Or what other difference between xterm and the
> > Linux console hides behind this "fallback"?
> 
> This is just keeping the behavior that already exists today.  I was 
> surprised by the current behavior: that term_mouse_position which only 
> works for a GPM enabled mouse gets called even when GPM could not be 
> used.  It could make sense to change this behavior however it would be a 
> bigger change as gpm-mouse-mode is enabled by default even for non-GPM 
> supporting terminals.  I suspect that GPM support is equivalent to 
> TERM=linux, but I may be wrong.
> 
> This works when xterm-mouse-mode is enabled because mouse_position_hook 
> (set to term_mouse_position) is called first, followed by 
> mouse-position-function (set to xterm-mouse-position-function).  On ttys 
> with xterm-mouse-mode enabled the return value from term_mouse_position 
> is ignored.  See existing definitions in Fmouse_pixel_position and 
> mouse_position in frame.c.

Maybe term_mouse_position should return immediately if GPM is not
enabled?

> >> +{
> >> +  Lisp_Object frame = CALLN (Ffuncall, Qframe_at,
> >> +			     make_fixnum (x), make_fixnum (y));
> > 
> > I'd refrain from calling into Lisp here: frame-at will call
> > tty-frame-list-z-order, which is implemented in C, and the rest of
> > frame-at can be trivially done in C.
> 
> Would it make sense to delete the Lisp implementation of frame-at and 
> make it implemented in C only?

Yes, I think so.



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

* Re: "Final" version of tty child frames
  2025-01-06  0:18                 ` Jared Finder
@ 2025-01-06 13:35                   ` Eli Zaretskii
  0 siblings, 0 replies; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-06 13:35 UTC (permalink / raw)
  To: Jared Finder; +Cc: rudalics, gerd.moellmann, emacs-devel

> Date: Sun, 05 Jan 2025 16:18:24 -0800
> From: Jared Finder <jared@finder.org>
> Cc: rudalics@gmx.at, gerd.moellmann@gmail.com, emacs-devel@gnu.org
> 
> On 2025-01-04 22:46, Eli Zaretskii wrote:
> > 
> > If you mean the text cursor, then it is positioned by calling cmgoto,
> > which is called from tty_cursor_to and tty_raw_cursor_to.
> > 
> > Frankly, I don't understand how the mouse could affect the cursor
> > positioning.  Do you mean the cursor position is wrong when you click
> > the mouse?
> 
> Emacs understanding of point ends up correct when I click the mouse, but 
> the cursor is incorrect.  This only applies to some lines.  I get 
> similar issues when using arrow keys for moving the point around as 
> well.  I do not see the same behavior in an xterm.  This predates my 
> change.  It only happens when tty child frames are visible with 
> TERM=linux, as far as I can tell.

Maybe some code moves the cursor after setting it according to the
click?  E.g., to redraw some part of the window?  We must move the
cursor before drawing anything on a TTY.

I hope the functions Gerd and myself mentioned will help you find the
culprit.



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

* Re: "Final" version of tty child frames
  2025-01-06 13:30                             ` Eli Zaretskii
@ 2025-01-07  5:40                               ` Jared Finder
  2025-01-07  7:36                                 ` Gerd Möllmann
  0 siblings, 1 reply; 194+ messages in thread
From: Jared Finder @ 2025-01-07  5:40 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gerd.moellmann, emacs-devel, rudalics

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

On 2025-01-06 05:30, Eli Zaretskii wrote:
>> Date: Sun, 05 Jan 2025 16:05:59 -0800
>> From: Jared Finder <jared@finder.org>
>> Cc: gerd.moellmann@gmail.com, emacs-devel@gnu.org, rudalics@gmx.at
>> 
>> Style comments addressed, thank you! Someday, I will internalize 
>> Emacs'
>> unique C style conventions.  Probably once I only code for Emacs and 
>> not
>> other projects. :)
> 
> FTR, they are not Emacs conventions, they are GNU conventions.

Ah, good point.  This gave me hope I could just run GNU indent on the 
files I edited, but sadly that doesn't work.  I got a lot of unrelated 
changes, some of which seem to be fine either way, and some of which are 
conflicts with the DEFUN() doc: parameter.  This is sad, it would be 
really really nice if GNU style conventions could be automatically 
applied.  Then such conventions could be automatically checked or fixed 
when commits are made.

Does anyone here have a way to automatically check or fix the Emacs 
style convention?  It would be soooo helpful for occasional committers 
like myself.

>> > Does this mean child frames on xterm will have the selected-frame set
>> > to a child frame, and thus do not need this trick with
>> > last_mouse_frame?  Or what other difference between xterm and the
>> > Linux console hides behind this "fallback"?
>> 
>> This is just keeping the behavior that already exists today.  I was
>> surprised by the current behavior: that term_mouse_position which only
>> works for a GPM enabled mouse gets called even when GPM could not be
>> used.  It could make sense to change this behavior however it would be 
>> a
>> bigger change as gpm-mouse-mode is enabled by default even for non-GPM
>> supporting terminals.  I suspect that GPM support is equivalent to
>> TERM=linux, but I may be wrong.
>> 
>> This works when xterm-mouse-mode is enabled because 
>> mouse_position_hook
>> (set to term_mouse_position) is called first, followed by
>> mouse-position-function (set to xterm-mouse-position-function).  On 
>> ttys
>> with xterm-mouse-mode enabled the return value from 
>> term_mouse_position
>> is ignored.  See existing definitions in Fmouse_pixel_position and
>> mouse_position in frame.c.
> 
> Maybe term_mouse_position should return immediately if GPM is not
> enabled?

Sure, that was easy enough.  Other comments addressed as well.  New 
patch attached.

   -- MJF

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Support-TTY-child-frames-with-GPM-mouse.patch --]
[-- Type: text/x-diff; name=0001-Support-TTY-child-frames-with-GPM-mouse.patch, Size: 7236 bytes --]

From 73545bf94102d60719c5dbb22259a791ddb3c0d9 Mon Sep 17 00:00:00 2001
From: Jared Finder <jared@finder.org>
Date: Mon, 6 Jan 2025 20:52:11 -0800
Subject: [PATCH] Support TTY child frames with GPM mouse

* lisp/frame.el (x-list-fonts): Delete `frame-at', to move to
C implementation.
* lisp/xt-mouse.el (xterm-mouse-event): Call new `tty-frame-at'.
* src/term.c (tty_frame_at, Ftty_frame_at): New C function,
replacing `frame-at' only for TTYs.
(term_mouse_position): Use last_mouse_frame when it is set.
(handle_one_term_event): Call tty_frame_at to get frame under
mouse, store it in last_mouse_frame.  Alter event coordinates
based on mouse frame.
(syms_of_term): Add tty-frame-at, last_mouse_frame.
* src/termhooks.h: Make Gpm_Event parameter const.
---
 lisp/frame.el    |  7 -----
 lisp/xt-mouse.el |  2 +-
 src/term.c       | 70 +++++++++++++++++++++++++++++++++++++++---------
 src/termhooks.h  |  2 +-
 4 files changed, 60 insertions(+), 21 deletions(-)

diff --git a/lisp/frame.el b/lisp/frame.el
index 7da6bce697a..091e84f09c4 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -1506,13 +1506,6 @@ 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))
 
diff --git a/lisp/xt-mouse.el b/lisp/xt-mouse.el
index ccb585ce631..19d688e4d1e 100644
--- a/lisp/xt-mouse.el
+++ b/lisp/xt-mouse.el
@@ -298,7 +298,7 @@ xterm-mouse-event
              ;; FIXME: The test for running in batch mode is here solely
              ;; for the sake of xt-mouse-tests where the only frame is
              ;; the initial frame.
-             (frame (unless noninteractive (frame-at x y)))
+             (frame (unless noninteractive (tty-frame-at x y)))
              ;;(_ (message (format "*** %S" frame)))
              (frame-pos (frame-position frame))
              ;;(_ (message (format "*** %S" frame-pos)))
diff --git a/src/term.c b/src/term.c
index 368e20803e1..10380eba4b9 100644
--- a/src/term.c
+++ b/src/term.c
@@ -141,6 +141,7 @@ #define OUTPUT1_IF(tty, a) \
 struct tty_display_info *gpm_tty = NULL;
 
 /* Last recorded mouse coordinates.  */
+static Lisp_Object last_mouse_frame;
 static int last_mouse_x, last_mouse_y;
 #endif /* HAVE_GPM */
 
@@ -2593,6 +2594,35 @@ tty_draw_row_with_mouse_face (struct window *w, struct glyph_row *row,
 
 #endif
 
+static Lisp_Object
+tty_frame_at (int x, int y)
+{
+  for (Lisp_Object frames = Ftty_frame_list_z_order (Qnil); frames != Qnil;
+       frames = Fcdr (frames))
+    {
+      Lisp_Object frame = Fcar (frames);
+      struct frame *f = XFRAME (frame);
+
+      if (f->left_pos <= x && x < f->left_pos + f->pixel_width &&
+	  f->top_pos <= y && y < f->top_pos + f->pixel_height)
+	return frame;
+    }
+
+  return Qnil;
+}
+
+DEFUN ("tty-frame-at", Ftty_frame_at, Stty_frame_at,
+       2, 2, 0,
+       doc: /* Return tty frame containing pixel position X, Y.  */)
+  (Lisp_Object x, Lisp_Object y)
+{
+  if (! FIXNUMP (x) || ! FIXNUMP (y))
+    /* Coordinates this big can not correspond to any frame.  */
+    return Qnil;
+
+  return tty_frame_at (XFIXNUM (x), XFIXNUM (y));
+}
+
 #ifdef HAVE_GPM
 
 void
@@ -2638,7 +2668,12 @@ term_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window,
 		     enum scroll_bar_part *part, Lisp_Object *x,
 		     Lisp_Object *y, Time *timeptr)
 {
-  *fp = SELECTED_FRAME ();
+  /* If we've gotten no GPM mouse events yet, last_mouse_frame won't be
+     set.  Perhaps `gpm-mouse-mode' was never active.  */
+  if (! FRAMEP (last_mouse_frame))
+    return;
+
+  *fp = XFRAME (last_mouse_frame);
   (*fp)->mouse_moved = 0;
 
   *bar_window = Qnil;
@@ -2713,9 +2748,14 @@ term_mouse_click (struct input_event *result, Gpm_Event *event,
 }
 
 int
-handle_one_term_event (struct tty_display_info *tty, Gpm_Event *event)
+handle_one_term_event (struct tty_display_info *tty, const Gpm_Event *event_in)
 {
-  struct frame *f = XFRAME (tty->top_frame);
+  Lisp_Object frame = tty_frame_at (event_in->x, event_in->y);
+  struct frame *f = decode_live_frame (frame);
+  Gpm_Event event = *event_in;
+  event.x -= f->left_pos;
+  event.y -= f->top_pos;
+
   struct input_event ie;
   int count = 0;
 
@@ -2723,30 +2763,34 @@ handle_one_term_event (struct tty_display_info *tty, Gpm_Event *event)
   ie.kind = NO_EVENT;
   ie.arg = Qnil;
 
-  if (event->type & (GPM_MOVE | GPM_DRAG))
+  if (event.type & (GPM_MOVE | GPM_DRAG))
     {
-      Gpm_DrawPointer (event->x, event->y, fileno (tty->output));
+      /* The pointer must be drawn using screen coordinates (x,y), not
+	 frame coordinates.  Use event_in which has an unmodified event
+	 directly from GPM.  */
+      Gpm_DrawPointer (event_in->x, event_in->y, fileno (tty->output));
 
       /* Has the mouse moved off the glyph it was on at the last
          sighting?  */
-      if (event->x != last_mouse_x || event->y != last_mouse_y)
+      if (event.x != last_mouse_x || event.y != last_mouse_y)
         {
-          /* FIXME: These three lines can not be moved into
+          /* FIXME: These four lines can not be moved into
              update_mouse_position unless xterm-mouse gets updated to
              generate mouse events via C code.  See
              https://lists.gnu.org/archive/html/emacs-devel/2020-11/msg00163.html */
-          last_mouse_x = event->x;
-          last_mouse_y = event->y;
+          last_mouse_frame = frame;
+          last_mouse_x = event.x;
+          last_mouse_y = event.y;
           f->mouse_moved = 1;
 
-          count += update_mouse_position (f, event->x, event->y);
+          count += update_mouse_position (f, event.x, event.y);
         }
     }
   else
     {
       f->mouse_moved = 0;
-      term_mouse_click (&ie, event, f);
-      ie.arg = tty_handle_tab_bar_click (f, event->x, event->y,
+      term_mouse_click (&ie, &event, f);
+      ie.arg = tty_handle_tab_bar_click (f, event.x, event.y,
 					 (ie.modifiers & down_modifier) != 0, &ie);
       kbd_buffer_store_event (&ie);
       count++;
@@ -4967,9 +5011,11 @@ syms_of_term (void)
   defsubr (&Stty__set_output_buffer_size);
   defsubr (&Stty__output_buffer_size);
 #endif /* !HAVE_ANDROID */
+  defsubr (&Stty_frame_at);
 #ifdef HAVE_GPM
   defsubr (&Sgpm_mouse_start);
   defsubr (&Sgpm_mouse_stop);
+  staticpro (&last_mouse_frame);
 #endif /* HAVE_GPM */
 
   defsubr (&Stty_frame_geometry);
diff --git a/src/termhooks.h b/src/termhooks.h
index b32804a57b3..0795148f1af 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -458,7 +458,7 @@ #define EVENT_INIT(event) (memset (&(event), 0, sizeof (struct input_event)), \
 
 #ifdef HAVE_GPM
 #include <gpm.h>
-extern int handle_one_term_event (struct tty_display_info *, Gpm_Event *);
+extern int handle_one_term_event (struct tty_display_info *, const Gpm_Event *);
 extern void term_mouse_moveto (int, int);
 
 /* The device for which we have enabled gpm support.  */
-- 
2.39.5


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

* Re: "Final" version of tty child frames
  2025-01-07  5:40                               ` Jared Finder
@ 2025-01-07  7:36                                 ` Gerd Möllmann
  0 siblings, 0 replies; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-07  7:36 UTC (permalink / raw)
  To: Jared Finder; +Cc: Eli Zaretskii, emacs-devel, rudalics

Jared Finder <jared@finder.org> writes:

> Does anyone here have a way to automatically check or fix the Emacs
> style convention?  It would be soooo helpful for occasional committers
> like myself.

Unfortunately, no, not at 100% solution.

You could use Eglot + clangd, M-x eglot-format. That can do many things,
but not all, like for example it doesn't remove the braces in the case
you had. And DEFUN of course, but one can turn automatic formatting off
in regions of the code.

I'd like to see the rules modified so that one automate tools, but I
don't see that happening, TBH. Like for the double space after period
which the world doesn't use.




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

* Re: "Final" version of tty child frames
  2025-01-04 22:09             ` Jared Finder
  2025-01-05  3:48               ` Gerd Möllmann
  2025-01-05  6:46               ` Eli Zaretskii
@ 2025-01-08  9:56               ` martin rudalics
  2025-01-08 10:29                 ` Gerd Möllmann
  2 siblings, 1 reply; 194+ messages in thread
From: martin rudalics @ 2025-01-08  9:56 UTC (permalink / raw)
  To: Jared Finder; +Cc: Eli Zaretskii, gerd.moellmann, emacs-devel

 >> 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.
 >
 > TTY child frame cursors behave even worse under GPM and a TERM=linux
 > terminal.  In this case, the cursor ends up on some lines always
 > appearing at the end of lines (ignoring separate frames or windows)
 > and sometimes in the right place.  The point position is correct
 > though and the cursor does appear correctly once I start pressing
 > simple keys like 'A'.

Both issues I raised above seem to be gone with master I pulled today.
Thanks for fixing them.

One thing I noticed is that with code like the following

(defvar tty-child-frame nil)
(defvar tty-child-buffer (get-buffer-create "*tty-child-buffer*"))
(defvar tty-child-window nil)

(defun tty-make-child-frame ()
   (interactive)
   (setq tty-child-frame
	(make-frame
	 `((parent-frame . ,(selected-frame))
	   (left . 20)
	   (top . 10)
	   (width . 0.3)
	   (height . 0.8)
	   (border-width . 0)
	   (background-color . "yellow")
	   (tool-bar-lines . 0)
	   (menu-bar-lines . 0)
	   (minibuffer . nil)
	   (no-special-glyphs . t)
	   )))
   (setq tty-child-window (frame-root-window tty-child-frame))
   (set-window-buffer tty-child-window tty-child-buffer)
   (set-face-background 'internal-border "blue" tty-child-frame)
   (set-face-background 'child-frame-border "blue" tty-child-frame))

(defun tty-toggle-child-frame ()
   (interactive)
   (if (frame-live-p tty-child-frame)
       (if (frame-visible-p tty-child-frame)
	  (make-frame-invisible tty-child-frame)
	(make-frame-visible tty-child-frame))
     (tty-make-child-frame)))

(defun tty-switch-to-child-frame ()
   (interactive)
   (if (frame-live-p tty-child-frame)
       (if (frame-visible-p tty-child-frame)
	  (if (eq tty-child-frame (selected-frame))
	      (select-frame-set-input-focus (frame-parent tty-child-frame))
	    (select-frame-set-input-focus tty-child-frame))
	(make-frame-visible tty-child-frame)
	(select-frame-set-input-focus tty-child-frame))
     (tty-make-child-frame)
     (select-frame-set-input-focus tty-child-frame)))

then when I first evaluate 'tty-toggle-child-frame' followed by
'tty-switch-to-child-frame' and I start typing I get an assertion
failure with a backtrace like the below

#0  terminate_due_to_signal (sig=6, backtrace_limit=2147483647) at ../../src/emacs.c:432
#1  0x0000000000699b76 in die (msg=0x7dfc63 "FRAME_VISIBLE_P (root)", file=0x7df29f "../../src/dispnew.c", line=3962) at ../../src/alloc.c:8059
#2  0x0000000000427575 in combine_updates_for_frame (f=0x18662e0, inhibit_scrolling=false) at ../../src/dispnew.c:3962
#3  0x00000000004832fc in redisplay_internal () at ../../src/xdisp.c:17699
#4  0x0000000000483719 in redisplay_preserve_echo_area (from_where=8) at ../../src/xdisp.c:17835
#5  0x000000000060b715 in detect_input_pending_run_timers (do_display=true) at ../../src/keyboard.c:11579
#6  0x00000000007465d9 in wait_reading_process_output (time_limit=30, nsecs=0, read_kbd=-1, do_display=true, wait_for_cell=XIL(0), wait_proc=0x0, just_wait_proc=0) at ../../src/process.c:5856
#7  0x0000000000430243 in sit_for (timeout=make_fixnum(30), reading=true, display_option=1) at ../../src/dispnew.c:6889
#8  0x00000000005f6383 in read_char (commandflag=1, map=XIL(0x7ff1fcdf4053), prev_event=XIL(0), used_mouse_menu=0x7ffc85a5703f, end_time=0x0) at ../../src/keyboard.c:2925
#9  0x00000000006097f3 in read_key_sequence (keybuf=0x7ffc85a571f0, prompt=XIL(0), dont_downcase_last=false, can_return_switch_frame=true, fix_current_buffer=true, prevent_redisplay=false, disable_text_conversion_p=false) at ../../src/keyboard.c:10746
#10 0x00000000005f1c46 in command_loop_1 () at ../../src/keyboard.c:1424
#11 0x00000000006d06e3 in internal_condition_case (bfun=0x5f1817 <command_loop_1>, handlers=XIL(0x90), hfun=0x5f0c99 <cmd_error>) at ../../src/eval.c:1607
#12 0x00000000005f13de in command_loop_2 (handlers=XIL(0x90)) at ../../src/keyboard.c:1163
#13 0x00000000006cfb39 in internal_catch (tag=XIL(0x122d0), func=0x5f13b4 <command_loop_2>, arg=XIL(0x90)) at ../../src/eval.c:1286
#14 0x00000000005f1370 in command_loop () at ../../src/keyboard.c:1141
#15 0x00000000005f073b in recursive_edit_1 () at ../../src/keyboard.c:749
#16 0x00000000005f0967 in Frecursive_edit () at ../../src/keyboard.c:832
#17 0x00000000005ec1cd in main (argc=5, argv=0x7ffc85a57828) at ../../src/emacs.c:2636

Lisp Backtrace:
"redisplay_internal (C function)" (0x0)

The cause is that my 'tty-make-child-frame' has

	 `((parent-frame . ,(selected-frame))
...
	   (minibuffer . nil)

but the consequences are completely unclear to me.  What happens is that
make_terminal_frame has this

	  if (EQ (mini, Qnone) || NILP (mini))
	    f = make_frame_without_minibuffer (Qnil, kb, Qnil);

which reenters make_terminal_frame and does

       if (FRAME_LIVE_P (root))
	SET_FRAME_VISIBLE (root, false);

for the old selected "root" frame.

What's the purpose of creating a new top frame without minibuffer here?
We end up with three frames - the initial one, the child frame and the
new root frame without minibuffer.  redisplay_internal eventually
detects here

       Lisp_Object mini_window = FRAME_MINIBUF_WINDOW (sf);
       struct frame *mini_frame = XFRAME (WINDOW_FRAME (XWINDOW (mini_window)));

       if (mini_frame != sf)

that the mini_frame is not the selected frame

	{
	  XWINDOW (mini_window)->must_be_updated_p = true;
	  update_frame (mini_frame, false);
	  if (is_tty_frame (mini_frame))
	    combine_updates_for_frame (mini_frame, false);

and the call here crashes because mini_frame is the old root frame but
that frame's visibility has been set to false by the code above.

martin



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

* Re: "Final" version of tty child frames
  2025-01-08  9:56               ` martin rudalics
@ 2025-01-08 10:29                 ` Gerd Möllmann
  2025-01-09 10:24                   ` martin rudalics
  0 siblings, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-08 10:29 UTC (permalink / raw)
  To: martin rudalics; +Cc: Jared Finder, Eli Zaretskii, emacs-devel

martin rudalics <rudalics@gmx.at> writes:

> The cause is that my 'tty-make-child-frame' has
>
> 	 `((parent-frame . ,(selected-frame))
> ...
> 	   (minibuffer . nil)
>
> but the consequences are completely unclear to me.  What happens is that
> make_terminal_frame has this
>
> 	  if (EQ (mini, Qnone) || NILP (mini))
> 	    f = make_frame_without_minibuffer (Qnil, kb, Qnil);
>
> which reenters make_terminal_frame and does
>
>       if (FRAME_LIVE_P (root))
> 	SET_FRAME_VISIBLE (root, false);
>
> for the old selected "root" frame.

Guess that wasn't what I intended :-).

> What's the purpose of creating a new top frame without minibuffer
> here?

The frame from make_frame_without_minibuffer is supposed to become a
child frame at bit later

      if (f == NULL)
	f = make_frame (true);
      f->parent_frame = parent;
      ^^^^^^^^^^^^^^^^^^^^^^^^

> We end up with three frames - the initial one, the child frame and the
> new root frame without minibuffer.  redisplay_internal eventually
> detects here

I've not yet understood where the third frame is created. Probably have to
see this in the debugger to get into this again.

Could you please submit a bug so that I don't forget looking into this?



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

* Re: "Final" version of tty child frames
  2025-01-08 10:29                 ` Gerd Möllmann
@ 2025-01-09 10:24                   ` martin rudalics
  2025-01-09 10:45                     ` Gerd Möllmann
  0 siblings, 1 reply; 194+ messages in thread
From: martin rudalics @ 2025-01-09 10:24 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Jared Finder, Eli Zaretskii, emacs-devel

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

 > I've not yet understood where the third frame is created. Probably have to
 > see this in the debugger to get into this again.

Without really checking it must be here

       /* Use default-minibuffer-frame if possible.  */
       if (!FRAMEP (KVAR (kb, Vdefault_minibuffer_frame))
	  || ! FRAME_LIVE_P (XFRAME (KVAR (kb, Vdefault_minibuffer_frame))))
	{
	  Lisp_Object initial_frame;

	  /* If there's no minibuffer frame to use, create one.  */
	  initial_frame = call1 (Qmake_initial_minibuffer_frame,
				 display);
	  kset_default_minibuffer_frame (kb, initial_frame);
	}

'default-minibuffer-frame' on ttys is nil, only 'x-create-frame' and the
like set it AFAICT.  Even if it were some frame, it might be of no use:
On a tty the mini window to use _must_ be on the visible frame.  So
Emacs makes a new initial_frame and uses that frame's mini window.  But
the child frame gets another root frame and sooner or later this will
crash anyway.  The attached patch fixes it here.

martin (who can still post a bug report if you think it's needed)

[-- Attachment #2: tty-child-frame.diff --]
[-- Type: text/x-patch, Size: 927 bytes --]

diff --git a/src/frame.c b/src/frame.c
index 7c07100e944..94e466b14c6 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -1297,12 +1297,22 @@ make_terminal_frame (struct terminal *terminal, Lisp_Object 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);
+	    {
+	      /* A terminal root frame must have a minibuffer window.  */
+	      Lisp_Object mini_window
+		= root_frame (XFRAME (parent))->minibuffer_window;
+
+	      f = make_frame (false);
+	      fset_minibuffer_window (f, mini_window);
+	      store_frame_param (f, Qminibuffer, mini_window);
+	    }
 	  else if (EQ (mini, Qonly))
 	    error ("minibuffer-only child frames are not implemented");
 	  else if (WINDOWP (mini))

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

* Re: "Final" version of tty child frames
  2025-01-09 10:24                   ` martin rudalics
@ 2025-01-09 10:45                     ` Gerd Möllmann
  2025-01-11 10:13                       ` martin rudalics
  2025-01-11 10:51                       ` Eli Zaretskii
  0 siblings, 2 replies; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-09 10:45 UTC (permalink / raw)
  To: martin rudalics; +Cc: Jared Finder, Eli Zaretskii, emacs-devel

martin rudalics <rudalics@gmx.at> writes:

>> I've not yet understood where the third frame is created. Probably have to
>> see this in the debugger to get into this again.
>
> Without really checking it must be here
>
>       /* Use default-minibuffer-frame if possible.  */
>       if (!FRAMEP (KVAR (kb, Vdefault_minibuffer_frame))
> 	  || ! FRAME_LIVE_P (XFRAME (KVAR (kb, Vdefault_minibuffer_frame))))
> 	{
> 	  Lisp_Object initial_frame;
>
> 	  /* If there's no minibuffer frame to use, create one.  */
> 	  initial_frame = call1 (Qmake_initial_minibuffer_frame,
> 				 display);
> 	  kset_default_minibuffer_frame (kb, initial_frame);
> 	}
>
> 'default-minibuffer-frame' on ttys is nil, only 'x-create-frame' and the
> like set it AFAICT.  Even if it were some frame, it might be of no use:
> On a tty the mini window to use _must_ be on the visible frame.  So
> Emacs makes a new initial_frame and uses that frame's mini window.  But
> the child frame gets another root frame and sooner or later this will
> crash anyway.  The attached patch fixes it here.

That would have taken me some time to understand :-). 

> martin (who can still post a bug report if you think it's needed)

If that solves everything you reported previously, no bug needed. LGTM.
(Sorry, I'm a bit forgetful when I'm doing something else, and a todo
list doesn't really help, it seems to grow only.)

LGTM, please apply.



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

* Re: "Final" version of tty child frames
  2025-01-09 10:45                     ` Gerd Möllmann
@ 2025-01-11 10:13                       ` martin rudalics
  2025-01-11 12:52                         ` Gerd Möllmann
  2025-01-11 10:51                       ` Eli Zaretskii
  1 sibling, 1 reply; 194+ messages in thread
From: martin rudalics @ 2025-01-11 10:13 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Jared Finder, Eli Zaretskii, emacs-devel

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

 > LGTM, please apply.

I've looked a bit further and have found out two more things.

When with emacs -Q -nw I call tty-2 specified as

(defun tty-2 ()
   (interactive)
   (let* ((window (minibuffer-window))
	 (frame (make-frame))
	 (child
	  (make-frame
	   `((parent-frame . ,frame) (minibuffer . ,window)
	     (left . 20) (top . 10)
	     (width . 0.3) (height . 0.8)
	     (border-width . 0)
	     (background-color . "yellow")))))
     (select-frame-set-input-focus child)))

and type into the new child frame I get a crash similar to the one
before.  The minibuffer frame must have the same root frame as any of
its client frames.  The attached patch should fix that too.

The restriction to not make minibuffer-only child frames is unnecessary.
The attached patch fixes that as well.  Tested with emacs -Q -nw and

(unless window-system (xterm-mouse-mode 1))
(defvar tty-child-mini-only nil)

(defun tty-4 ()
   (interactive)
   (let* ((child
	  (make-frame
	   `((parent-frame . ,(selected-frame)) (minibuffer . only)
	     (left . 20) (top . 10)
	     (width . 0.3) (height . 0.3)
	     (border-width . 0)
	     (background-color . "yellow")))))
     (setq tty-child-mini-only child)))

(defun tty-5 ()
   (interactive)
   (let* ((child
	  (make-frame
	   `((parent-frame . ,(selected-frame))
	     (minibuffer . ,(frame-root-window tty-child-mini-only))
	     (left . 40) (top . 10)
	     (width . 0.3) (height . 0.8)
	     (border-width . 0)
	     (background-color . "orange")))))
     (select-frame-set-input-focus child)))

(defun tty-6 ()
   (interactive)
   (make-frame-invisible  tty-child-mini-only))

First make a minibuffer-only child frame via tty-4.  Then via tty-5 make
a new child frame using the one made by tty-4 as its minibuffer frame.
Hide the minibuffer-only child frame via tty-6 (you have to invoke this
from the root frame).  Now in the child frame made via tty-5 type C-h f.
Here the minibuffer-only child frame is made visible and gets input
focus.

martin



[-- Attachment #2: tty-child-frame.diff --]
[-- Type: text/x-patch, Size: 2919 bytes --]

diff --git a/src/frame.c b/src/frame.c
index 146ecb226a4..55b287ec65e 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -1289,6 +1289,7 @@ make_terminal_frame (struct terminal *terminal, Lisp_Object parent,
     error ("Terminal is not live, can't create new frames on it");
 
   struct frame *f;
+
   if (NILP (parent))
     f = make_frame (true);
   else
@@ -1297,16 +1298,61 @@ make_terminal_frame (struct terminal *terminal, Lisp_Object parent,
 
       f = NULL;
       Lisp_Object mini = Fassq (Qminibuffer, params);
+
+      /* Handling the minibuffer parameter on a tty is different from a
+	 GUI.  On a GUI any "client frame" can have, in principle, its
+	 minibuffer window on any "minibuffer frame" - a frame that has
+	 a minibuffer window.  If necessary, Emacs tells the window
+	 manager to make the minibuffer frame visible, raise it and give
+	 it input focus.
+
+	 On a tty there's no window manager, so Emacs itself has to make
+	 such a minibuffer frame visible, raise and focus it.  Since a
+	 tty can show only one root frame (a frame that doesn't have a
+	 parent frame) at any time, any client frame shown on a tty must
+	 have a visible minibuffer frame shown on that tty at the same
+	 time.  Hence, unless that minibuffer frame is the client frame
+	 itself, it must be a child frame of the client frame's root
+	 frame since only such a child frame can appear together with
+	 the client frame at the same time on the same tty.
+
+	 Two trivial consequences of these observations for ttys are:
+
+	 - A root frame cannot be the minibuffer frame of another root
+	   frame.
+
+	 - Since a child frame cannot be created before its parent
+           frame, each root frame must have its own minibuffer window.
+
+         The situation may change as soon as we can delete and create
+         minibuffer windows on the fly.  */
       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);
+	    {
+	      mini = root_frame (XFRAME (parent))->minibuffer_window;
+	      f = make_frame (false);
+	      fset_minibuffer_window (f, mini);
+	      store_frame_param (f, Qminibuffer, mini);
+	    }
 	  else if (EQ (mini, Qonly))
-	    error ("minibuffer-only child frames are not implemented");
+	    f = make_minibuffer_frame ();
 	  else if (WINDOWP (mini))
-	    f = make_frame_without_minibuffer (mini, kb, Qnil);
+	    {
+	      if (!WINDOW_LIVE_P (mini)
+		  || !MINI_WINDOW_P (XWINDOW (mini))
+		  || (root_frame (WINDOW_XFRAME (XWINDOW (mini)))
+		      != root_frame (XFRAME (parent))))
+		error ("The `minibuffer' parameter does not specify a valid minibuffer window");
+	      else
+		{
+		  f = make_frame (false);
+		  fset_minibuffer_window (f, mini);
+		  store_frame_param (f, Qminibuffer, mini);
+		}
+	    }
 	}
 
       if (f == NULL)

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

* Re: "Final" version of tty child frames
  2025-01-09 10:45                     ` Gerd Möllmann
  2025-01-11 10:13                       ` martin rudalics
@ 2025-01-11 10:51                       ` Eli Zaretskii
  2025-01-11 15:55                         ` martin rudalics
  1 sibling, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-11 10:51 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: rudalics, jared, emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: Jared Finder <jared@finder.org>,  Eli Zaretskii <eliz@gnu.org>,
>   emacs-devel@gnu.org
> Date: Thu, 09 Jan 2025 11:45:45 +0100
> 
> martin rudalics <rudalics@gmx.at> writes:
> 
> >> I've not yet understood where the third frame is created. Probably have to
> >> see this in the debugger to get into this again.
> >
> > Without really checking it must be here
> >
> >       /* Use default-minibuffer-frame if possible.  */
> >       if (!FRAMEP (KVAR (kb, Vdefault_minibuffer_frame))
> > 	  || ! FRAME_LIVE_P (XFRAME (KVAR (kb, Vdefault_minibuffer_frame))))
> > 	{
> > 	  Lisp_Object initial_frame;
> >
> > 	  /* If there's no minibuffer frame to use, create one.  */
> > 	  initial_frame = call1 (Qmake_initial_minibuffer_frame,
> > 				 display);
> > 	  kset_default_minibuffer_frame (kb, initial_frame);
> > 	}
> >
> > 'default-minibuffer-frame' on ttys is nil, only 'x-create-frame' and the
> > like set it AFAICT.  Even if it were some frame, it might be of no use:
> > On a tty the mini window to use _must_ be on the visible frame.  So
> > Emacs makes a new initial_frame and uses that frame's mini window.  But
> > the child frame gets another root frame and sooner or later this will
> > crash anyway.  The attached patch fixes it here.
> 
> That would have taken me some time to understand :-). 
> 
> > martin (who can still post a bug report if you think it's needed)
> 
> If that solves everything you reported previously, no bug needed. LGTM.
> (Sorry, I'm a bit forgetful when I'm doing something else, and a todo
> list doesn't really help, it seems to grow only.)
> 
> LGTM, please apply.

Now installed on the master branch, thanks.



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

* Re: "Final" version of tty child frames
  2025-01-11 10:13                       ` martin rudalics
@ 2025-01-11 12:52                         ` Gerd Möllmann
  2025-01-11 17:23                           ` martin rudalics
  0 siblings, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-11 12:52 UTC (permalink / raw)
  To: martin rudalics; +Cc: Jared Finder, Eli Zaretskii, emacs-devel

martin rudalics <rudalics@gmx.at> writes:

> The restriction to not make minibuffer-only child frames is unnecessary.
> The attached patch fixes that as well.  Tested with emacs -Q -nw and

Very good! That minibuffer game was entirely confusing for me, maybe
because I've never ever used, nor even seen such a minibuffer-only setup.

Thanks for that, Martin! Please apply!



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

* Re: "Final" version of tty child frames
  2025-01-11 10:51                       ` Eli Zaretskii
@ 2025-01-11 15:55                         ` martin rudalics
  0 siblings, 0 replies; 194+ messages in thread
From: martin rudalics @ 2025-01-11 15:55 UTC (permalink / raw)
  To: Eli Zaretskii, Gerd Möllmann; +Cc: jared, emacs-devel

 >> LGTM, please apply.
 >
 > Now installed on the master branch, thanks.

This was supposedly posted in the wrong sub-thread.

It gives me here

../../src/term.c: In function ‘tty_frame_at’:
../../src/term.c:2600:68: error: invalid operands to binary != (have ‘Lisp_Object’ and ‘Lisp_Object’)
  2600 |   for (Lisp_Object frames = Ftty_frame_list_z_order (Qnil); frames != Qnil;
       |                                                                    ^~

And while we're here please avoid idioms like

   if (! FRAMEP (last_mouse_frame))
     return;

   *fp = XFRAME (last_mouse_frame);

since we just had a quite similar bug elsewhere.  As a rule, always
write

   if (!FRAMEP (last_mouse_frame)
       || !FRAME_LIVE_P (XFRAME (last_mouse_frame)))
     return;

Thanks, martin

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

* Re: "Final" version of tty child frames
  2025-01-11 12:52                         ` Gerd Möllmann
@ 2025-01-11 17:23                           ` martin rudalics
  2025-01-11 17:57                             ` Gerd Möllmann
  0 siblings, 1 reply; 194+ messages in thread
From: martin rudalics @ 2025-01-11 17:23 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Jared Finder, Eli Zaretskii, emacs-devel

 > Thanks for that, Martin! Please apply!

Applied now.  There's one ugly issue we should resolve next.
With the following code


(defvar tty-child-frame nil)
(defvar tty-child-buffer (get-buffer-create "*tty-child-buffer*"))
(defvar tty-child-window nil)

(defun tty-make-child-frame ()
   (interactive)
   (setq tty-child-frame
	(make-frame
	 `((parent-frame . ,(selected-frame))
	   (left . 20)
	   (top . 10)
	   (width . 0.3)
	   (height . 0.8)
	   (border-width . 0)
	   (background-color . "yellow")
	   (tool-bar-lines . 0)
	   (menu-bar-lines . 0)
	   (minibuffer . nil)
	   (no-special-glyphs . t)
	   )))
   (setq tty-child-window (frame-root-window tty-child-frame))
   (set-window-buffer tty-child-window tty-child-buffer)
   (set-face-background 'internal-border "blue" tty-child-frame)
   (set-face-background 'child-frame-border "blue" tty-child-frame))

(defun tty-toggle-child-frame ()
   (interactive)
   (if (frame-live-p tty-child-frame)
       (if (frame-visible-p tty-child-frame)
	  (make-frame-invisible tty-child-frame)
	(make-frame-visible tty-child-frame))
     (tty-make-child-frame)))

(defun tty-switch-to-child-frame ()
   (interactive)
   (if (frame-live-p tty-child-frame)
       (if (frame-visible-p tty-child-frame)
	  (if (eq tty-child-frame (selected-frame))
	      (select-frame-set-input-focus (frame-parent tty-child-frame))
	    (select-frame-set-input-focus tty-child-frame))
	(make-frame-visible tty-child-frame)
	(select-frame-set-input-focus tty-child-frame))
     (tty-make-child-frame)
     (select-frame-set-input-focus tty-child-frame)))

(global-set-key [(control l)] 'tty-toggle-child-frame)
(global-set-key [(meta l)] 'tty-switch-to-child-frame)


and emacs -Q -nw do C-l followed by M-l and C-l.  That is, make a child
frame, switch to it and make it invisible.  This leaves the point (bar)
cursor in the middle of the normal, root frame in an area where there's
no text and you cannot really move it anywhere.  'xterm-mouse-mode' can
help me out but this might not be available everywhere.

Any ideas?

martin



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

* Re: "Final" version of tty child frames
  2025-01-11 17:23                           ` martin rudalics
@ 2025-01-11 17:57                             ` Gerd Möllmann
  2025-01-12  5:26                               ` Gerd Möllmann
  0 siblings, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-11 17:57 UTC (permalink / raw)
  To: martin rudalics; +Cc: Jared Finder, Eli Zaretskii, emacs-devel

martin rudalics <rudalics@gmx.at> writes:

> and emacs -Q -nw do C-l followed by M-l and C-l.  That is, make a child
> frame, switch to it and make it invisible.  This leaves the point (bar)
> cursor in the middle of the normal, root frame in an area where there's
> no text and you cannot really move it anywhere.  'xterm-mouse-mode' can
> help me out but this might not be available everywhere.
>
> Any ideas?

The bar cursor must have been put where it is by redisplay. If the root
has no text where the bar cursor is, it must have been the display of
the child. Maybe the bar is still in the invisible child?

That would fit with the fact that you can move the bar to the root frame
with the mouse IIUC, which should mean that C-x 5 o probably also works.

I suspect make-frame-invisible has to do something. Do we have to
change selected-frame maybe? Or something like that?

Please tell if you'd like me to take a look at this. I'm away for the
rest of the evening, so it would have to wait 'til tomorrow, alas.




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

* Re: "Final" version of tty child frames
  2025-01-11 17:57                             ` Gerd Möllmann
@ 2025-01-12  5:26                               ` Gerd Möllmann
  2025-01-12  6:49                                 ` Gerd Möllmann
  0 siblings, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-12  5:26 UTC (permalink / raw)
  To: martin rudalics; +Cc: Jared Finder, Eli Zaretskii, emacs-devel

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

> martin rudalics <rudalics@gmx.at> writes:
>
>> and emacs -Q -nw do C-l followed by M-l and C-l.  That is, make a child
>> frame, switch to it and make it invisible.  This leaves the point (bar)
>> cursor in the middle of the normal, root frame in an area where there's
>> no text and you cannot really move it anywhere.  'xterm-mouse-mode' can
>> help me out but this might not be available everywhere.
>>
>> Any ideas?
>
> The bar cursor must have been put where it is by redisplay. If the root
> has no text where the bar cursor is, it must have been the display of
> the child. Maybe the bar is still in the invisible child?
>
> That would fit with the fact that you can move the bar to the root frame
> with the mouse IIUC, which should mean that C-x 5 o probably also works.
>
> I suspect make-frame-invisible has to do something. Do we have to
> change selected-frame maybe? Or something like that?
>
> Please tell if you'd like me to take a look at this. I'm away for the
> rest of the evening, so it would have to wait 'til tomorrow, alas.

I've pulled now, and I can reproduce it here. Some observations.

After C-l M-l the child frame is no longer visible, but the bar cursor
is.

  M-: (frame-visible-p tty-child-frame)
  => t.

That looks to me like something in the visibility department did not
work, or worked only temporarily and was reverted (?). Which can also be
seen with M-x redraw-display.

ATM, I don't see where that could happen. Please let me know when you
find something out.




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

* Re: "Final" version of tty child frames
  2025-01-12  5:26                               ` Gerd Möllmann
@ 2025-01-12  6:49                                 ` Gerd Möllmann
  2025-01-12  7:36                                   ` Gerd Möllmann
  0 siblings, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-12  6:49 UTC (permalink / raw)
  To: martin rudalics; +Cc: Jared Finder, Eli Zaretskii, emacs-devel

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

> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
>
>> martin rudalics <rudalics@gmx.at> writes:
>>
>>> and emacs -Q -nw do C-l followed by M-l and C-l.  That is, make a child
>>> frame, switch to it and make it invisible.  This leaves the point (bar)
>>> cursor in the middle of the normal, root frame in an area where there's
>>> no text and you cannot really move it anywhere.  'xterm-mouse-mode' can
>>> help me out but this might not be available everywhere.
>>>
>>> Any ideas?
>>
>> The bar cursor must have been put where it is by redisplay. If the root
>> has no text where the bar cursor is, it must have been the display of
>> the child. Maybe the bar is still in the invisible child?
>>
>> That would fit with the fact that you can move the bar to the root frame
>> with the mouse IIUC, which should mean that C-x 5 o probably also works.
>>
>> I suspect make-frame-invisible has to do something. Do we have to
>> change selected-frame maybe? Or something like that?
>>
>> Please tell if you'd like me to take a look at this. I'm away for the
>> rest of the evening, so it would have to wait 'til tomorrow, alas.
>
> I've pulled now, and I can reproduce it here. Some observations.
>
> After C-l M-l the child frame is no longer visible, but the bar cursor
> is.
>
>   M-: (frame-visible-p tty-child-frame)
>   => t.
>
> That looks to me like something in the visibility department did not
> work, or worked only temporarily and was reverted (?). Which can also be
> seen with M-x redraw-display.
>
> ATM, I don't see where that could happen. Please let me know when you
> find something out.

I've used this to trace SET_FRAME_VISIBLE.

1 file changed, 5 insertions(+), 1 deletion(-)
src/frame.h | 6 +++++-

modified   src/frame.h
@@ -1433,8 +1433,10 @@ #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, bool v)
+xSET_FRAME_VISIBLE (struct frame *f, bool v, const char *file,
+		    int line, const char *func)
 {
+  fprintf (stderr, "%s:%d %s SET_FRAME_VISIBLE %p %d\n", file, line, func, f, v);
   if (v)
     {
       if (v == 1 && f->visible != 1)
@@ -1445,6 +1447,8 @@ SET_FRAME_VISIBLE (struct frame *f, bool v)
   f->visible = v;
 }

+#define SET_FRAME_VISIBLE(f, v) xSET_FRAME_VISIBLE(f, v, __FILE__, __LINE__, __func__)
+
 /* Set iconified status of frame F.  */
 INLINE void
 SET_FRAME_ICONIFIED (struct frame *f, int i)


Now C-l M-l C-l M-x C-g. I see a trace like

Sun Jan 12 07:35:45 CET 2025
frame.c:1246 make_initial_frame SET_FRAME_VISIBLE 0x155015c00 1
frame.c:1369 make_terminal_frame SET_FRAME_VISIBLE 0x15506ef00 1
frame.c:1781 do_switch_frame SET_FRAME_VISIBLE 0x155015c00 1
frame.c:1782 do_switch_frame SET_FRAME_VISIBLE 0x155015c00 1
frame.c:1781 do_switch_frame SET_FRAME_VISIBLE 0x155015c00 1
frame.c:1782 do_switch_frame SET_FRAME_VISIBLE 0x15506ef00 1
frame.c:1781 do_switch_frame SET_FRAME_VISIBLE 0x155015c00 1
frame.c:1782 do_switch_frame SET_FRAME_VISIBLE 0x155015c00 1
frame.c:1781 do_switch_frame SET_FRAME_VISIBLE 0x155015c00 1
frame.c:1782 do_switch_frame SET_FRAME_VISIBLE 0x15506ef00 1
frame.c:1781 do_switch_frame SET_FRAME_VISIBLE 0x155015c00 1
frame.c:1782 do_switch_frame SET_FRAME_VISIBLE 0x155015c00 1
frame.c:1781 do_switch_frame SET_FRAME_VISIBLE 0x155015c00 1
frame.c:1782 do_switch_frame SET_FRAME_VISIBLE 0x15506ef00 1
frame.c:3136 Fmake_frame_visible SET_FRAME_VISIBLE 0x15506ef00 1
frame.c:3194 Fmake_frame_invisible SET_FRAME_VISIBLE 0x15506ef00 0
^^^^^^^
This is from the last C-l, and below is M-x C-g
frame.c:3136 Fmake_frame_visible SET_FRAME_VISIBLE 0x155015c00 1
frame.c:1781 do_switch_frame SET_FRAME_VISIBLE 0x155015c00 1
...

So the child is invisible after the last C-l. Whether or not it becoming
visible again is something else to consider later, I think.



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

* Re: "Final" version of tty child frames
  2025-01-12  6:49                                 ` Gerd Möllmann
@ 2025-01-12  7:36                                   ` Gerd Möllmann
  2025-01-12  8:14                                     ` Eli Zaretskii
  2025-01-12  8:22                                     ` martin rudalics
  0 siblings, 2 replies; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-12  7:36 UTC (permalink / raw)
  To: martin rudalics; +Cc: Jared Finder, Eli Zaretskii, emacs-devel

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

> So the child is invisible after the last C-l. Whether or not it becoming
> visible again is something else to consider later, I think.

Just has the thought that this might be the wrong angle to look at the
problem because I think there is something missing on ttys which GUIs
have: proper focus management.

I imagine that on a GUI we get events as needed from the window manager
to change the focus frame to what the window manager wants. I think that
part is missing on ttys. Or is select-frame-set-input-focus a complete
replacement for that?




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

* Re: "Final" version of tty child frames
  2025-01-12  7:36                                   ` Gerd Möllmann
@ 2025-01-12  8:14                                     ` Eli Zaretskii
  2025-01-12  8:27                                       ` martin rudalics
  2025-01-12  8:57                                       ` Gerd Möllmann
  2025-01-12  8:22                                     ` martin rudalics
  1 sibling, 2 replies; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-12  8:14 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: rudalics, jared, emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: Jared Finder <jared@finder.org>,  Eli Zaretskii <eliz@gnu.org>,
>   emacs-devel@gnu.org
> Date: Sun, 12 Jan 2025 08:36:16 +0100
> 
> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
> 
> > So the child is invisible after the last C-l. Whether or not it becoming
> > visible again is something else to consider later, I think.
> 
> Just has the thought that this might be the wrong angle to look at the
> problem because I think there is something missing on ttys which GUIs
> have: proper focus management.
> 
> I imagine that on a GUI we get events as needed from the window manager
> to change the focus frame to what the window manager wants. I think that
> part is missing on ttys.

I think on TTYs, change of focus should be triggered by either a mouse
click (if there is a mouse) or by explicitly selecting a frame (or
maybe by raising it).

> Or is select-frame-set-input-focus a complete replacement for that?

In that function, we have this:

  (select-frame frame norecord)
  (raise-frame frame)
  ;; Ensure, if possible, that FRAME gets input focus.
  (when (display-multi-frame-p frame)
    (x-focus-frame frame))

Does raise-frame change input focus to the frame we raise on TTYs?

display-multi-frame-p currently reports nil on TTY -- is that correct?
What does x-focus-frame does on GUI frames, and does that make sense
on TTY?



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

* Re: "Final" version of tty child frames
  2025-01-12  7:36                                   ` Gerd Möllmann
  2025-01-12  8:14                                     ` Eli Zaretskii
@ 2025-01-12  8:22                                     ` martin rudalics
  2025-01-12  9:03                                       ` Gerd Möllmann
  1 sibling, 1 reply; 194+ messages in thread
From: martin rudalics @ 2025-01-12  8:22 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Jared Finder, Eli Zaretskii, emacs-devel

 > Just has the thought that this might be the wrong angle to look at the
 > problem because I think there is something missing on ttys which GUIs
 > have: proper focus management.

For some value of "proper".

 > I imagine that on a GUI we get events as needed from the window manager
 > to change the focus frame to what the window manager wants. I think that
 > part is missing on ttys. Or is select-frame-set-input-focus a complete
 > replacement for that?

It should be.  Just that we need the right argument for it which is, in
the case at hand, the frame hosting the window returned by
'get-mru-window' with ALL-FRAMES set to all visible frames with the same
root frame as the frame we just made invisible.  If there's no other
child frame this will return the root frame of the frame that has been
made invisible just now.  Elementary, but splicing in the root frame
into 'get-mru-window' is not straightforward.  The same approach is
probably also needed when deleting or iconifying a child frame.  I'll
try to make this work.  Till then we could try to use the root frame as
argument.

martin




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

* Re: "Final" version of tty child frames
  2025-01-12  8:14                                     ` Eli Zaretskii
@ 2025-01-12  8:27                                       ` martin rudalics
  2025-01-12  8:59                                         ` Gerd Möllmann
  2025-01-12  8:57                                       ` Gerd Möllmann
  1 sibling, 1 reply; 194+ messages in thread
From: martin rudalics @ 2025-01-12  8:27 UTC (permalink / raw)
  To: Eli Zaretskii, Gerd Möllmann; +Cc: jared, emacs-devel

 > I think on TTYs, change of focus should be triggered by either a mouse
 > click (if there is a mouse) or by explicitly selecting a frame (or
 > maybe by raising it).

These already work miraculously.

 > Does raise-frame change input focus to the frame we raise on TTYs?

AFAICT yes.  The current problem is that when we make the selected frame
invisible or delete it, we have to choose a substitute for it.  On a GUI
the substitute could be any other visible window of any application
around or the desktop itself if there's no such window.  On a tty we
have to handle that ourselves.

martin




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

* Re: "Final" version of tty child frames
  2025-01-12  8:14                                     ` Eli Zaretskii
  2025-01-12  8:27                                       ` martin rudalics
@ 2025-01-12  8:57                                       ` Gerd Möllmann
  1 sibling, 0 replies; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-12  8:57 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: Jared Finder <jared@finder.org>,  Eli Zaretskii <eliz@gnu.org>,
>>   emacs-devel@gnu.org
>> Date: Sun, 12 Jan 2025 08:36:16 +0100
>> 
>> Gerd Möllmann <gerd.moellmann@gmail.com> writes:
>> 
>> > So the child is invisible after the last C-l. Whether or not it becoming
>> > visible again is something else to consider later, I think.
>> 
>> Just has the thought that this might be the wrong angle to look at the
>> problem because I think there is something missing on ttys which GUIs
>> have: proper focus management.
>> 
>> I imagine that on a GUI we get events as needed from the window manager
>> to change the focus frame to what the window manager wants. I think that
>> part is missing on ttys.
>
> I think on TTYs, change of focus should be triggered by either a mouse
> click (if there is a mouse) or by explicitly selecting a frame (or
> maybe by raising it).
>
>> Or is select-frame-set-input-focus a complete replacement for that?
>
> In that function, we have this:
>
>   (select-frame frame norecord)
>   (raise-frame frame)
>   ;; Ensure, if possible, that FRAME gets input focus.
>   (when (display-multi-frame-p frame)
>     (x-focus-frame frame))
>
> Does raise-frame change input focus to the frame we raise on TTYs?

That should land in tty_raise_lower_frame which only changes the
z-order. It does nothing concerning the focus. raise-frame itself
doesn't do much more than call that hook function.

> display-multi-frame-p currently reports nil on TTY -- is that correct?

Its doc says "Return non-nil if DISPLAY is a graphic display", so I
think we shoudn't change that for ttys, for compatibility reasons.

> What does x-focus-frame does on GUI frames, and does that make sense
> on TTY?

Hm, on NS I think this is 

static void
ns_focus_frame (struct frame *f, bool noactivate)
/* --------------------------------------------------------------------------
     External (hook)
   -------------------------------------------------------------------------- */
{
  struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);

  if (dpyinfo->ns_focus_frame != f)
    {
      EmacsView *view = FRAME_NS_VIEW (f);
      block_input ();
      [NSApp activateIgnoringOtherApps: YES];
      [[view window] makeKeyAndOrderFront: view];
      unblock_input ();
    }
}

static void
ns_raise_frame (struct frame *f, BOOL make_key)
/* --------------------------------------------------------------------------
     Bring window to foreground and if make_key is YES, give it focus.
   -------------------------------------------------------------------------- */
{
  NSView *view;

  check_window_system (f);
  view = FRAME_NS_VIEW (f);
  block_input ();
  if (FRAME_VISIBLE_P (f))
    {
      if (make_key && !f->no_accept_focus)
        [[view window] makeKeyAndOrderFront: NSApp];
      else
        [[view window] orderFront: NSApp];
    }
  unblock_input ();
}

What that does exactly I don't know, but it reads like it could do
something (makeKeyAndOrderFront) that changes both input focus and
z-order (?).



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

* Re: "Final" version of tty child frames
  2025-01-12  8:27                                       ` martin rudalics
@ 2025-01-12  8:59                                         ` Gerd Möllmann
  0 siblings, 0 replies; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-12  8:59 UTC (permalink / raw)
  To: martin rudalics; +Cc: Eli Zaretskii, jared, emacs-devel

martin rudalics <rudalics@gmx.at> writes:

>> I think on TTYs, change of focus should be triggered by either a mouse
>> click (if there is a mouse) or by explicitly selecting a frame (or
>> maybe by raising it).
>
> These already work miraculously.

As I always say, this frame management is a maze.

>
>> Does raise-frame change input focus to the frame we raise on TTYs?
>
> AFAICT yes.  The current problem is that when we make the selected frame
> invisible or delete it, we have to choose a substitute for it.  On a GUI
> the substitute could be any other visible window of any application
> around or the desktop itself if there's no such window.  On a tty we
> have to handle that ourselves.
>
> martin

Exactly.



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

* Re: "Final" version of tty child frames
  2025-01-12  8:22                                     ` martin rudalics
@ 2025-01-12  9:03                                       ` Gerd Möllmann
  2025-01-12  9:45                                         ` martin rudalics
  2025-01-14  9:10                                         ` martin rudalics
  0 siblings, 2 replies; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-12  9:03 UTC (permalink / raw)
  To: martin rudalics; +Cc: Jared Finder, Eli Zaretskii, emacs-devel

martin rudalics <rudalics@gmx.at> writes:

>> Just has the thought that this might be the wrong angle to look at the
>> problem because I think there is something missing on ttys which GUIs
>> have: proper focus management.
>
> For some value of "proper".

:-)

>> I imagine that on a GUI we get events as needed from the window manager
>> to change the focus frame to what the window manager wants. I think that
>> part is missing on ttys. Or is select-frame-set-input-focus a complete
>> replacement for that?
>
> It should be.  Just that we need the right argument for it which is, in
> the case at hand, the frame hosting the window returned by
> 'get-mru-window' with ALL-FRAMES set to all visible frames with the same
> root frame as the frame we just made invisible.  If there's no other
> child frame this will return the root frame of the frame that has been
> made invisible just now.  Elementary, but splicing in the root frame
> into 'get-mru-window' is not straightforward.  The same approach is
> probably also needed when deleting or iconifying a child frame.  

Iconifying :-O? I found having tooltips on ttys already quite funny, but
iconified child frames would beat that :-)

> I'll try to make this work. Till then we could try to use the root
> frame as argument.

Thanks!



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

* Re: "Final" version of tty child frames
  2025-01-12  9:03                                       ` Gerd Möllmann
@ 2025-01-12  9:45                                         ` martin rudalics
  2025-01-12 10:31                                           ` Gerd Möllmann
  2025-01-14  9:10                                         ` martin rudalics
  1 sibling, 1 reply; 194+ messages in thread
From: martin rudalics @ 2025-01-12  9:45 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Jared Finder, Eli Zaretskii, emacs-devel

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

 >> I'll try to make this work. Till then we could try to use the root
 >> frame as argument.
 >
 > Thanks!

I attach a patch that selects the root frame here.  As a next step I'll
use the frame hosting the mru-window.

martin

[-- Attachment #2: tty-set-frame-visible.diff --]
[-- Type: text/x-patch, Size: 1725 bytes --]

diff --git a/src/frame.h b/src/frame.h
index bff610472c0..df73e63eb69 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -1428,23 +1428,6 @@ #define AUTO_FRAME_ARG(name, parameter, value)		\
 /* False means there are no visible garbaged frames.  */
 extern bool frame_garbaged;
 
-/* Set visibility of frame F.
-   We call redisplay_other_windows to make sure the frame gets redisplayed
-   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, bool v)
-{
-  if (v)
-    {
-      if (v == 1 && f->visible != 1)
-	redisplay_other_windows ();
-      if (FRAME_GARBAGED_P (f))
-	frame_garbaged = true;
-    }
-  f->visible = v;
-}
-
 /* Set iconified status of frame F.  */
 INLINE void
 SET_FRAME_ICONIFIED (struct frame *f, int i)
@@ -1541,6 +1524,31 @@ #define SELECTED_FRAME()				\
       ? XFRAME (selected_frame)				\
       : (emacs_abort (), (struct frame *) 0))
 
+/* Set visibility of frame F.
+   We call redisplay_other_windows to make sure the frame gets redisplayed
+   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, bool v)
+{
+  if (v)
+    {
+      if (v == 1 && f->visible != 1)
+	redisplay_other_windows ();
+      if (FRAME_GARBAGED_P (f))
+	frame_garbaged = true;
+    }
+  else if (is_tty_child_frame (f) && f == XFRAME (selected_frame))
+    {
+      struct frame *r = root_frame (f);
+      Lisp_Object frame;
+
+      XSETFRAME (frame, r);
+      Fselect_frame (frame, Qnil);
+    }
+
+  f->visible = v;
+}
 \f
 /***********************************************************************
 			Display-related Macros

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

* Re: "Final" version of tty child frames
  2025-01-12  9:45                                         ` martin rudalics
@ 2025-01-12 10:31                                           ` Gerd Möllmann
  2025-01-14  9:11                                             ` martin rudalics
  0 siblings, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-12 10:31 UTC (permalink / raw)
  To: martin rudalics; +Cc: Jared Finder, Eli Zaretskii, emacs-devel

martin rudalics <rudalics@gmx.at> writes:

>>> I'll try to make this work. Till then we could try to use the root
>>> frame as argument.
>>
>> Thanks!
>
> I attach a patch that selects the root frame here.  As a next step I'll
> use the frame hosting the mru-window.

Thanks!



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

* Re: "Final" version of tty child frames
  2025-01-05  3:48               ` Gerd Möllmann
@ 2025-01-13  6:32                 ` Jared Finder
  2025-01-13  7:13                   ` Gerd Möllmann
                                     ` (2 more replies)
  0 siblings, 3 replies; 194+ messages in thread
From: Jared Finder @ 2025-01-13  6:32 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: martin rudalics, Eli Zaretskii, emacs-devel

On 2025-01-04 19:48, Gerd Möllmann wrote:
> Jared Finder <jared@finder.org> writes:
> 
>> Unfortunately, the redisplay code in Emacs is super hard for me to
>> understand and I can't figure out where or how the cursor gets
>> positioned.  If someone could give me some pointers here, I'd love to
>> investigate further.
> 
> I think you want to take a look at dispnew.c, functions
> combine_updates_for_frame, and there at this part
> 
>   /* If a child is displayed, and the cursor is displayed in another
>      frame, the child might lay above the cursor, so that it appears to
>      "shine through" the child.  Avoid that because it's confusing.  */
>   if (topmost_child)
>     terminal_cursor_magic (root, topmost_child);
>   flush_terminal (root

Thanks for the pointer.  There's definitely something strange going on 
here, like Emacs is writing the entire row and not restoring the cursor 
to the previous location.

The good news is that this seems to affect both xterm as well as Linux 
terminals, so hopefully someone more familiar with this code can debug.  
A simple repro case (with posframe installed):

1. Evaluate (posframe-show " *test*" :string (concat "A\nB\n*long 
text*\nC\nD"))
2. Now try to place the cursor on some text after one of the single 
letter lines while still being in the root frame. (I've been using M-x 
info since the directory node has lots of nice text)

When I do this, my cursor always appears at the end of the line so long 
as it is after the posframe.  When the cursor is before the posframe, 
the cursor is displayed fine.

I confirmed via debugging code that tty_cursor_to and tty_raw_cursor_to 
were being called with the expected values, so the cursor must be moving 
after that.

   -- MJF



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

* Re: "Final" version of tty child frames
  2025-01-13  6:32                 ` Jared Finder
@ 2025-01-13  7:13                   ` Gerd Möllmann
  2025-01-13 13:13                   ` Eli Zaretskii
  2025-01-14  9:11                   ` martin rudalics
  2 siblings, 0 replies; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-13  7:13 UTC (permalink / raw)
  To: Jared Finder; +Cc: martin rudalics, Eli Zaretskii, emacs-devel

Jared Finder <jared@finder.org> writes:

> The good news is that this seems to affect both xterm as well as Linux
> terminals, so hopefully someone more familiar with this code can
> debug.  A simple repro case (with posframe installed):
>
> 1. Evaluate (posframe-show " *test*" :string (concat "A\nB\n*long
> text*\nC\nD"))
> 2. Now try to place the cursor on some text after one of the single
> letter lines while still being in the root frame. (I've been using M-x
> info since the directory node has lots of nice text)

I can't yet reproduce this. I do this

  (posframe-show " *test*" :string (concat "A\nB\n*long text*\nC\nD")
     :background-color "grey15")

That pops up the posframe. Then I C-h i d to display the Info dir in the
root frame. The posframe is still there of course. Only one window on
the root frame.
 
> When I do this, my cursor always appears at the end of the line so
> long as it is after the posframe.  When the cursor is before the
> posframe, the cursor is displayed fine.

I move point in the root frame, in the Info dir buffer. When it is under
the posframe, my bar cursor is not shown, when I move right, it appears
when it is no longer under the posframe, when I move left again it
disappears. I've done that for the lines of the posframe, but I don't
see the cursor behaving strangely.

This is on macOS, iTerm2, TERM=xterm-256color.

So, something is still missing in the recipe, I guess. Or I'm missing
something when trying to reproduce this. Could you perhaps try with -Q
and without posframe?

> I confirmed via debugging code that tty_cursor_to and
> tty_raw_cursor_to were being called with the expected values, so the
> cursor must be moving after that.
>
>   -- MJF



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

* Re: "Final" version of tty child frames
  2025-01-13  6:32                 ` Jared Finder
  2025-01-13  7:13                   ` Gerd Möllmann
@ 2025-01-13 13:13                   ` Eli Zaretskii
  2025-01-14  9:11                   ` martin rudalics
  2 siblings, 0 replies; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-13 13:13 UTC (permalink / raw)
  To: Jared Finder; +Cc: gerd.moellmann, rudalics, emacs-devel

> Date: Sun, 12 Jan 2025 22:32:03 -0800
> From: Jared Finder <jared@finder.org>
> Cc: martin rudalics <rudalics@gmx.at>, Eli Zaretskii <eliz@gnu.org>,
>  emacs-devel@gnu.org
> 
> On 2025-01-04 19:48, Gerd Möllmann wrote:
> > Jared Finder <jared@finder.org> writes:
> > 
> >> Unfortunately, the redisplay code in Emacs is super hard for me to
> >> understand and I can't figure out where or how the cursor gets
> >> positioned.  If someone could give me some pointers here, I'd love to
> >> investigate further.
> > 
> > I think you want to take a look at dispnew.c, functions
> > combine_updates_for_frame, and there at this part
> > 
> >   /* If a child is displayed, and the cursor is displayed in another
> >      frame, the child might lay above the cursor, so that it appears to
> >      "shine through" the child.  Avoid that because it's confusing.  */
> >   if (topmost_child)
> >     terminal_cursor_magic (root, topmost_child);
> >   flush_terminal (root
> 
> Thanks for the pointer.  There's definitely something strange going on 
> here, like Emacs is writing the entire row and not restoring the cursor 
> to the previous location.
> 
> The good news is that this seems to affect both xterm as well as Linux 
> terminals, so hopefully someone more familiar with this code can debug.  
> A simple repro case (with posframe installed):
> 
> 1. Evaluate (posframe-show " *test*" :string (concat "A\nB\n*long 
> text*\nC\nD"))
> 2. Now try to place the cursor on some text after one of the single 
> letter lines while still being in the root frame. (I've been using M-x 
> info since the directory node has lots of nice text)
> 
> When I do this, my cursor always appears at the end of the line so long 
> as it is after the posframe.  When the cursor is before the posframe, 
> the cursor is displayed fine.
> 
> I confirmed via debugging code that tty_cursor_to and tty_raw_cursor_to 
> were being called with the expected values, so the cursor must be moving 
> after that.

Cursor on TTY frames can move because we write to the terminal (I'm
sure you knew that, but just to be sure).

Another idea is to record the stuff sent to the terminal, by using the
open-termscript command.  Then you can examine that file to find out
what Emacs actually sent to the screen.



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

* Re: "Final" version of tty child frames
  2025-01-12  9:03                                       ` Gerd Möllmann
  2025-01-12  9:45                                         ` martin rudalics
@ 2025-01-14  9:10                                         ` martin rudalics
  2025-01-14  9:24                                           ` Gerd Möllmann
  1 sibling, 1 reply; 194+ messages in thread
From: martin rudalics @ 2025-01-14  9:10 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Jared Finder, Eli Zaretskii, emacs-devel

 >> The same approach is
 >> probably also needed when deleting or iconifying a child frame.
 >
 > Iconifying :-O? I found having tooltips on ttys already quite funny, but
 > iconified child frames would beat that :-)

Agreed.  But we have the option 'iconify-child-frame' and we probably
have to make sure that it does something reasonable on ttys.

martin



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

* Re: "Final" version of tty child frames
  2025-01-12 10:31                                           ` Gerd Möllmann
@ 2025-01-14  9:11                                             ` martin rudalics
  2025-01-14  9:25                                               ` Gerd Möllmann
  0 siblings, 1 reply; 194+ messages in thread
From: martin rudalics @ 2025-01-14  9:11 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Jared Finder, Eli Zaretskii, emacs-devel

 >> I attach a patch that selects the root frame here.  As a next step I'll
 >> use the frame hosting the mru-window.

I pushed that now.  I have also taken the liberty to move root_frame to
frame.c and add a new function 'frame-root-frame' that exposes it to
Lisp.  That function might be useful for people who want to adapt their
GUI child frame code for ttys.

martin



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

* Re: "Final" version of tty child frames
  2025-01-13  6:32                 ` Jared Finder
  2025-01-13  7:13                   ` Gerd Möllmann
  2025-01-13 13:13                   ` Eli Zaretskii
@ 2025-01-14  9:11                   ` martin rudalics
  2025-01-14  9:37                     ` Gerd Möllmann
  2 siblings, 1 reply; 194+ messages in thread
From: martin rudalics @ 2025-01-14  9:11 UTC (permalink / raw)
  To: Jared Finder, Gerd Möllmann; +Cc: Eli Zaretskii, emacs-devel

 > 1. Evaluate (posframe-show " *test*" :string (concat "A\nB\n*long text*\nC\nD"))
 > 2. Now try to place the cursor on some text after one of the single letter lines while still being in the root frame. (I've been using M-x info since the directory node has lots of nice text)
 >
 > When I do this, my cursor always appears at the end of the line so long as it is after the posframe.  When the cursor is before the posframe, the cursor is displayed fine.

Confirmed.  But it happens only with cursor movement here.  Inserting
text in the underlying window works normally.  Also mouse clicks in the
part on the right of the child frame are handled correctly.

martin



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

* Re: "Final" version of tty child frames
  2025-01-14  9:10                                         ` martin rudalics
@ 2025-01-14  9:24                                           ` Gerd Möllmann
  0 siblings, 0 replies; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-14  9:24 UTC (permalink / raw)
  To: martin rudalics; +Cc: Jared Finder, Eli Zaretskii, emacs-devel

martin rudalics <rudalics@gmx.at> writes:

>>> The same approach is
>>> probably also needed when deleting or iconifying a child frame.
>>
>> Iconifying :-O? I found having tooltips on ttys already quite funny, but
>> iconified child frames would beat that :-)
>
> Agreed.  But we have the option 'iconify-child-frame' and we probably
> have to make sure that it does something reasonable on ttys.
>
> martin

Of couse. Take that Neovim! :-)



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

* Re: "Final" version of tty child frames
  2025-01-14  9:11                                             ` martin rudalics
@ 2025-01-14  9:25                                               ` Gerd Möllmann
  0 siblings, 0 replies; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-14  9:25 UTC (permalink / raw)
  To: martin rudalics; +Cc: Jared Finder, Eli Zaretskii, emacs-devel

martin rudalics <rudalics@gmx.at> writes:

>>> I attach a patch that selects the root frame here.  As a next step I'll
>>> use the frame hosting the mru-window.
>
> I pushed that now.  I have also taken the liberty to move root_frame to
> frame.c and add a new function 'frame-root-frame' that exposes it to
> Lisp.  That function might be useful for people who want to adapt their
> GUI child frame code for ttys.
>
> martin

Thanks!



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

* Re: "Final" version of tty child frames
  2025-01-14  9:11                   ` martin rudalics
@ 2025-01-14  9:37                     ` Gerd Möllmann
  2025-01-14  9:55                       ` martin rudalics
  2025-01-14 13:38                       ` Eli Zaretskii
  0 siblings, 2 replies; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-14  9:37 UTC (permalink / raw)
  To: martin rudalics; +Cc: Jared Finder, Eli Zaretskii, emacs-devel

martin rudalics <rudalics@gmx.at> writes:

>> 1. Evaluate (posframe-show " *test*" :string (concat "A\nB\n*long text*\nC\nD"))
>> 2. Now try to place the cursor on some text after one of the single
>> letter lines while still being in the root frame. (I've been using
>> M-x info since the directory node has lots of nice text)
>>
>> When I do this, my cursor always appears at the end of the line so
>> long as it is after the posframe. When the cursor is before the
>> posframe, the cursor is displayed fine.
>
> Confirmed.  But it happens only with cursor movement here.  Inserting
> text in the underlying window works normally.  Also mouse clicks in the
> part on the right of the child frame are handled correctly.
>
> martin

Maybe I have an idea, but that is only based on memories how things were
decades ago.

Around xdisp.c:20500, there should be something like this in master:

  /* Handle case where text has not changed, only point, and it has
     not moved off the frame, and we are not retrying after hscroll.
     (current_matrix_up_to_date_p is true when retrying.)  */
  if (current_matrix_up_to_date_p
      && (rc = try_cursor_movement (window, startp, &temp_scroll_step),
	  rc != CURSOR_MOVEMENT_CANNOT_BE_USED))
    {

What happens if you add "false && " to the if-condition so that the body
isn't executed?

Background: I used the frame's current matrix to speed
up cursor movement redisplay. Haven't checked if that's still the case,
but I wouldn't know why not. And the root's current matrix is different
with a child frame on it.

And the question is of course why I can't reproduce this. Hm.



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

* Re: "Final" version of tty child frames
  2025-01-14  9:37                     ` Gerd Möllmann
@ 2025-01-14  9:55                       ` martin rudalics
  2025-01-14 10:12                         ` Gerd Möllmann
  2025-01-14 13:58                         ` Eli Zaretskii
  2025-01-14 13:38                       ` Eli Zaretskii
  1 sibling, 2 replies; 194+ messages in thread
From: martin rudalics @ 2025-01-14  9:55 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Jared Finder, Eli Zaretskii, emacs-devel

 > Around xdisp.c:20500, there should be something like this in master:
 >
 >    /* Handle case where text has not changed, only point, and it has
 >       not moved off the frame, and we are not retrying after hscroll.
 >       (current_matrix_up_to_date_p is true when retrying.)  */
 >    if (current_matrix_up_to_date_p
 >        && (rc = try_cursor_movement (window, startp, &temp_scroll_step),
 > 	  rc != CURSOR_MOVEMENT_CANNOT_BE_USED))
 >      {
 >
 > What happens if you add "false && " to the if-condition so that the body
 > isn't executed?

Doesn't help here (I have

   if (current_matrix_up_to_date_p
       && false
       && (rc = try_cursor_movement (window, startp, &temp_scroll_step),
	  rc != CURSOR_MOVEMENT_CANNOT_BE_USED))

to make sure I did what you mean).

It's obvious that this _is_ a redisplay optimization trap.  But it's a
very sophisticated one because the cursor ends up at the correct end of
the line.  With other words C-f does what C-e does.

martin



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

* Re: "Final" version of tty child frames
  2025-01-14  9:55                       ` martin rudalics
@ 2025-01-14 10:12                         ` Gerd Möllmann
  2025-01-14 13:58                         ` Eli Zaretskii
  1 sibling, 0 replies; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-14 10:12 UTC (permalink / raw)
  To: martin rudalics; +Cc: Jared Finder, Eli Zaretskii, emacs-devel

martin rudalics <rudalics@gmx.at> writes:

>> Around xdisp.c:20500, there should be something like this in master:
>>
>>    /* Handle case where text has not changed, only point, and it has
>>       not moved off the frame, and we are not retrying after hscroll.
>>       (current_matrix_up_to_date_p is true when retrying.)  */
>>    if (current_matrix_up_to_date_p
>>        && (rc = try_cursor_movement (window, startp, &temp_scroll_step),
>> 	  rc != CURSOR_MOVEMENT_CANNOT_BE_USED))
>>      {
>>
>> What happens if you add "false && " to the if-condition so that the body
>> isn't executed?
>
> Doesn't help here (I have
>
>   if (current_matrix_up_to_date_p
>       && false
>       && (rc = try_cursor_movement (window, startp, &temp_scroll_step),
> 	  rc != CURSOR_MOVEMENT_CANNOT_BE_USED))
>
> to make sure I did what you mean).

That's what I meant, yes. Too bad.

>
> It's obvious that this _is_ a redisplay optimization trap.  But it's a
> very sophisticated one because the cursor ends up at the correct end of
> the line.  With other words C-f does what C-e does.

Yeah, everything in redisplay is kind of sophisticated, aka complicated
:-). I need to find a way to reproduce this somehow. I'm afraid that
could take some days, since I'm currently in the midst of doing
something else.

Please remind me, should I forget.



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

* Re: "Final" version of tty child frames
  2025-01-14  9:37                     ` Gerd Möllmann
  2025-01-14  9:55                       ` martin rudalics
@ 2025-01-14 13:38                       ` Eli Zaretskii
  1 sibling, 0 replies; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-14 13:38 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: rudalics, jared, emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: Jared Finder <jared@finder.org>,  Eli Zaretskii <eliz@gnu.org>,
>   emacs-devel@gnu.org
> Date: Tue, 14 Jan 2025 10:37:24 +0100
> 
> Maybe I have an idea, but that is only based on memories how things were
> decades ago.
> 
> Around xdisp.c:20500, there should be something like this in master:
> 
>   /* Handle case where text has not changed, only point, and it has
>      not moved off the frame, and we are not retrying after hscroll.
>      (current_matrix_up_to_date_p is true when retrying.)  */
>   if (current_matrix_up_to_date_p
>       && (rc = try_cursor_movement (window, startp, &temp_scroll_step),
> 	  rc != CURSOR_MOVEMENT_CANNOT_BE_USED))
>     {
> 
> What happens if you add "false && " to the if-condition so that the body
> isn't executed?

In a build configured with --enable-checking=glyphs, one can simply
set inhibit-try-cursor-movement non-nil to cause try_cursor_movement
give up.



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

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

> Date: Tue, 14 Jan 2025 10:55:31 +0100
> Cc: Jared Finder <jared@finder.org>, Eli Zaretskii <eliz@gnu.org>,
>  emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
> 
>  > Around xdisp.c:20500, there should be something like this in master:
>  >
>  >    /* Handle case where text has not changed, only point, and it has
>  >       not moved off the frame, and we are not retrying after hscroll.
>  >       (current_matrix_up_to_date_p is true when retrying.)  */
>  >    if (current_matrix_up_to_date_p
>  >        && (rc = try_cursor_movement (window, startp, &temp_scroll_step),
>  > 	  rc != CURSOR_MOVEMENT_CANNOT_BE_USED))
>  >      {
>  >
>  > What happens if you add "false && " to the if-condition so that the body
>  > isn't executed?
> 
> Doesn't help here (I have
> 
>    if (current_matrix_up_to_date_p
>        && false
>        && (rc = try_cursor_movement (window, startp, &temp_scroll_step),
> 	  rc != CURSOR_MOVEMENT_CANNOT_BE_USED))
> 
> to make sure I did what you mean).
> 
> It's obvious that this _is_ a redisplay optimization trap.  But it's a
> very sophisticated one because the cursor ends up at the correct end of
> the line.  With other words C-f does what C-e does.

I can suggest two things:

  . try the other inhibit-try-* variables, which disable some
    redisplay optimizations
  . invoke M-x trace-redisplay and watch the messages emitted while
    you do the recipe -- that could give you hints about the code
    paths taken

(For the second recipe, I find it useful to disable show-paren-mode
and global-eldoc-mode, so that redisplay is not triggered by unrelated
events.)



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

* Re: "Final" version of tty child frames
  2025-01-14 13:58                         ` Eli Zaretskii
@ 2025-01-14 17:33                           ` martin rudalics
  2025-01-14 19:25                             ` Gerd Möllmann
  2025-01-14 20:29                             ` Eli Zaretskii
  0 siblings, 2 replies; 194+ messages in thread
From: martin rudalics @ 2025-01-14 17:33 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gerd.moellmann, jared, emacs-devel

 >> It's obvious that this _is_ a redisplay optimization trap.  But it's a
 >> very sophisticated one because the cursor ends up at the correct end of
 >> the line.  With other words C-f does what C-e does.
 >
 > I can suggest two things:

These don't help.  Meanwhile I suppose that it's not related to
optimizations.  What happens is:

I'm in a base (underlying, normal) frame which is partially covered by a
child frame.  With 'line-beginning-position' 686 and 'line-end-position'
759 and point at 724 I try to move point to position 725 which is the
first position on that row covered by the child frame.

In set_cursor_from_row at

   if (!((row->reversed_p ? glyph > glyphs_end : glyph < glyphs_end)
	&& BUFFERP (glyph->object) && glyph->charpos == pt_old)
       && !(bpos_max <= pt_old && pt_old <= bpos_covered))

I have x = 39 which is the expected value 725 - 686 and I would like it.
But that check fails (or better "succeeds") because of

(gdb) p glyph->object
$104 = XIL(0)
(gdb) p glyph->charpos
$105 = -1
(gdb) p pt_old
$106 = 725
(gdb) p bpos_covered
$109 = 0

Then in

       else if ((row->truncated_on_right_p && pt_old > bpos_max)
	       /* Zero-width characters produce no glyphs.  */
	       || (!empty_line_p
		   && (row->reversed_p
		       ? glyph_after > glyphs_end
		       : glyph_after < glyphs_end)))
	{
	  cursor = glyph_after;
	  x = -1;
	}

I have

(gdb) p !empty_line_p
$111 = 1
(gdb) p glyph_after < glyphs_end
$110 = 1

x is set to -1 and in

   if (x < 0)
     {
       struct glyph *g;

       /* Need to compute x that corresponds to GLYPH.  */
       for (g = row->glyphs[TEXT_AREA], x = row->x; g < glyph; g++)
	{
	  if (g >= row->glyphs[TEXT_AREA] + row->used[TEXT_AREA])
	    emacs_abort ();
	  x += g->pixel_width;
	}
     }

x eventually becomes 73 (759 - 686) which is what I see.  What could I
do to make the check at the top succeed?

martin



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

* Re: "Final" version of tty child frames
  2025-01-14 17:33                           ` martin rudalics
@ 2025-01-14 19:25                             ` Gerd Möllmann
  2025-01-15  4:29                               ` Gerd Möllmann
  2025-01-14 20:29                             ` Eli Zaretskii
  1 sibling, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-14 19:25 UTC (permalink / raw)
  To: martin rudalics; +Cc: Eli Zaretskii, jared, emacs-devel

martin rudalics <rudalics@gmx.at> writes:

>>> It's obvious that this _is_ a redisplay optimization trap.  But it's a
>>> very sophisticated one because the cursor ends up at the correct end of
>>> the line.  With other words C-f does what C-e does.
>>
>> I can suggest two things:
>
> These don't help.  Meanwhile I suppose that it's not related to
> optimizations.  What happens is:
>
> I'm in a base (underlying, normal) frame which is partially covered by a
> child frame.  With 'line-beginning-position' 686 and 'line-end-position'
> 759 and point at 724 I try to move point to position 725 which is the
> first position on that row covered by the child frame.
>
> In set_cursor_from_row at
>
>   if (!((row->reversed_p ? glyph > glyphs_end : glyph < glyphs_end)
> 	&& BUFFERP (glyph->object) && glyph->charpos == pt_old)
>       && !(bpos_max <= pt_old && pt_old <= bpos_covered))
>
> I have x = 39 which is the expected value 725 - 686 and I would like it.
> But that check fails (or better "succeeds") because of
>
> (gdb) p glyph->object
> $104 = XIL(0)
> (gdb) p glyph->charpos
> $105 = -1
> (gdb) p pt_old
> $106 = 725
> (gdb) p bpos_covered
> $109 = 0
>
> Then in
>
>       else if ((row->truncated_on_right_p && pt_old > bpos_max)
> 	       /* Zero-width characters produce no glyphs.  */
> 	       || (!empty_line_p
> 		   && (row->reversed_p
> 		       ? glyph_after > glyphs_end
> 		       : glyph_after < glyphs_end)))
> 	{
> 	  cursor = glyph_after;
> 	  x = -1;
> 	}
>
> I have
>
> (gdb) p !empty_line_p
> $111 = 1
> (gdb) p glyph_after < glyphs_end
> $110 = 1
>
> x is set to -1 and in
>
>   if (x < 0)
>     {
>       struct glyph *g;
>
>       /* Need to compute x that corresponds to GLYPH.  */
>       for (g = row->glyphs[TEXT_AREA], x = row->x; g < glyph; g++)
> 	{
> 	  if (g >= row->glyphs[TEXT_AREA] + row->used[TEXT_AREA])
> 	    emacs_abort ();
> 	  x += g->pixel_width;
> 	}
>     }
>
> x eventually becomes 73 (759 - 686) which is what I see.  What could I
> do to make the check at the top succeed?
>
> martin

I'm not sure that would be sufficient.

The basic problem seems to go like below. Sorry if that gets a bit
complicated :-).

For ttys, I've introduced "frame-based" redisplay. The glyph producing
part of redisplay always works on windows, both on GUIs and ttys. On
GUIs, windows have their own glyph matrices which are independent of
each other. The frame has no glyph matrices. On ttys, frames _have_
glyph matrices that are allocated for them, and window glyph matrices
are sub-allocated from frame matrices. IOW when I look at a glyph G in a
window matrix on a tty frame, G is actually in the frame matrix.

I did that frame-based hocus pocus to minimize terminal output by being
able to determine if one can scroll part of the display. It's a long
story. The important part here is that a root frame's current matrix
_must_ contain what's displayed on the terminal for the whole update
part of redisplay on ttys to work.

Good.

Now let's assume we have a root frame R and a child C, and we are in the
update phase of redisplay.

If C is _not_ displayed, R's current matrix reflects R's contents; for
all windows on R, a window's current matrix corresponds to what is
currently displayed. I've used that fact to speed up redisplay. I didn't
remember set_cursor_from_row specifically, but it's one of those places,
as one can simply see from the fact that it looks at glyphs from R's
current matrix.

If C _is_ displayed, the combine_updates step in dispnew.c manipulates
R's current matrix to include C. This is needed because of what I said
above: R's current matrix must be what is displayed on the terminal. But
now, for all (x, y) on R displaying C, the glyphs now actually re copied
fro some window on C!. Or, said the other way 'round, the assumption
made that some window's current matrix can be used in cases like
set_cursor_from_row is no longer valid!

So, that's the problem, or what I think it is. A bit difficult to
describe :-/. It's sophisticated :-)



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

* Re: "Final" version of tty child frames
  2025-01-14 17:33                           ` martin rudalics
  2025-01-14 19:25                             ` Gerd Möllmann
@ 2025-01-14 20:29                             ` Eli Zaretskii
  1 sibling, 0 replies; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-14 20:29 UTC (permalink / raw)
  To: martin rudalics; +Cc: gerd.moellmann, jared, emacs-devel

> Date: Tue, 14 Jan 2025 18:33:01 +0100
> Cc: gerd.moellmann@gmail.com, jared@finder.org, emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
> 
> I'm in a base (underlying, normal) frame which is partially covered by a
> child frame.  With 'line-beginning-position' 686 and 'line-end-position'
> 759 and point at 724 I try to move point to position 725 which is the
> first position on that row covered by the child frame.
> 
> In set_cursor_from_row at
> 
>    if (!((row->reversed_p ? glyph > glyphs_end : glyph < glyphs_end)
> 	&& BUFFERP (glyph->object) && glyph->charpos == pt_old)
>        && !(bpos_max <= pt_old && pt_old <= bpos_covered))
> 
> I have x = 39 which is the expected value 725 - 686 and I would like it.
> But that check fails (or better "succeeds") because of

If set_cursor_from_row wasn't taught about child frames on TTY, all
hell will break loose, because that function doesn't check that the
glyphs belong to the same window/frame.  The 'struct glyph' now
includes the pointer to the frame from which the glyph came, so these
tests should be augmented to have the loop stop when it bumps into a
glyph from another frame.



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

* Re: "Final" version of tty child frames
  2025-01-14 19:25                             ` Gerd Möllmann
@ 2025-01-15  4:29                               ` Gerd Möllmann
  2025-01-15  8:41                                 ` martin rudalics
  0 siblings, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-15  4:29 UTC (permalink / raw)
  To: martin rudalics; +Cc: Eli Zaretskii, jared, emacs-devel

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

>> x eventually becomes 73 (759 - 686) which is what I see.  What could I
>> do to make the check at the top succeed?
>>
>> martin
>
> I'm not sure that would be sufficient.
>
> The basic problem seems to go like below. Sorry if that gets a bit
> complicated :-).
>
> For ttys, I've introduced "frame-based" redisplay. The glyph producing
> part of redisplay always works on windows, both on GUIs and ttys. On
> GUIs, windows have their own glyph matrices which are independent of
> each other. The frame has no glyph matrices. On ttys, frames _have_
> glyph matrices that are allocated for them, and window glyph matrices
> are sub-allocated from frame matrices. IOW when I look at a glyph G in a
> window matrix on a tty frame, G is actually in the frame matrix.
>
> I did that frame-based hocus pocus to minimize terminal output by being
> able to determine if one can scroll part of the display. It's a long
> story. The important part here is that a root frame's current matrix
> _must_ contain what's displayed on the terminal for the whole update
> part of redisplay on ttys to work.
>
> Good.
>
> Now let's assume we have a root frame R and a child C, and we are in the
> update phase of redisplay.
>
> If C is _not_ displayed, R's current matrix reflects R's contents; for
> all windows on R, a window's current matrix corresponds to what is
> currently displayed. I've used that fact to speed up redisplay. I didn't
> remember set_cursor_from_row specifically, but it's one of those places,
> as one can simply see from the fact that it looks at glyphs from R's
> current matrix.
>
> If C _is_ displayed, the combine_updates step in dispnew.c manipulates
> R's current matrix to include C. This is needed because of what I said
> above: R's current matrix must be what is displayed on the terminal. But
> now, for all (x, y) on R displaying C, the glyphs now actually re copied
> fro some window on C!. Or, said the other way 'round, the assumption
> made that some window's current matrix can be used in cases like
> set_cursor_from_row is no longer valid!
>
> So, that's the problem, or what I think it is. A bit difficult to
> describe :-/. It's sophisticated :-)

Let me just quickly add something that came to my mind, namely that one
could tackle the problem in two different ways:

1. In xdisp.c, so that current matrices are not used in tty frames
having visible child frames and blah blah.

2. In a completely different way that a avoids the problem altogether.
That was the idea I had a few minutes ago:

In combine_updates, currently the root frame's current matrix is
modified by copying glyphs from child frames to it. That makes the
problems.

What if we didn't do that? Instead, build a new matrix, let's call it a
combined matrix, that is built by first copying the root frame's matrix,
and then the child frames' matrix. That would leave the root's current
matrix alone. The combined current matrix would then represent what is
currently displayed on the terminal.

It's just an idea, not a complete design. Other random thoughts

- Need only be done if child frames are visible

- Maybe a lot of work in the call tree below combine_updates which
  currently only knows about current/desired matrices. But maybe one
  could cheat and replace current/desired matrices with
  combined-current/desired matrices temporarily?

- what about mirror_line_dance or what the name is.



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

* Re: "Final" version of tty child frames
  2025-01-15  4:29                               ` Gerd Möllmann
@ 2025-01-15  8:41                                 ` martin rudalics
  2025-01-15  8:58                                   ` Gerd Möllmann
  2025-01-15 15:27                                   ` Eli Zaretskii
  0 siblings, 2 replies; 194+ messages in thread
From: martin rudalics @ 2025-01-15  8:41 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Eli Zaretskii, jared, emacs-devel

 > Let me just quickly add something that came to my mind, namely that one
 > could tackle the problem in two different ways:

Back to redisplay optimization.  Suppose my cursor is in the underlying
frame immediately on the left of the left edge of a child frame.  I now
start typing a character and the cursor disappears.  I continue typing
and the cursor eventually reappears quite normally on the right of the
right edge of the child frame.  IIUC this is cursor movement when buffer
text was modified, that is, without redisplay optimization.

So couldn't we, as long as there's a visible child frame and the user
navigates (moves the cursor) in an underlying frame, inhibit redisplay
optimizations and pretend a modification of the underlying frame?

As a rule, navigating in the invisible area beneath a child frame
shouldn't be a very frequent operation and maybe we could enhance
performance by finding some heuristics detecting whether the cursor is
anywhere "near" a child frame.

martin



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

* Re: "Final" version of tty child frames
  2025-01-15  8:41                                 ` martin rudalics
@ 2025-01-15  8:58                                   ` Gerd Möllmann
  2025-01-15 15:29                                     ` Eli Zaretskii
  2025-01-15 15:27                                   ` Eli Zaretskii
  1 sibling, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-15  8:58 UTC (permalink / raw)
  To: martin rudalics; +Cc: Eli Zaretskii, jared, emacs-devel

martin rudalics <rudalics@gmx.at> writes:

>> Let me just quickly add something that came to my mind, namely that one
>> could tackle the problem in two different ways:
>
> Back to redisplay optimization.  Suppose my cursor is in the underlying
> frame immediately on the left of the left edge of a child frame.  I now
> start typing a character and the cursor disappears.  I continue typing
> and the cursor eventually reappears quite normally on the right of the
> right edge of the child frame.  IIUC this is cursor movement when buffer
> text was modified, that is, without redisplay optimization.
>
> So couldn't we, as long as there's a visible child frame and the user
> navigates (moves the cursor) in an underlying frame, inhibit redisplay
> optimizations and pretend a modification of the underlying frame?

We could try. Back in my days, i-r-o did something slightly different
wrt optimizations, but maybe it's sufficient. (It kind of forced using
try_window instead of try_window_id for example.)

> As a rule, navigating in the invisible area beneath a child frame
> shouldn't be a very frequent operation and maybe we could enhance
> performance by finding some heuristics detecting whether the cursor is
> anywhere "near" a child frame.

We have glyph::frame (only on ttys), which should be equal to the frame
from where the glyphs stems, except maybe for things like vertical
border between windows but that's probably irrelevant for the cursor.
Don't know at the moment how to use that, though.



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

* Re: "Final" version of tty child frames
  2025-01-15  8:41                                 ` martin rudalics
  2025-01-15  8:58                                   ` Gerd Möllmann
@ 2025-01-15 15:27                                   ` Eli Zaretskii
  2025-01-15 17:01                                     ` martin rudalics
  1 sibling, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-15 15:27 UTC (permalink / raw)
  To: martin rudalics; +Cc: gerd.moellmann, jared, emacs-devel

> Date: Wed, 15 Jan 2025 09:41:59 +0100
> Cc: Eli Zaretskii <eliz@gnu.org>, jared@finder.org, emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
> 
>  > Let me just quickly add something that came to my mind, namely that one
>  > could tackle the problem in two different ways:
> 
> Back to redisplay optimization.

Does this mean you've solved the problem in set_cursor_from_row?
Because that problem, if it's real, has nothing to do with redisplay
optimizations.

> Suppose my cursor is in the underlying
> frame immediately on the left of the left edge of a child frame.  I now
> start typing a character and the cursor disappears.  I continue typing
> and the cursor eventually reappears quite normally on the right of the
> right edge of the child frame.  IIUC this is cursor movement when buffer
> text was modified, that is, without redisplay optimization.

There are still redisplay optimizations involved: Emacs doesn't update
the display except on the single screen line where you insert
characters.

Or maybe you are talking about a different kind of "optimizations"?

> So couldn't we, as long as there's a visible child frame and the user
> navigates (moves the cursor) in an underlying frame, inhibit redisplay
> optimizations and pretend a modification of the underlying frame?

Pretend and do what on display?



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

* Re: "Final" version of tty child frames
  2025-01-15  8:58                                   ` Gerd Möllmann
@ 2025-01-15 15:29                                     ` Eli Zaretskii
  2025-01-15 15:38                                       ` Gerd Möllmann
  0 siblings, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-15 15:29 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.org,  emacs-devel@gnu.org
> Date: Wed, 15 Jan 2025 09:58:03 +0100
> 
> martin rudalics <rudalics@gmx.at> writes:
> 
> >> Let me just quickly add something that came to my mind, namely that one
> >> could tackle the problem in two different ways:
> >
> > Back to redisplay optimization.  Suppose my cursor is in the underlying
> > frame immediately on the left of the left edge of a child frame.  I now
> > start typing a character and the cursor disappears.  I continue typing
> > and the cursor eventually reappears quite normally on the right of the
> > right edge of the child frame.  IIUC this is cursor movement when buffer
> > text was modified, that is, without redisplay optimization.
> >
> > So couldn't we, as long as there's a visible child frame and the user
> > navigates (moves the cursor) in an underlying frame, inhibit redisplay
> > optimizations and pretend a modification of the underlying frame?
> 
> We could try. Back in my days, i-r-o did something slightly different
> wrt optimizations, but maybe it's sufficient. (It kind of forced using
> try_window instead of try_window_id for example.)

I guess you have in mind the per-buffer flag
prevent_redisplay_optimizations_p?  In that case, it still does what
you remember.  But I think Martin is talking about a different kind of
optimizations.



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

* Re: "Final" version of tty child frames
  2025-01-15 15:29                                     ` Eli Zaretskii
@ 2025-01-15 15:38                                       ` Gerd Möllmann
  0 siblings, 0 replies; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-15 15:38 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.org,  emacs-devel@gnu.org
>> Date: Wed, 15 Jan 2025 09:58:03 +0100
>> 
>> martin rudalics <rudalics@gmx.at> writes:
>> 
>> >> Let me just quickly add something that came to my mind, namely that one
>> >> could tackle the problem in two different ways:
>> >
>> > Back to redisplay optimization.  Suppose my cursor is in the underlying
>> > frame immediately on the left of the left edge of a child frame.  I now
>> > start typing a character and the cursor disappears.  I continue typing
>> > and the cursor eventually reappears quite normally on the right of the
>> > right edge of the child frame.  IIUC this is cursor movement when buffer
>> > text was modified, that is, without redisplay optimization.
>> >
>> > So couldn't we, as long as there's a visible child frame and the user
>> > navigates (moves the cursor) in an underlying frame, inhibit redisplay
>> > optimizations and pretend a modification of the underlying frame?
>> 
>> We could try. Back in my days, i-r-o did something slightly different
>> wrt optimizations, but maybe it's sufficient. (It kind of forced using
>> try_window instead of try_window_id for example.)
>
> I guess you have in mind the per-buffer flag
> prevent_redisplay_optimizations_p?  In that case, it still does what
> you remember.  

Yes, I think so. When I heard it, it rang a bell :-)

> But I think Martin is talking about a different kind of optimizations.

Probably.



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

* Re: "Final" version of tty child frames
  2025-01-15 15:27                                   ` Eli Zaretskii
@ 2025-01-15 17:01                                     ` martin rudalics
  2025-01-15 17:29                                       ` Gerd Möllmann
  2025-01-15 19:43                                       ` Eli Zaretskii
  0 siblings, 2 replies; 194+ messages in thread
From: martin rudalics @ 2025-01-15 17:01 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gerd.moellmann, jared, emacs-devel

 >> Back to redisplay optimization.
 >
 > Does this mean you've solved the problem in set_cursor_from_row?
 > Because that problem, if it's real, has nothing to do with redisplay
 > optimizations.

Turning the table: When I insert a character in the row at hand the
problem doesn't occur.  When I just move point it does occur.  Why?

 >> Suppose my cursor is in the underlying
 >> frame immediately on the left of the left edge of a child frame.  I now
 >> start typing a character and the cursor disappears.  I continue typing
 >> and the cursor eventually reappears quite normally on the right of the
 >> right edge of the child frame.  IIUC this is cursor movement when buffer
 >> text was modified, that is, without redisplay optimization.
 >
 > There are still redisplay optimizations involved: Emacs doesn't update
 > the display except on the single screen line where you insert
 > characters.

The cursor is on that single line.

 > Or maybe you are talking about a different kind of "optimizations"?
 >
 >> So couldn't we, as long as there's a visible child frame and the user
 >> navigates (moves the cursor) in an underlying frame, inhibit redisplay
 >> optimizations and pretend a modification of the underlying frame?
 >
 > Pretend and do what on display?

Do what Emacs would do when it inserted a character: For example,
atomically move the cursor, insert a character and delete it.

But it's obvious that all this would be overly complicated.  I think now
that the problem should be fixed in set_cursor_from_row using the
following approach:

First special case the approach to tty frames.  On a tty there is only
one cursor in the selected window so we don't have to care about the
cursor in any window but the selected one.  Right?

In set_cursor_from_row in step 1 advance over the border glyphs of a
child frame, over any glyph whose object is a buffer but not the buffer
of the selected window and maybe over any glyph whose object is nil (I
don't know what kind of such glyphs might exist - maybe the
truncation/continuation line glyphs).  Whatever it is, we have to bypass
the

	   /* glyph was not inserted by redisplay for internal purposes */
	   && !NILP (glyph->object))

check.

If the glyph at point is not an object of the selected window's buffer,
the cursor is not shown (IIUC there is a separate routine for detecting
that but we maybe could do it here right away).  Otherwise show the
cursor as usual.

If step 1 fails, we could try a similar approach in step 2 (stop when a
glyph's object is at PT) but I didn't look into that yet.

martin



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

* Re: "Final" version of tty child frames
  2025-01-15 17:01                                     ` martin rudalics
@ 2025-01-15 17:29                                       ` Gerd Möllmann
  2025-01-15 17:35                                         ` Gerd Möllmann
  2025-01-15 19:43                                       ` Eli Zaretskii
  1 sibling, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-15 17:29 UTC (permalink / raw)
  To: martin rudalics; +Cc: Eli Zaretskii, jared, emacs-devel

martin rudalics <rudalics@gmx.at> writes:

>>> Back to redisplay optimization.
>>
>> Does this mean you've solved the problem in set_cursor_from_row?
>> Because that problem, if it's real, has nothing to do with redisplay
>> optimizations.
>
> Turning the table: When I insert a character in the row at hand the
> problem doesn't occur.  When I just move point it does occur.  Why?

These two cases will probably end up in different places in
redisplay_internal.

> First special case the approach to tty frames.  On a tty there is only
> one cursor in the selected window so we don't have to care about the
> cursor in any window but the selected one.  Right?

Right, but I would check if set_cursor_from_row sets something that is
used somewhere else. I don't think so, but it's something like 450 lines
long, so I won't bet.

(And I think anything special has only to be done if the frame is a root
frame AND has visible children. Only a root frame's current matrix can
be affected, and only by visible children.)

>
> In set_cursor_from_row in step 1 advance over the border glyphs of a
> child frame, over any glyph whose object is a buffer but not the buffer
> of the selected window and maybe over any glyph whose object is nil (I
> don't know what kind of such glyphs might exist - maybe the
> truncation/continuation line glyphs).  Whatever it is, we have to bypass
> the
>
> 	   /* glyph was not inserted by redisplay for internal purposes */
> 	   && !NILP (glyph->object))
>
> check.

Hm, you should be able to use glyph::frame here somewhere. if matrix is
the current matrix, and the frame is a tty root frame, and glyph->frame
!= frame, then it can come only from a child frame would be my theory.

>
> If the glyph at point is not an object of the selected window's buffer,
> the cursor is not shown (IIUC there is a separate routine for detecting
> that but we maybe could do it here right away).  Otherwise show the
> cursor as usual.

I think you mean is_cursor_obscured in dispnew.c? Maybe that could
indeed be used. I'm using the current matrix and glyph::frame there.

>
> If step 1 fails, we could try a similar approach in step 2 (stop when a
> glyph's object is at PT) but I didn't look into that yet.
>
> martin



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

* Re: "Final" version of tty child frames
  2025-01-15 17:29                                       ` Gerd Möllmann
@ 2025-01-15 17:35                                         ` Gerd Möllmann
  0 siblings, 0 replies; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-15 17:35 UTC (permalink / raw)
  To: martin rudalics; +Cc: Eli Zaretskii, jared, emacs-devel

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

>> If the glyph at point is not an object of the selected window's buffer,
>> the cursor is not shown (IIUC there is a separate routine for detecting
>> that but we maybe could do it here right away).  Otherwise show the
>> cursor as usual.
>
> I think you mean is_cursor_obscured in dispnew.c? Maybe that could
> indeed be used. I'm using the current matrix and glyph::frame there.

Or something extracted from it, taking (x, y) as parameters.



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

* Re: "Final" version of tty child frames
  2025-01-15 17:01                                     ` martin rudalics
  2025-01-15 17:29                                       ` Gerd Möllmann
@ 2025-01-15 19:43                                       ` Eli Zaretskii
  2025-01-16  4:35                                         ` Gerd Möllmann
  1 sibling, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-15 19:43 UTC (permalink / raw)
  To: martin rudalics; +Cc: gerd.moellmann, jared, emacs-devel

> Date: Wed, 15 Jan 2025 18:01:52 +0100
> Cc: gerd.moellmann@gmail.com, jared@finder.org, emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
> 
>  >> Back to redisplay optimization.
>  >
>  > Does this mean you've solved the problem in set_cursor_from_row?
>  > Because that problem, if it's real, has nothing to do with redisplay
>  > optimizations.
> 
> Turning the table: When I insert a character in the row at hand the
> problem doesn't occur.  When I just move point it does occur.  Why?

Sheer luck?

> First special case the approach to tty frames.  On a tty there is only
> one cursor in the selected window so we don't have to care about the
> cursor in any window but the selected one.  Right?

No, I don't think so.  set_cursor_from_row is needed not only to know
where to draw the cursor, it is also needed to make sure that the
cursor is visible.

> In set_cursor_from_row in step 1 advance over the border glyphs of a
> child frame, over any glyph whose object is a buffer but not the buffer
> of the selected window and maybe over any glyph whose object is nil (I
> don't know what kind of such glyphs might exist - maybe the
> truncation/continuation line glyphs).  Whatever it is, we have to bypass
> the
> 
> 	   /* glyph was not inserted by redisplay for internal purposes */
> 	   && !NILP (glyph->object))
> 
> check.
> 
> If the glyph at point is not an object of the selected window's buffer,
> the cursor is not shown (IIUC there is a separate routine for detecting
> that but we maybe could do it here right away).  Otherwise show the
> cursor as usual.
> 
> If step 1 fails, we could try a similar approach in step 2 (stop when a
> glyph's object is at PT) but I didn't look into that yet.

This is fragile.  E.g., the same buffer could be displayed both in the
parent and the child frames.  And there could be other complications,
for example, the glyphs inserted by redisplay could appear where you
don't expect them, because frames overlap.

I suggested earlier to look at glyph->frame, and stop when you see a
frame that is different from XFRAME (w->frame), where w is the window
pointer passed to set_cursor_from_row.  Isn't that a simpler and more
reliable way to know when you bump into a glyph from the wrong frame,
which means you've stepped into a different frame?

Then the next question is what do we want set_cursor_from_row to
return in that case.



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

* Re: "Final" version of tty child frames
  2025-01-15 19:43                                       ` Eli Zaretskii
@ 2025-01-16  4:35                                         ` Gerd Möllmann
  2025-01-16  8:16                                           ` Eli Zaretskii
  0 siblings, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-16  4:35 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: martin rudalics, jared, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> Date: Wed, 15 Jan 2025 18:01:52 +0100
>> Cc: gerd.moellmann@gmail.com, jared@finder.org, emacs-devel@gnu.org
>> From: martin rudalics <rudalics@gmx.at>
>> 
>>  >> Back to redisplay optimization.
>>  >
>>  > Does this mean you've solved the problem in set_cursor_from_row?
>>  > Because that problem, if it's real, has nothing to do with redisplay
>>  > optimizations.
>> 
>> Turning the table: When I insert a character in the row at hand the
>> problem doesn't occur.  When I just move point it does occur.  Why?
>
> Sheer luck?

It's something simple:

When inserting a character, a desired glyph row is built. That row can
be used to set the cursor.

When just moving the cursor, no desired glyphs are produced, and the
current glyph row is used, which can contain glyphs from child frames.



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

* Re: "Final" version of tty child frames
  2025-01-16  4:35                                         ` Gerd Möllmann
@ 2025-01-16  8:16                                           ` Eli Zaretskii
  2025-01-16  8:42                                             ` Gerd Möllmann
  0 siblings, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-16  8:16 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: rudalics, jared, emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: martin rudalics <rudalics@gmx.at>,  jared@finder.org,  emacs-devel@gnu.org
> Date: Thu, 16 Jan 2025 05:35:13 +0100
> 
> When inserting a character, a desired glyph row is built. That row can
> be used to set the cursor.
> 
> When just moving the cursor, no desired glyphs are produced, and the
> current glyph row is used, which can contain glyphs from child frames.

The current glyph row which can contain glyphs from a child frame is a
glyph row of the frame's current matrix.  But try_cursor_movement uses
the _window's_ current matrix.  Are you saying that, due to glyph row
sub-allocation on TTY frames, the glyphs from the child frame "seep"
into the current glyph matrix of the parent frame's windows?  If so,
this could cause many problems, so we could prevent that somehow, in
which case set_cursor_from_row could be kept unaltered.

Alternatively, set_cursor_from_row should be modified to deal with
glyphs from another frame.  Then it will be able to work correctly
both on desired and on current glyph rows.  But then the question is
what do we expect from set_cursor_from_row to do when the correct
position of the cursor is obscured by the child frame?
set_cursor_from_row produces two results: it returns a boolean that is
true if the cursor should be placed on the glyph row passed to it,
false otherwise; and it sets the w->cursor fields if it returns true.
If this should still be the case when a child frame conceals the
cursor position on its parent frame, we need to see how to do that
when the glyph row doesn't include the glyphs from the window passed
to the function.  Alternatively, we could do something special in this
case (but what?), since the cursor will not be drawn anyway.



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

* Re: "Final" version of tty child frames
  2025-01-16  8:16                                           ` Eli Zaretskii
@ 2025-01-16  8:42                                             ` Gerd Möllmann
  2025-01-16  9:47                                               ` Eli Zaretskii
  2025-01-16 10:02                                               ` martin rudalics
  0 siblings, 2 replies; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-16  8:42 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: martin rudalics <rudalics@gmx.at>,  jared@finder.org,  emacs-devel@gnu.org
>> Date: Thu, 16 Jan 2025 05:35:13 +0100
>> 
>> When inserting a character, a desired glyph row is built. That row can
>> be used to set the cursor.
>> 
>> When just moving the cursor, no desired glyphs are produced, and the
>> current glyph row is used, which can contain glyphs from child frames.
>
> The current glyph row which can contain glyphs from a child frame is a
> glyph row of the frame's current matrix.  But try_cursor_movement uses
> the _window's_ current matrix.  Are you saying that, due to glyph row
> sub-allocation on TTY frames, the glyphs from the child frame "seep"
> into the current glyph matrix of the parent frame's windows?  

Yes that is so.

> If so, this could cause many problems, so we could prevent that
> somehow, in which case set_cursor_from_row could be kept unaltered.

That's what I propose as an idea in a previous message. Basically, leave
the root's matrices alone, and let the display update use a set of other
matrices for what is displayed and what should be displayed on the
terminal. The desired "terminal matrix" would be built by copying the
root desired matrix, and all of its children.

Something like that. It's just an idea, and I didn't think it through,
but I think it could be a clean solution.

> Alternatively, set_cursor_from_row should be modified to deal with
> glyphs from another frame.  Then it will be able to work correctly
> both on desired and on current glyph rows.  But then the question is
> what do we expect from set_cursor_from_row to do when the correct
> position of the cursor is obscured by the child frame?

That's the question, yes.

> set_cursor_from_row produces two results: it returns a boolean that is
> true if the cursor should be placed on the glyph row passed to it,
> false otherwise; and it sets the w->cursor fields if it returns true.
> If this should still be the case when a child frame conceals the
> cursor position on its parent frame, we need to see how to do that
> when the glyph row doesn't include the glyphs from the window passed
> to the function.  Alternatively, we could do something special in this
> case (but what?), since the cursor will not be drawn anyway.

Maybe it could return not a boolean but an enum with a third value
'invisible' in case it cannot place the cursor anywhere because it finds
child frame glyphs? It would have to look carefully though, and cannot
just give up on the first child frame glyph it encounters. I'd think
that setting the cursor position could be left out. We have only one
cursor on ttys, and it will not end up in that window for the
'invivable' case.



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

* Re: "Final" version of tty child frames
  2025-01-16  8:42                                             ` Gerd Möllmann
@ 2025-01-16  9:47                                               ` Eli Zaretskii
  2025-01-16 10:22                                                 ` Gerd Möllmann
  2025-01-16 10:02                                               ` martin rudalics
  1 sibling, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-16  9:47 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: rudalics, jared, emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: rudalics@gmx.at,  jared@finder.org,  emacs-devel@gnu.org
> Date: Thu, 16 Jan 2025 09:42:27 +0100
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > The current glyph row which can contain glyphs from a child frame is a
> > glyph row of the frame's current matrix.  But try_cursor_movement uses
> > the _window's_ current matrix.  Are you saying that, due to glyph row
> > sub-allocation on TTY frames, the glyphs from the child frame "seep"
> > into the current glyph matrix of the parent frame's windows?  
> 
> Yes that is so.
> 
> > If so, this could cause many problems, so we could prevent that
> > somehow, in which case set_cursor_from_row could be kept unaltered.
> 
> That's what I propose as an idea in a previous message. Basically, leave
> the root's matrices alone, and let the display update use a set of other
> matrices for what is displayed and what should be displayed on the
> terminal. The desired "terminal matrix" would be built by copying the
> root desired matrix, and all of its children.
> 
> Something like that. It's just an idea, and I didn't think it through,
> but I think it could be a clean solution.

Yes.  Its advantage is that then set_cursor_from_row could be left
unaltered.

> > Alternatively, set_cursor_from_row should be modified to deal with
> > glyphs from another frame.  Then it will be able to work correctly
> > both on desired and on current glyph rows.  But then the question is
> > what do we expect from set_cursor_from_row to do when the correct
> > position of the cursor is obscured by the child frame?
> 
> That's the question, yes.
> 
> > set_cursor_from_row produces two results: it returns a boolean that is
> > true if the cursor should be placed on the glyph row passed to it,
> > false otherwise; and it sets the w->cursor fields if it returns true.
> > If this should still be the case when a child frame conceals the
> > cursor position on its parent frame, we need to see how to do that
> > when the glyph row doesn't include the glyphs from the window passed
> > to the function.  Alternatively, we could do something special in this
> > case (but what?), since the cursor will not be drawn anyway.
> 
> Maybe it could return not a boolean but an enum with a third value
> 'invisible' in case it cannot place the cursor anywhere because it finds
> child frame glyphs?

The value it returns is not enough, because w->cursor fields are
accessed in other places.  So it sounds like a separate frame matrix
would be a better, easier (and cleaner) solution.

> It would have to look carefully though, and cannot
> just give up on the first child frame glyph it encounters.

Yes, it will have to treat that as glyphs that came from a string, and
skip them.

> I'd think that setting the cursor position could be left out. We
> have only one cursor on ttys, and it will not end up in that window
> for the 'invivable' case.

If we do that, i.e. set w->cursor.x negative, which is an indication
that the cursor position of the window is unknown, we'd need to make
sure set_cursor_from_row is called when the underlying frame is
exposed.  So the separate-matrix alternative still sounds simpler.



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

* Re: "Final" version of tty child frames
  2025-01-16  8:42                                             ` Gerd Möllmann
  2025-01-16  9:47                                               ` Eli Zaretskii
@ 2025-01-16 10:02                                               ` martin rudalics
  2025-01-16 10:23                                                 ` Gerd Möllmann
  2025-01-16 11:19                                                 ` Eli Zaretskii
  1 sibling, 2 replies; 194+ messages in thread
From: martin rudalics @ 2025-01-16 10:02 UTC (permalink / raw)
  To: Gerd Möllmann, Eli Zaretskii; +Cc: jared, emacs-devel

 >> But then the question is
 >> what do we expect from set_cursor_from_row to do when the correct
 >> position of the cursor is obscured by the child frame?
 >
 > That's the question, yes.

In a fix I don't like I've put it on the first character on the right of
any child frame encountered.  But then the real point position will be
still hidden under the child frame.  So when you lean on C-f (in a
non-bidi context), the cursor immediately jumps to that position on the
right of any child frame but point still has to catch up with that
situation and only when it does that the cursor will continue moving.
Disconcerting and ugly.

martin



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

* Re: "Final" version of tty child frames
  2025-01-16  9:47                                               ` Eli Zaretskii
@ 2025-01-16 10:22                                                 ` Gerd Möllmann
  2025-01-16 11:24                                                   ` Eli Zaretskii
  0 siblings, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-16 10:22 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: rudalics, jared, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> If we do that, i.e. set w->cursor.x negative, which is an indication
> that the cursor position of the window is unknown, we'd need to make
> sure set_cursor_from_row is called when the underlying frame is
> exposed.  So the separate-matrix alternative still sounds simpler.

From the top of my head, I have some points though, that one would have
to take into account when using separate matrices:

- I don't know if the copying of root's matrix is fast enough to not be
  noticeable. I think it's well possible one won't notice anything, but if
  it is noticeable, one would have to arrange that the "terminal
  matrices" are only used in case we have a visible child frame. I don't
  have a clear picture of how complicated that would be.

- Tty menus work currently by saving/restoring the current frmae matrix,
  That would have to be adapted.

Maybe there are more.



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

* Re: "Final" version of tty child frames
  2025-01-16 10:02                                               ` martin rudalics
@ 2025-01-16 10:23                                                 ` Gerd Möllmann
  2025-01-16 10:50                                                   ` martin rudalics
  2025-01-16 11:19                                                 ` Eli Zaretskii
  1 sibling, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-16 10:23 UTC (permalink / raw)
  To: martin rudalics; +Cc: Eli Zaretskii, jared, emacs-devel

martin rudalics <rudalics@gmx.at> writes:

>>> But then the question is
>>> what do we expect from set_cursor_from_row to do when the correct
>>> position of the cursor is obscured by the child frame?
>>
>> That's the question, yes.
>
> In a fix I don't like I've put it on the first character on the right of
> any child frame encountered.  But then the real point position will be
> still hidden under the child frame.  So when you lean on C-f (in a
> non-bidi context), the cursor immediately jumps to that position on the
> right of any child frame but point still has to catch up with that
> situation and only when it does that the cursor will continue moving.
> Disconcerting and ugly.
>
> martin

Eli said cursor position < 0 means "don't know". Maybe try that?



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

* Re: "Final" version of tty child frames
  2025-01-16 10:23                                                 ` Gerd Möllmann
@ 2025-01-16 10:50                                                   ` martin rudalics
  2025-01-16 10:55                                                     ` Gerd Möllmann
  2025-01-16 11:42                                                     ` Eli Zaretskii
  0 siblings, 2 replies; 194+ messages in thread
From: martin rudalics @ 2025-01-16 10:50 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Eli Zaretskii, jared, emacs-devel

 > Eli said cursor position < 0 means "don't know". Maybe try that?

I now subtract 1 from the w->cursor.hpos if glyph->charpos is larger
than PT and it seems to work with your terminal_cursor_magic.  So unless
there are more fundamental reasons to preserve obscured parts of frame
matrices, I think this problem can be fixed in set_cursor_from_row.

martin



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

* Re: "Final" version of tty child frames
  2025-01-16 10:50                                                   ` martin rudalics
@ 2025-01-16 10:55                                                     ` Gerd Möllmann
  2025-01-16 11:42                                                     ` Eli Zaretskii
  1 sibling, 0 replies; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-16 10:55 UTC (permalink / raw)
  To: martin rudalics; +Cc: Eli Zaretskii, jared, emacs-devel

martin rudalics <rudalics@gmx.at> writes:

>> Eli said cursor position < 0 means "don't know". Maybe try that?
>
> I now subtract 1 from the w->cursor.hpos if glyph->charpos is larger
> than PT and it seems to work with your terminal_cursor_magic.  So unless
> there are more fundamental reasons to preserve obscured parts of frame
> matrices, I think this problem can be fixed in set_cursor_from_row.
>
> martin

If that's all that's needed, it's for sure simpler than separate
matrices. 👍 👍



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

* Re: "Final" version of tty child frames
  2025-01-16 10:02                                               ` martin rudalics
  2025-01-16 10:23                                                 ` Gerd Möllmann
@ 2025-01-16 11:19                                                 ` Eli Zaretskii
  1 sibling, 0 replies; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-16 11:19 UTC (permalink / raw)
  To: martin rudalics; +Cc: gerd.moellmann, jared, emacs-devel

> Date: Thu, 16 Jan 2025 11:02:50 +0100
> Cc: jared@finder.org, emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
> 
>  >> But then the question is
>  >> what do we expect from set_cursor_from_row to do when the correct
>  >> position of the cursor is obscured by the child frame?
>  >
>  > That's the question, yes.
> 
> In a fix I don't like I've put it on the first character on the right of
> any child frame encountered.  But then the real point position will be
> still hidden under the child frame.  So when you lean on C-f (in a
> non-bidi context), the cursor immediately jumps to that position on the
> right of any child frame but point still has to catch up with that
> situation and only when it does that the cursor will continue moving.
> Disconcerting and ugly.

Yes, I don't think this strategy could work well.

I could try thinking about something more sophisticated and complex,
but given the alternative with having a separate glyph matrix, it
sounds unnecessary.



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

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

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: rudalics@gmx.at,  jared@finder.org,  emacs-devel@gnu.org
> Date: Thu, 16 Jan 2025 11:22:44 +0100
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > If we do that, i.e. set w->cursor.x negative, which is an indication
> > that the cursor position of the window is unknown, we'd need to make
> > sure set_cursor_from_row is called when the underlying frame is
> > exposed.  So the separate-matrix alternative still sounds simpler.
> 
> >From the top of my head, I have some points though, that one would have
> to take into account when using separate matrices:
> 
> - I don't know if the copying of root's matrix is fast enough to not be
>   noticeable. I think it's well possible one won't notice anything, but if
>   it is noticeable, one would have to arrange that the "terminal
>   matrices" are only used in case we have a visible child frame. I don't
>   have a clear picture of how complicated that would be.
> 
> - Tty menus work currently by saving/restoring the current frmae matrix,
>   That would have to be adapted.

I agree, but these seem to be simpler and more straightforward to fix
than the problems of what to do in set_cursor_from_row, for which I
currently don't even see a good solution.



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

* Re: "Final" version of tty child frames
  2025-01-16 10:50                                                   ` martin rudalics
  2025-01-16 10:55                                                     ` Gerd Möllmann
@ 2025-01-16 11:42                                                     ` Eli Zaretskii
  2025-01-16 12:15                                                       ` Gerd Möllmann
  1 sibling, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-16 11:42 UTC (permalink / raw)
  To: martin rudalics; +Cc: gerd.moellmann, jared, emacs-devel

> Date: Thu, 16 Jan 2025 11:50:04 +0100
> Cc: Eli Zaretskii <eliz@gnu.org>, jared@finder.org, emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
> 
>  > Eli said cursor position < 0 means "don't know". Maybe try that?
> 
> I now subtract 1 from the w->cursor.hpos if glyph->charpos is larger
> than PT

This assumes unidirectional text, doesn't it?  The glyph whose charpos
is <= PT could well be to the right, not to the left.  In the brave
new world of bidirectional display, character positions change
non-linearly with screen positions.

> and it seems to work with your terminal_cursor_magic.  So unless
> there are more fundamental reasons to preserve obscured parts of frame
> matrices, I think this problem can be fixed in set_cursor_from_row.

I very much doubt that.  The sheer complexity of that function is
because we cannot rely on character positions increasing monotonically
with the horizontal screen position, so let's please not add any code
there which ignores the bidi complications.



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

* Re: "Final" version of tty child frames
  2025-01-16 11:42                                                     ` Eli Zaretskii
@ 2025-01-16 12:15                                                       ` Gerd Möllmann
  2025-01-16 12:50                                                         ` Eli Zaretskii
  0 siblings, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-16 12:15 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: martin rudalics, jared, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: martin rudalics <rudalics@gmx.at>
>> 
>> I now subtract 1 from the w->cursor.hpos if glyph->charpos is larger
>> than PT
>
> This assumes unidirectional text, doesn't it?  The glyph whose charpos
> is <= PT could well be to the right, not to the left.  In the brave
> new world of bidirectional display, character positions change
> non-linearly with screen positions.

We need only a suitable hpos/x though that is in the child frame.
That should make the cursor magic in dispnew.c work that prevents
cursors from "shining through child frames". Don't know what that means
for R2L though.




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

* Re: "Final" version of tty child frames
  2025-01-16 12:15                                                       ` Gerd Möllmann
@ 2025-01-16 12:50                                                         ` Eli Zaretskii
  2025-01-16 13:41                                                           ` Gerd Möllmann
  0 siblings, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-16 12:50 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: rudalics, jared, emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: martin rudalics <rudalics@gmx.at>,  jared@finder.org,  emacs-devel@gnu.org
> Date: Thu, 16 Jan 2025 13:15:48 +0100
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> >> From: martin rudalics <rudalics@gmx.at>
> >> 
> >> I now subtract 1 from the w->cursor.hpos if glyph->charpos is larger
> >> than PT
> >
> > This assumes unidirectional text, doesn't it?  The glyph whose charpos
> > is <= PT could well be to the right, not to the left.  In the brave
> > new world of bidirectional display, character positions change
> > non-linearly with screen positions.
> 
> We need only a suitable hpos/x though that is in the child frame.
> That should make the cursor magic in dispnew.c work that prevents
> cursors from "shining through child frames". Don't know what that means
> for R2L though.

"Suitable" in what way?  What is proposed here is to have an hpos
whose relation to charpos is indeterminate.  I'm not sure this will
fly.  AFAIU, we don't even understand well enough how come it fixed
the problem, do we?



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

* Re: "Final" version of tty child frames
  2025-01-16 12:50                                                         ` Eli Zaretskii
@ 2025-01-16 13:41                                                           ` Gerd Möllmann
  2025-01-16 15:22                                                             ` Eli Zaretskii
  0 siblings, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-16 13:41 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: rudalics, jared, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> We need only a suitable hpos/x though that is in the child frame.
>> That should make the cursor magic in dispnew.c work that prevents
>> cursors from "shining through child frames". Don't know what that means
>> for R2L though.
>
> "Suitable" in what way?  What is proposed here is to have an hpos
> whose relation to charpos is indeterminate.  I'm not sure this will
> fly.  

Can't say anything to that. Can you elaborate what the problem might be?

> AFAIU, we don't even understand well enough how come it fixed the
> problem, do we?

We do :-). The magic involved is in combine_updates_for_frame

  /* If a child is displayed, and the cursor is displayed in another
     frame, the child might lay above the cursor, so that it appears to
     "shine through" the child.  Avoid that because it's confusing.  */
  if (topmost_child)
    terminal_cursor_magic (root, topmost_child);

where

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));

and so on, which ultimately looks at w->cursor of the selected frame's
selected window. So "suitable" position means in this context, that (x,
y) is inside the child frame's rectangle (top, left, width, height).



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

* Re: "Final" version of tty child frames
  2025-01-16 13:41                                                           ` Gerd Möllmann
@ 2025-01-16 15:22                                                             ` Eli Zaretskii
  2025-01-16 15:46                                                               ` Gerd Möllmann
  0 siblings, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-16 15:22 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: rudalics, jared, emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: rudalics@gmx.at,  jared@finder.org,  emacs-devel@gnu.org
> Date: Thu, 16 Jan 2025 14:41:54 +0100
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> >> We need only a suitable hpos/x though that is in the child frame.
> >> That should make the cursor magic in dispnew.c work that prevents
> >> cursors from "shining through child frames". Don't know what that means
> >> for R2L though.
> >
> > "Suitable" in what way?  What is proposed here is to have an hpos
> > whose relation to charpos is indeterminate.  I'm not sure this will
> > fly.  
> 
> Can't say anything to that. Can you elaborate what the problem might be?
> 
> > AFAIU, we don't even understand well enough how come it fixed the
> > problem, do we?
> 
> We do :-). The magic involved is in combine_updates_for_frame
> 
>   /* If a child is displayed, and the cursor is displayed in another
>      frame, the child might lay above the cursor, so that it appears to
>      "shine through" the child.  Avoid that because it's confusing.  */
>   if (topmost_child)
>     terminal_cursor_magic (root, topmost_child);
> 
> where
> 
> 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));
> 
> and so on, which ultimately looks at w->cursor of the selected frame's
> selected window. So "suitable" position means in this context, that (x,
> y) is inside the child frame's rectangle (top, left, width, height).

That's just how the cursor is hidden.  What I meant was the other
problems Martin described, with moving the cursor:

> In a fix I don't like I've put it on the first character on the right of
> any child frame encountered.  But then the real point position will be
> still hidden under the child frame.  So when you lean on C-f (in a
> non-bidi context), the cursor immediately jumps to that position on the
> right of any child frame but point still has to catch up with that
> situation and only when it does that the cursor will continue moving.
> Disconcerting and ugly.

In general, when w->cursor are all non-negative, the display engine
believes that the cursor position corresponds to point, so violating
that could cause problems elsewhere.  At the very least, this should
be prominently documented, and perhaps also some indication of that
kept around, so that this abnormality could be easily detected if
needed.

IOW, let's not add one more subtle factoid into the display code, it
has enough already.



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

* Re: "Final" version of tty child frames
  2025-01-16 15:22                                                             ` Eli Zaretskii
@ 2025-01-16 15:46                                                               ` Gerd Möllmann
  2025-01-16 16:50                                                                 ` Eli Zaretskii
  0 siblings, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-16 15:46 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: rudalics, jared, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> That's just how the cursor is hidden.  What I meant was the other
> problems Martin described, with moving the cursor:
>
>> In a fix I don't like I've put it on the first character on the right of
>> any child frame encountered.  But then the real point position will be
>> still hidden under the child frame.  So when you lean on C-f (in a
>> non-bidi context), the cursor immediately jumps to that position on the
>> right of any child frame but point still has to catch up with that
>> situation and only when it does that the cursor will continue moving.
>> Disconcerting and ugly.

Martin describes a case where he set w->cursor to a position _outside_
of the child frame, immediately to the right of it. As soon as be moves
the cursor "under" the child frame, it therefore appears to the right of
it. And it stays there with C-f, until it is no longer obscured by the
child frame.

Makes perfect sense to me, is the wrong thing to do, and he doesn't do
that anymore.

> In general, when w->cursor are all non-negative, the display engine
> believes that the cursor position corresponds to point, so violating
> that could cause problems elsewhere.  

And my question is where that is.

> At the very least, this should be prominently documented, and perhaps
> also some indication of that kept around, so that this abnormality
> could be easily detected if needed.
>
> IOW, let's not add one more subtle factoid into the display code, it
> has enough already.

Let's Martin decide if he wants to do more.



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

* Re: "Final" version of tty child frames
  2025-01-16 15:46                                                               ` Gerd Möllmann
@ 2025-01-16 16:50                                                                 ` Eli Zaretskii
  2025-01-16 18:00                                                                   ` martin rudalics
  0 siblings, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-16 16:50 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: rudalics, jared, emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: rudalics@gmx.at,  jared@finder.org,  emacs-devel@gnu.org
> Date: Thu, 16 Jan 2025 16:46:39 +0100
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > In general, when w->cursor are all non-negative, the display engine
> > believes that the cursor position corresponds to point, so violating
> > that could cause problems elsewhere.  
> 
> And my question is where that is.

A $64K question.  In general, grep for "->cursor.vpos >= 0" as the
first approximation.

> > At the very least, this should be prominently documented, and perhaps
> > also some indication of that kept around, so that this abnormality
> > could be easily detected if needed.
> >
> > IOW, let's not add one more subtle factoid into the display code, it
> > has enough already.
> 
> Let's Martin decide if he wants to do more.

Of course.



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

* Re: "Final" version of tty child frames
  2025-01-16 16:50                                                                 ` Eli Zaretskii
@ 2025-01-16 18:00                                                                   ` martin rudalics
  2025-01-16 19:05                                                                     ` Gerd Möllmann
  2025-01-16 19:22                                                                     ` Eli Zaretskii
  0 siblings, 2 replies; 194+ messages in thread
From: martin rudalics @ 2025-01-16 18:00 UTC (permalink / raw)
  To: Eli Zaretskii, Gerd Möllmann; +Cc: jared, emacs-devel

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

 >> Let's Martin decide if he wants to do more.
 >
 > Of course.

I attach what I have done so far.  It only handles LR text so far and
affects only the special case where W is the selected window and there
is a child frame on its frame.

I also attach a file tty-child-frames.el to be loaded with emacs -Q -nw.
With C-l and M-l make one or two child frames.  In the normal frame do
M-x info and scroll it if necessary so you an see a line which extends
past one of these frames.  On such a line lean on C-f and C-b - the
cursor should remain invisible as long as point is hidden by a child
frame and should reappear as soon as it is no more hidden.

I won't dispute that the patch might be overly simplistic and probably
won't correctly handle properties like display strings in the hidden or
in the covering parts.  But what we want to handle here is a very
peculiar case and I think that an approximation like mine is better than
the present state of affairs.  And it can be easily removed as soon as a
working solution is found that allows to splice in any underlying frame
parts in the current matrix.

martin

[-- Attachment #2: tty-child-frames-cursor.diff --]
[-- Type: text/x-patch, Size: 3000 bytes --]

diff --git a/src/xdisp.c b/src/xdisp.c
index 4087ff975ee..52f3ebf6f2e 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -18228,6 +18228,32 @@ set_cursor_from_row (struct window *w, struct glyph_row *row,
      comes from a text property, not from an overlay.  */
   bool string_from_text_prop = false;
 
+  /* is_tty_selected is true iff the following hold
+     - W is the selected window.
+     - There exists at least one visible child frame whose root is the
+       same frame as the root of W's frame.  Such a frame could overlap
+       the position of point in W in which case we (1) don't want to
+       show the cursor and (2) make sure that if point is not overlapped
+       by that frame, the cursor is shown.  */
+  bool is_tty_selected = false;
+  struct frame *f = WINDOW_XFRAME (w);
+  struct frame *r = root_frame (WINDOW_XFRAME (w));
+
+  Lisp_Object tail, other;
+
+  if (w == XWINDOW (selected_window))
+    FOR_EACH_FRAME (tail, other)
+      {
+	struct frame *o = XFRAME (other);
+
+	if (FRAME_LIVE_P (o) && is_tty_child_frame (o)
+	    && FRAME_VISIBLE_P (o) && root_frame (o) == r)
+	  {
+	    is_tty_selected = true;
+	    break;
+	  }
+      }
+
   /* Don't even try doing anything if called for a mode-line or
      header-line or tab-line row, since the rest of the code isn't
      prepared to deal with such calamities.  */
@@ -18309,12 +18335,17 @@ set_cursor_from_row (struct window *w, struct glyph_row *row,
     while (/* not marched to end of glyph row */
 	   glyph < end
 	   /* glyph was not inserted by redisplay for internal purposes */
-	   && !NILP (glyph->object))
+	   && (!NILP (glyph->object) || is_tty_selected))
       {
-	if (BUFFERP (glyph->object))
+	if (is_tty_selected && f != glyph->frame)
+	  ;
+	else if (BUFFERP (glyph->object))
 	  {
 	    ptrdiff_t dpos = glyph->charpos - pt_old;
 
+	    if (is_tty_selected && dpos > 0) /* Overshot.  */
+	      break;
+
 	    if (glyph->charpos > bpos_max)
 	      bpos_max = glyph->charpos;
 	    if (glyph->charpos < bpos_min)
@@ -18473,7 +18504,9 @@ set_cursor_from_row (struct window *w, struct glyph_row *row,
      GLYPH_BEFORE and GLYPH_AFTER.  */
   if (!((row->reversed_p ? glyph > glyphs_end : glyph < glyphs_end)
 	&& BUFFERP (glyph->object) && glyph->charpos == pt_old)
-      && !(bpos_max <= pt_old && pt_old <= bpos_covered))
+      && !(bpos_max <= pt_old && pt_old <= bpos_covered)
+      && (!is_tty_selected
+	  || !(row->reversed_p ? glyph > glyphs_end : glyph < glyphs_end)))
     {
       /* An empty line has a single glyph whose OBJECT is nil and
 	 whose CHARPOS is the position of a newline on that line.
@@ -18786,6 +18819,13 @@ set_cursor_from_row (struct window *w, struct glyph_row *row,
     }
   w->cursor.hpos = glyph - row->glyphs[TEXT_AREA];
   w->cursor.x = x;
+
+  if (is_tty_selected && glyph->charpos > PT)
+    {
+      w->cursor.hpos = -1;
+      w->cursor.x = -1;
+    }
+
   w->cursor.vpos = MATRIX_ROW_VPOS (row, matrix) + dvpos;
   w->cursor.y = row->y + dy;
 

[-- Attachment #3: tty-child-frames.el --]
[-- Type: text/x-emacs-lisp, Size: 1045 bytes --]

(unless window-system (xterm-mouse-mode 1))

(defvar tty-1 nil)
(defvar tty-2 nil)

(defun tty-1 ()
  (interactive)
  (setq tty-1
	(make-frame
	 `((parent-frame . ,(frame-root-frame))
	   (left . 40) (top . 10)
	   (width . 0.3) (height . 0.5)
	   (border-width . 0)
	   (background-color . "yellow"))))
  (select-frame-set-input-focus tty-1))

(defun tty-2 ()
  (interactive)
  (setq tty-2
	(make-frame
	 `((parent-frame . ,(frame-root-frame))
	   (left . 10) (top . 10)
	   (width . 0.6) (height . 0.3)
	   (border-width . 0)
	   (background-color . "orange"))))
  (select-frame-set-input-focus tty-2))

(defun tty-toggle-1 ()
  (interactive)
  (if (frame-live-p tty-1)
      (if (frame-visible-p tty-1)
	  (make-frame-invisible tty-1)
	(make-frame-visible tty-1))
    (tty-1)))

(defun tty-toggle-2 ()
  (interactive)
  (if (frame-live-p tty-2)
      (if (frame-visible-p tty-2)
	  (make-frame-invisible tty-2)
	(make-frame-visible tty-2))
    (tty-2)))

(global-set-key [(control l)] 'tty-toggle-1)
(global-set-key [(meta l)] 'tty-toggle-2)

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

* Re: "Final" version of tty child frames
  2025-01-16 18:00                                                                   ` martin rudalics
@ 2025-01-16 19:05                                                                     ` Gerd Möllmann
  2025-01-17  9:08                                                                       ` martin rudalics
  2025-01-16 19:22                                                                     ` Eli Zaretskii
  1 sibling, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-16 19:05 UTC (permalink / raw)
  To: martin rudalics; +Cc: Eli Zaretskii, jared, emacs-devel

martin rudalics <rudalics@gmx.at> writes:

> diff --git a/src/xdisp.c b/src/xdisp.c
> index 4087ff975ee..52f3ebf6f2e 100644
> --- a/src/xdisp.c
> +++ b/src/xdisp.c
> @@ -18228,6 +18228,32 @@ set_cursor_from_row (struct window *w, struct glyph_row *row,
>       comes from a text property, not from an overlay.  */
>    bool string_from_text_prop = false;
>  
> +  /* is_tty_selected is true iff the following hold
> +     - W is the selected window.
> +     - There exists at least one visible child frame whose root is the
> +       same frame as the root of W's frame.  Such a frame could overlap
> +       the position of point in W in which case we (1) don't want to
> +       show the cursor and (2) make sure that if point is not overlapped
> +       by that frame, the cursor is shown.  */

If one wants, one could sharpen the second point a bit. Glyphs from
child frames can only ever appear if

- in a current matrix

  matrix == w->current_matrix

- if w->frame is a root frame (NILP (w->frame->parent_frame)
  IOW, the current matrix of child frames will never show this
  phenomenon.

  struct frame *f = XFRAME (w->frame);
  is_tty_root_frame (f);

- w is the selected window 

- that root frame has visible child frames. Something like

  Lisp_Object z_order = frames_in_reverse_z_order (f, true);
  bool has_visible_child = CONSP (XCDR (z_order);

Which might be easier to understand than the below.

> +  bool is_tty_selected = false;
> +  struct frame *f = WINDOW_XFRAME (w);
> +  struct frame *r = root_frame (WINDOW_XFRAME (w));
> +
> +  Lisp_Object tail, other;
> +
> +  if (w == XWINDOW (selected_window))
> +    FOR_EACH_FRAME (tail, other)
> +      {
> +	struct frame *o = XFRAME (other);
> +
> +	if (FRAME_LIVE_P (o) && is_tty_child_frame (o)
> +	    && FRAME_VISIBLE_P (o) && root_frame (o) == r)
> +	  {
> +	    is_tty_selected = true;
> +	    break;
> +	  }
> +      }
> +

The rest LGTM, but I'm must admit that I don't understand parts of the
logic of this function well enough to be 100% sure. But the proof is in
the pudding - it seems to work with your .el. And AFAICS if the flag is
false, it does what it did before, so that's good, I'd say :-).

Thanks!



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

* Re: "Final" version of tty child frames
  2025-01-16 18:00                                                                   ` martin rudalics
  2025-01-16 19:05                                                                     ` Gerd Möllmann
@ 2025-01-16 19:22                                                                     ` Eli Zaretskii
  2025-01-16 19:32                                                                       ` Gerd Möllmann
  2025-01-17  9:08                                                                       ` martin rudalics
  1 sibling, 2 replies; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-16 19:22 UTC (permalink / raw)
  To: martin rudalics; +Cc: gerd.moellmann, jared, emacs-devel

> Date: Thu, 16 Jan 2025 19:00:24 +0100
> Cc: jared@finder.org, emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
> 
> I attach what I have done so far.  It only handles LR text so far and
> affects only the special case where W is the selected window and there
> is a child frame on its frame.
> 
> I also attach a file tty-child-frames.el to be loaded with emacs -Q -nw.
> With C-l and M-l make one or two child frames.  In the normal frame do
> M-x info and scroll it if necessary so you an see a line which extends
> past one of these frames.  On such a line lean on C-f and C-b - the
> cursor should remain invisible as long as point is hidden by a child
> frame and should reappear as soon as it is no more hidden.

Did you try this with auto-hscroll-mode, both set to t and to
current-line?  What happens then if you move cursor with C-n and enter
a line which is partially obscured by a child frame?  Or if point
becomes hidden because it enters the obscure part of the frame from
above or below, not from left or from right?

> +  if (is_tty_selected && glyph->charpos > PT)
> +    {
> +      w->cursor.hpos = -1;
> +      w->cursor.x = -1;
> +    }

Why do you need this test, and specifically the second part of the
condition?



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

* Re: "Final" version of tty child frames
  2025-01-16 19:22                                                                     ` Eli Zaretskii
@ 2025-01-16 19:32                                                                       ` Gerd Möllmann
  2025-01-16 19:55                                                                         ` Eli Zaretskii
  2025-01-17  9:08                                                                       ` martin rudalics
  1 sibling, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-16 19:32 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: martin rudalics, jared, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> Date: Thu, 16 Jan 2025 19:00:24 +0100
>> Cc: jared@finder.org, emacs-devel@gnu.org
>> From: martin rudalics <rudalics@gmx.at>
>> 
>> I attach what I have done so far.  It only handles LR text so far and
>> affects only the special case where W is the selected window and there
>> is a child frame on its frame.
>> 
>> I also attach a file tty-child-frames.el to be loaded with emacs -Q -nw.
>> With C-l and M-l make one or two child frames.  In the normal frame do
>> M-x info and scroll it if necessary so you an see a line which extends
>> past one of these frames.  On such a line lean on C-f and C-b - the
>> cursor should remain invisible as long as point is hidden by a child
>> frame and should reappear as soon as it is no more hidden.
>
> Did you try this with auto-hscroll-mode, both set to t and to
> current-line?  

If that leads to something being scrolled, we'll have built a desired
row to display the scrolled result, so that would not be a problematic
case if we check that the matrix used is the current matrix.

> What happens then if you move cursor with C-n and enter a line which
> is partially obscured by a child frame? Or if point becomes hidden
> because it enters the obscure part of the frame from above or below,
> not from left or from right?

From a redisplay perspective, I'd say C-n, C-p, C-b, C-p lead all to the
same "PT given, find it in glyph row".

>
>> +  if (is_tty_selected && glyph->charpos > PT)
>> +    {
>> +      w->cursor.hpos = -1;
>> +      w->cursor.x = -1;
>> +    }
>
> Why do you need this test, and specifically the second part of the
> condition?

Good question. Something I overlooked.



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

* Re: "Final" version of tty child frames
  2025-01-16 19:32                                                                       ` Gerd Möllmann
@ 2025-01-16 19:55                                                                         ` Eli Zaretskii
  2025-01-16 21:03                                                                           ` Gerd Möllmann
  2025-01-17  9:09                                                                           ` martin rudalics
  0 siblings, 2 replies; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-16 19:55 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: rudalics, jared, emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: martin rudalics <rudalics@gmx.at>,  jared@finder.org,  emacs-devel@gnu.org
> Date: Thu, 16 Jan 2025 20:32:17 +0100
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > Did you try this with auto-hscroll-mode, both set to t and to
> > current-line?  
> 
> If that leads to something being scrolled, we'll have built a desired
> row to display the scrolled result, so that would not be a problematic
> case if we check that the matrix used is the current matrix.

Yes, but the auto-hscroll-mode = current-line case is one of the cases
I remember that rely on w->cursor.vpos for some logic.

Btw, another complication is to do all this testing with
display-line-numbers turned on, especially when Emacs decides to
enlarge the number of digits in a line number, and all the display
of the parent frame needs to scroll horizontally left or right.

> > What happens then if you move cursor with C-n and enter a line which
> > is partially obscured by a child frame? Or if point becomes hidden
> > because it enters the obscure part of the frame from above or below,
> > not from left or from right?
> 
> >From a redisplay perspective, I'd say C-n, C-p, C-b, C-p lead all to the
> same "PT given, find it in glyph row".

Yes, but C-n and C-p change w->cursor.vpos, i.e. set_cursor_from_row
needs to decide that the previous row is no longer a cursor row, and
find the new row.  C-f and C-b don't have that problem.

> >> +  if (is_tty_selected && glyph->charpos > PT)
> >> +    {
> >> +      w->cursor.hpos = -1;
> >> +      w->cursor.x = -1;
> >> +    }
> >
> > Why do you need this test, and specifically the second part of the
> > condition?
> 
> Good question. Something I overlooked.

Here's another one: what happens if the horizontal coordinate where a
child frame starts to hide its parent is in the middle of a display
string or an overlay string?  Does cursor motion then works correctly?



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

* Re: "Final" version of tty child frames
  2025-01-16 19:55                                                                         ` Eli Zaretskii
@ 2025-01-16 21:03                                                                           ` Gerd Möllmann
  2025-01-17  6:48                                                                             ` Eli Zaretskii
  2025-01-17  9:09                                                                           ` martin rudalics
  1 sibling, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-16 21:03 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: martin rudalics <rudalics@gmx.at>,  jared@finder.org,  emacs-devel@gnu.org
>> Date: Thu, 16 Jan 2025 20:32:17 +0100
>>
>> Eli Zaretskii <eliz@gnu.org> writes:
>>
>> > Did you try this with auto-hscroll-mode, both set to t and to
>> > current-line?
>>
>> If that leads to something being scrolled, we'll have built a desired
>> row to display the scrolled result, so that would not be a problematic
>> case if we check that the matrix used is the current matrix.
>
> Yes, but the auto-hscroll-mode = current-line case is one of the cases
> I remember that rely on w->cursor.vpos for some logic.

Okay, but the vpos has already been determined in set_cursor_from_row,
right? And set_cursor_from_row should not touch vpos/y.

> Btw, another complication is to do all this testing with
> display-line-numbers turned on, especially when Emacs decides to
> enlarge the number of digits in a line number, and all the display
> of the parent frame needs to scroll horizontally left or right.

AFAIU, this also requires desired glyphs to be built. So I think this
should work too.

>> > What happens then if you move cursor with C-n and enter a line which
>> > is partially obscured by a child frame? Or if point becomes hidden
>> > because it enters the obscure part of the frame from above or below,
>> > not from left or from right?
>>
>> >From a redisplay perspective, I'd say C-n, C-p, C-b, C-p lead all to the
>> same "PT given, find it in glyph row".
>
> Yes, but C-n and C-p change w->cursor.vpos, i.e. set_cursor_from_row
> needs to decide that the previous row is no longer a cursor row, and
> find the new row.  C-f and C-b don't have that problem.

I made "row" const in set_cursor_from_row like so, to be sure

    static bool
    set_cursor_from_row (struct window *w, struct glyph_row *const row,

to make sure that 'row' is indeed not changed in the function. Which means
the decision about that is made in a caller. And I would expect that is
done by using the height of the row (which is 1).

>
>> >> +  if (is_tty_selected && glyph->charpos > PT)
>> >> +    {
>> >> +      w->cursor.hpos = -1;
>> >> +      w->cursor.x = -1;
>> >> +    }
>> >
>> > Why do you need this test, and specifically the second part of the
>> > condition?
>>
>> Good question. Something I overlooked.
>
> Here's another one: what happens if the horizontal coordinate where a
> child frame starts to hide its parent is in the middle of a display
> string or an overlay string?  Does cursor motion then works correctly?

That I'm not 100% sure of because I don't grasp set_cursor_from_row
completely. The glyphs of a display string have the frame set like other
glyphs have (see produce_glyphs and subroutines). I think we skip over
glyphs from strings, if I read that correctly, and stop after them. If
that's a glyph from a buffer. and glyph->frame != f we're under a child,
else not. Like I said, I'd bet only a small amount on that, perhaps you
and Martin could also check?



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

* Re: "Final" version of tty child frames
  2025-01-16 21:03                                                                           ` Gerd Möllmann
@ 2025-01-17  6:48                                                                             ` Eli Zaretskii
  2025-01-17  7:50                                                                               ` Gerd Möllmann
  2025-01-17  9:09                                                                               ` martin rudalics
  0 siblings, 2 replies; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-17  6:48 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: rudalics, jared, emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: rudalics@gmx.at,  jared@finder.org,  emacs-devel@gnu.org
> Date: Thu, 16 Jan 2025 22:03:54 +0100
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> >> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> >> Cc: martin rudalics <rudalics@gmx.at>,  jared@finder.org,  emacs-devel@gnu.org
> >> Date: Thu, 16 Jan 2025 20:32:17 +0100
> >>
> >> Eli Zaretskii <eliz@gnu.org> writes:
> >>
> >> > Did you try this with auto-hscroll-mode, both set to t and to
> >> > current-line?
> >>
> >> If that leads to something being scrolled, we'll have built a desired
> >> row to display the scrolled result, so that would not be a problematic
> >> case if we check that the matrix used is the current matrix.
> >
> > Yes, but the auto-hscroll-mode = current-line case is one of the cases
> > I remember that rely on w->cursor.vpos for some logic.
> 
> Okay, but the vpos has already been determined in set_cursor_from_row,
> right?

Not necessarily.  When I developed the auto-hscroll-mode =
current-line feature, I remember sometimes bumping into cases, when
typing C-n or C-p, that vpos was still negative when entering
display_line, which means it could be negative in set_cursor_from_row.

> And set_cursor_from_row should not touch vpos/y.

I meant w->cursor.vpos, not vpos of the row or the iterator:

  w->cursor.hpos = glyph - row->glyphs[TEXT_AREA];
  w->cursor.x = x;
  w->cursor.vpos = MATRIX_ROW_VPOS (row, matrix) + dvpos;
  w->cursor.y = row->y + dy;

> > Btw, another complication is to do all this testing with
> > display-line-numbers turned on, especially when Emacs decides to
> > enlarge the number of digits in a line number, and all the display
> > of the parent frame needs to scroll horizontally left or right.
> 
> AFAIU, this also requires desired glyphs to be built. So I think this
> should work too.

The code in set_cursor_from_row doesn't currently distinguish between
the current and desired matrices.

> >> > What happens then if you move cursor with C-n and enter a line which
> >> > is partially obscured by a child frame? Or if point becomes hidden
> >> > because it enters the obscure part of the frame from above or below,
> >> > not from left or from right?
> >>
> >> >From a redisplay perspective, I'd say C-n, C-p, C-b, C-p lead all to the
> >> same "PT given, find it in glyph row".
> >
> > Yes, but C-n and C-p change w->cursor.vpos, i.e. set_cursor_from_row
> > needs to decide that the previous row is no longer a cursor row, and
> > find the new row.  C-f and C-b don't have that problem.
> 
> I made "row" const in set_cursor_from_row like so, to be sure
> 
>     static bool
>     set_cursor_from_row (struct window *w, struct glyph_row *const row,
> 
> to make sure that 'row' is indeed not changed in the function. Which means
> the decision about that is made in a caller. And I would expect that is
> done by using the height of the row (which is 1).

That's not what I meant.  'row' is indeed changed only by the caller
of set_cursor_from_row, but the decision whether 'row' is the cursor
row is done by set_cursor_from_row, and it indicates that by setting
the w->cursor fields, as shown in the code fragment above.

> > Here's another one: what happens if the horizontal coordinate where a
> > child frame starts to hide its parent is in the middle of a display
> > string or an overlay string?  Does cursor motion then works correctly?
> 
> That I'm not 100% sure of because I don't grasp set_cursor_from_row
> completely. The glyphs of a display string have the frame set like other
> glyphs have (see produce_glyphs and subroutines). I think we skip over
> glyphs from strings, if I read that correctly, and stop after them.

Right.  But the glyph->frame test in Martin's patch seems to be done
only where we examine glyphs whose OBJECT is a buffer, and thus we can
compare their CHARPOS to point.  The code which follows that examines
glyphs from strings, but there's no glyph->frame test there, AFAICT.
So if we bump into a glyph from the child frame in the middle of
examining glyphs from a string, we could fail to notice that until
later.

> If that's a glyph from a buffer. and glyph->frame != f we're under a
> child, else not. Like I said, I'd bet only a small amount on that,
> perhaps you and Martin could also check?

My question was a suggestion to check this, yes.



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

* Re: "Final" version of tty child frames
  2025-01-17  6:48                                                                             ` Eli Zaretskii
@ 2025-01-17  7:50                                                                               ` Gerd Möllmann
  2025-01-17  8:34                                                                                 ` Eli Zaretskii
  2025-01-17  9:09                                                                               ` martin rudalics
  1 sibling, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-17  7:50 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: rudalics, jared, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> If that's a glyph from a buffer. and glyph->frame != f we're under a
>> child, else not. Like I said, I'd bet only a small amount on that,
>> perhaps you and Martin could also check?
>
> My question was a suggestion to check this, yes.

There might be another problem. Feng Shu, the author of Posframe,
reported a problem with child frames over Chinese text at some point:

  https://yhetil.org/emacs-devel/m25xpji82g.fsf@gmail.com/

In short, this handles the case that a child frame overlaps a
double-width character, in the sense that one half of the displayed
character is outside of the child frame, and the other half is under the
child frame.

Maybe separate matrices would be easier to reason about :-/.




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

* Re: "Final" version of tty child frames
  2025-01-17  7:50                                                                               ` Gerd Möllmann
@ 2025-01-17  8:34                                                                                 ` Eli Zaretskii
  2025-01-17  8:50                                                                                   ` Gerd Möllmann
  0 siblings, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-17  8:34 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: rudalics, jared, emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: rudalics@gmx.at,  jared@finder.org,  emacs-devel@gnu.org
> Date: Fri, 17 Jan 2025 08:50:58 +0100
> 
> There might be another problem. Feng Shu, the author of Posframe,
> reported a problem with child frames over Chinese text at some point:
> 
>   https://yhetil.org/emacs-devel/m25xpji82g.fsf@gmail.com/
> 
> In short, this handles the case that a child frame overlaps a
> double-width character, in the sense that one half of the displayed
> character is outside of the child frame, and the other half is under the
> child frame.

Didn't you already fix that problem in the past, when child frames on
TTYs were first introduced?

What should happen in this case is that we don't show the double-width
character at all.



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

* Re: "Final" version of tty child frames
  2025-01-17  8:34                                                                                 ` Eli Zaretskii
@ 2025-01-17  8:50                                                                                   ` Gerd Möllmann
  2025-01-17 12:14                                                                                     ` Eli Zaretskii
  0 siblings, 1 reply; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-17  8:50 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: rudalics@gmx.at,  jared@finder.org,  emacs-devel@gnu.org
>> Date: Fri, 17 Jan 2025 08:50:58 +0100
>> 
>> There might be another problem. Feng Shu, the author of Posframe,
>> reported a problem with child frames over Chinese text at some point:
>> 
>>   https://yhetil.org/emacs-devel/m25xpji82g.fsf@gmail.com/
>> 
>> In short, this handles the case that a child frame overlaps a
>> double-width character, in the sense that one half of the displayed
>> character is outside of the child frame, and the other half is under the
>> child frame.
>
> Didn't you already fix that problem in the past, when child frames on
> TTYs were first introduced?
>
> What should happen in this case is that we don't show the double-width
> character at all.

Yes it's fixed, and the fix is the problem. It modifies the root's
current matrix outside of the area that a child occupies, by changing
glyphs to padding glyphs.

I've made me a branch now for the terminal matrices. Can take a bit,
though :-/.



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

* Re: "Final" version of tty child frames
  2025-01-16 19:05                                                                     ` Gerd Möllmann
@ 2025-01-17  9:08                                                                       ` martin rudalics
  2025-01-17  9:35                                                                         ` Gerd Möllmann
  2025-01-17 12:29                                                                         ` Eli Zaretskii
  0 siblings, 2 replies; 194+ messages in thread
From: martin rudalics @ 2025-01-17  9:08 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: Eli Zaretskii, jared, emacs-devel

 >> +     - There exists at least one visible child frame whose root is the
 >> +       same frame as the root of W's frame.  Such a frame could overlap
 >> +       the position of point in W in which case we (1) don't want to
 >> +       show the cursor and (2) make sure that if point is not overlapped
 >> +       by that frame, the cursor is shown.  */
 >
 > If one wants, one could sharpen the second point a bit. Glyphs from
 > child frames can only ever appear if
 >
 > - in a current matrix
 >
 >    matrix == w->current_matrix
 >
 > - if w->frame is a root frame (NILP (w->frame->parent_frame)
 >    IOW, the current matrix of child frames will never show this
 >    phenomenon.
 >
 >    struct frame *f = XFRAME (w->frame);
 >    is_tty_root_frame (f);
 >
 > - w is the selected window
 >
 > - that root frame has visible child frames. Something like
 >
 >    Lisp_Object z_order = frames_in_reverse_z_order (f, true);
 >    bool has_visible_child = CONSP (XCDR (z_order);
 >
 > Which might be easier to understand than the below.

I am a bit lost with this.  Just note one aspect which you can test with
my example file: With C-l and M-l you get two child frames overlapping
each other.  Now in the child frame beneath move the cursor in the area
right of the child frame above to the left so that it is hidden and back
again until it is visible.  Here I have two child frames and no root
frame involved.  Does your "sharpened second point" handle that?

 >> +  bool is_tty_selected = false;
 >> +  struct frame *f = WINDOW_XFRAME (w);
 >> +  struct frame *r = root_frame (WINDOW_XFRAME (w));
 >> +
 >> +  Lisp_Object tail, other;
 >> +
 >> +  if (w == XWINDOW (selected_window))
 >> +    FOR_EACH_FRAME (tail, other)
 >> +      {
 >> +	struct frame *o = XFRAME (other);
 >> +
 >> +	if (FRAME_LIVE_P (o) && is_tty_child_frame (o)
 >> +	    && FRAME_VISIBLE_P (o) && root_frame (o) == r)

This root_frame (o) == r check is meant to handle the case of two
overlapping child frames too.

 >> +	  {
 >> +	    is_tty_selected = true;
 >> +	    break;
 >> +	  }
 >> +      }
 >> +

martin



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

* Re: "Final" version of tty child frames
  2025-01-16 19:22                                                                     ` Eli Zaretskii
  2025-01-16 19:32                                                                       ` Gerd Möllmann
@ 2025-01-17  9:08                                                                       ` martin rudalics
  2025-01-17 12:26                                                                         ` Eli Zaretskii
  1 sibling, 1 reply; 194+ messages in thread
From: martin rudalics @ 2025-01-17  9:08 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gerd.moellmann, jared, emacs-devel

 > Did you try this with auto-hscroll-mode, both set to t and to
 > current-line?  What happens then if you move cursor with C-n and enter
 > a line which is partially obscured by a child frame?  Or if point
 > becomes hidden because it enters the obscure part of the frame from
 > above or below, not from left or from right?

I now checked these and see no problems.  I see problems when moving
point in a child frame with truncated lines but this might be a separate
problem.

 >> +  if (is_tty_selected && glyph->charpos > PT)
 >> +    {
 >> +      w->cursor.hpos = -1;
 >> +      w->cursor.x = -1;
 >> +    }
 >
 > Why do you need this test, and specifically the second part of the
 > condition?

It handles the case where the cursor should be hidden because of an
overlapping child frame.  I may run the loop in step 1 until the end of
all glyphs when there is an overlapping child frame.  But if I re-enter
in the combined matrix the part belonging to the underlying frame, I may
be at some glyph far beyond the glyph that would have covered PT because
in the combined glyph matrix some glyph of the child frame is at that
position.  So I have to make the cursor invisible in that case to not
irritate the user (as I did in a first approximation mentioned earlier).

martin




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

* Re: "Final" version of tty child frames
  2025-01-16 19:55                                                                         ` Eli Zaretskii
  2025-01-16 21:03                                                                           ` Gerd Möllmann
@ 2025-01-17  9:09                                                                           ` martin rudalics
  2025-01-17 12:40                                                                             ` Eli Zaretskii
  1 sibling, 1 reply; 194+ messages in thread
From: martin rudalics @ 2025-01-17  9:09 UTC (permalink / raw)
  To: Eli Zaretskii, Gerd Möllmann; +Cc: jared, emacs-devel

 > Btw, another complication is to do all this testing with
 > display-line-numbers turned on, especially when Emacs decides to
 > enlarge the number of digits in a line number, and all the display
 > of the parent frame needs to scroll horizontally left or right.

I tested that and see no problems.

 > Here's another one: what happens if the horizontal coordinate where a
 > child frame starts to hide its parent is in the middle of a display
 > string or an overlay string?  Does cursor motion then works correctly?

I mentioned that in my first post.  Basically, this might be already a
problem when setting up the combined matrices.  Any display property
within the underlying frame will have to be on any glyph shown in the
combined matrix.  Any display property of the covering frame will be
ignored.

This can be compared to a display property in a normal frame hscrolled
to the left so the beginning of the line is not visible.  How is a
display property starting before the first glyph shown on that line
handled?  Then we could do the same for a display property that starts
in the part of the frame covered by a child frame but extends to the
visible part following the child frame.

martin



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

* Re: "Final" version of tty child frames
  2025-01-17  6:48                                                                             ` Eli Zaretskii
  2025-01-17  7:50                                                                               ` Gerd Möllmann
@ 2025-01-17  9:09                                                                               ` martin rudalics
  2025-01-17 12:44                                                                                 ` Eli Zaretskii
  1 sibling, 1 reply; 194+ messages in thread
From: martin rudalics @ 2025-01-17  9:09 UTC (permalink / raw)
  To: Eli Zaretskii, Gerd Möllmann; +Cc: jared, emacs-devel

 > So if we bump into a glyph from the child frame in the middle of
 > examining glyphs from a string, we could fail to notice that until
 > later.

I would have to understand how that happens.  How, in practice, are we
"in the middle of examining glyphs from a string"?

martin



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

* Re: "Final" version of tty child frames
  2025-01-17  9:08                                                                       ` martin rudalics
@ 2025-01-17  9:35                                                                         ` Gerd Möllmann
  2025-01-17 12:29                                                                         ` Eli Zaretskii
  1 sibling, 0 replies; 194+ messages in thread
From: Gerd Möllmann @ 2025-01-17  9:35 UTC (permalink / raw)
  To: martin rudalics; +Cc: Eli Zaretskii, jared, emacs-devel

martin rudalics <rudalics@gmx.at> writes:

>> If one wants, one could sharpen the second point a bit. Glyphs from
>> child frames can only ever appear if
>>
>> - in a current matrix
>>
>>    matrix == w->current_matrix
>>
>> - if w->frame is a root frame (NILP (w->frame->parent_frame)
>>    IOW, the current matrix of child frames will never show this
>>    phenomenon.
>>
>>    struct frame *f = XFRAME (w->frame);
>>    is_tty_root_frame (f);
>>
>> - w is the selected window
>>
>> - that root frame has visible child frames. Something like
>>
>>    Lisp_Object z_order = frames_in_reverse_z_order (f, true);
>>    bool has_visible_child = CONSP (XCDR (z_order);
>>
>> Which might be easier to understand than the below.
>
> I am a bit lost with this. 

Understandable.

> Just note one aspect which you can test with my example file: With C-l
> and M-l you get two child frames overlapping each other. Now in the
> child frame beneath move the cursor in the area right of the child
> frame above to the left so that it is hidden and back again until it
> is visible. Here I have two child frames and no root frame involved.
> Does your "sharpened second point" handle that?

Yes, if I understand you correctly. The combined update works by
producing a _root_ current matrix that has child frames copied to it, so
to speak. Child frame matrices are entirely left alone.

BTW, as I said else-mail, I've made me a branch for my terminal matrices
idea. So, it it's not an urgent problem, maybe we could wait a bit, and
see if my idea flies.



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

* Re: "Final" version of tty child frames
  2025-01-17  8:50                                                                                   ` Gerd Möllmann
@ 2025-01-17 12:14                                                                                     ` Eli Zaretskii
  2025-01-17 13:21                                                                                       ` Eli Zaretskii
  0 siblings, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-17 12:14 UTC (permalink / raw)
  To: Gerd Möllmann; +Cc: rudalics, jared, emacs-devel

> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> Cc: rudalics@gmx.at,  jared@finder.org,  emacs-devel@gnu.org
> Date: Fri, 17 Jan 2025 09:50:40 +0100
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> >> From: Gerd Möllmann <gerd.moellmann@gmail.com>
> >> Cc: rudalics@gmx.at,  jared@finder.org,  emacs-devel@gnu.org
> >> Date: Fri, 17 Jan 2025 08:50:58 +0100
> >> 
> >> There might be another problem. Feng Shu, the author of Posframe,
> >> reported a problem with child frames over Chinese text at some point:
> >> 
> >>   https://yhetil.org/emacs-devel/m25xpji82g.fsf@gmail.com/
> >> 
> >> In short, this handles the case that a child frame overlaps a
> >> double-width character, in the sense that one half of the displayed
> >> character is outside of the child frame, and the other half is under the
> >> child frame.
> >
> > Didn't you already fix that problem in the past, when child frames on
> > TTYs were first introduced?
> >
> > What should happen in this case is that we don't show the double-width
> > character at all.
> 
> Yes it's fixed, and the fix is the problem. It modifies the root's
> current matrix outside of the area that a child occupies, by changing
> glyphs to padding glyphs.

I guess the child's matrix should save and restore mote that it
overwrites?



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

* Re: "Final" version of tty child frames
  2025-01-17  9:08                                                                       ` martin rudalics
@ 2025-01-17 12:26                                                                         ` Eli Zaretskii
  2025-01-17 15:08                                                                           ` martin rudalics
  0 siblings, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-17 12:26 UTC (permalink / raw)
  To: martin rudalics; +Cc: gerd.moellmann, jared, emacs-devel

> Date: Fri, 17 Jan 2025 10:08:35 +0100
> Cc: gerd.moellmann@gmail.com, jared@finder.org, emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
> 
>  > Did you try this with auto-hscroll-mode, both set to t and to
>  > current-line?  What happens then if you move cursor with C-n and enter
>  > a line which is partially obscured by a child frame?  Or if point
>  > becomes hidden because it enters the obscure part of the frame from
>  > above or below, not from left or from right?
> 
> I now checked these and see no problems.  I see problems when moving
> point in a child frame with truncated lines but this might be a separate
> problem.

Let's hope so.

>  >> +  if (is_tty_selected && glyph->charpos > PT)
>  >> +    {
>  >> +      w->cursor.hpos = -1;
>  >> +      w->cursor.x = -1;
>  >> +    }
>  >
>  > Why do you need this test, and specifically the second part of the
>  > condition?
> 
> It handles the case where the cursor should be hidden because of an
> overlapping child frame.  I may run the loop in step 1 until the end of
> all glyphs when there is an overlapping child frame.  But if I re-enter
> in the combined matrix the part belonging to the underlying frame, I may
> be at some glyph far beyond the glyph that would have covered PT because
> in the combined glyph matrix some glyph of the child frame is at that
> position.  So I have to make the cursor invisible in that case to not
> irritate the user (as I did in a first approximation mentioned earlier).

Since is_tty_selected means the window set_cursor_from_row is called
for is the one from the parent frame (right?), any condition that
looks at glyph->charpos without first verifying the glyph is from that
window's frame must be wrong.  Do you agree?

If the cursor should be hidden because of an overlapping child frame,
how come you didn't detect that when you found the first glyph whose
FRAME is not the window's frame?

Anyway, this is wrong for bidirectional display, where a glyph to the
right of another one could have its charpos smaller.



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

* Re: "Final" version of tty child frames
  2025-01-17  9:08                                                                       ` martin rudalics
  2025-01-17  9:35                                                                         ` Gerd Möllmann
@ 2025-01-17 12:29                                                                         ` Eli Zaretskii
  2025-01-17 15:08                                                                           ` martin rudalics
  1 sibling, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-17 12:29 UTC (permalink / raw)
  To: martin rudalics; +Cc: gerd.moellmann, jared, emacs-devel

> Date: Fri, 17 Jan 2025 10:08:12 +0100
> Cc: Eli Zaretskii <eliz@gnu.org>, jared@finder.org, emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
> 
>  >> +     - There exists at least one visible child frame whose root is the
>  >> +       same frame as the root of W's frame.  Such a frame could overlap
>  >> +       the position of point in W in which case we (1) don't want to
>  >> +       show the cursor and (2) make sure that if point is not overlapped
>  >> +       by that frame, the cursor is shown.  */
>  >
>  > If one wants, one could sharpen the second point a bit. Glyphs from
>  > child frames can only ever appear if
>  >
>  > - in a current matrix
>  >
>  >    matrix == w->current_matrix
>  >
>  > - if w->frame is a root frame (NILP (w->frame->parent_frame)
>  >    IOW, the current matrix of child frames will never show this
>  >    phenomenon.
>  >
>  >    struct frame *f = XFRAME (w->frame);
>  >    is_tty_root_frame (f);
>  >
>  > - w is the selected window
>  >
>  > - that root frame has visible child frames. Something like
>  >
>  >    Lisp_Object z_order = frames_in_reverse_z_order (f, true);
>  >    bool has_visible_child = CONSP (XCDR (z_order);
>  >
>  > Which might be easier to understand than the below.
> 
> I am a bit lost with this.

Welcome to the club.

Can you explain why you need the is_tty_selected flag in the first
place?  What problem does it solve that the rest of the code in
set_cursor_from_row cannot?  IOW, why should set_cursor_from_row care
whether the 'w' argument is the selected window or not?



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

* Re: "Final" version of tty child frames
  2025-01-17  9:09                                                                           ` martin rudalics
@ 2025-01-17 12:40                                                                             ` Eli Zaretskii
  2025-01-17 15:09                                                                               ` martin rudalics
  0 siblings, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-17 12:40 UTC (permalink / raw)
  To: martin rudalics; +Cc: gerd.moellmann, jared, emacs-devel

> Date: Fri, 17 Jan 2025 10:09:10 +0100
> Cc: jared@finder.org, emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
> 
>  > Here's another one: what happens if the horizontal coordinate where a
>  > child frame starts to hide its parent is in the middle of a display
>  > string or an overlay string?  Does cursor motion then works correctly?
> 
> I mentioned that in my first post.  Basically, this might be already a
> problem when setting up the combined matrices.  Any display property
> within the underlying frame will have to be on any glyph shown in the
> combined matrix.  Any display property of the covering frame will be
> ignored.

You mean, display properties and overlay strings are not shown on
child frames?  Why not?

> This can be compared to a display property in a normal frame hscrolled
> to the left so the beginning of the line is not visible.  How is a
> display property starting before the first glyph shown on that line
> handled?

It is shown because we iterate from the beginning of the screen line,
even if it is obscured.  During that iteration, we pay attention to
all the display and overlay properties, but do not produce glyphs.
However, the iterator object does get its state updated during that
stage, so when we get to the first glyph whose coordinate is inside
the viewport, and start producing glyphs, we are ready to display as
the user expects.  It's this part at the beginning of display_line
(which is the workhorse of the display engine for layout of screen
lines):

  /* Move over display elements that are not visible because we are
     hscrolled.  This may stop at an x-position < first_visible_x
     if the first glyph is partially visible or if we hit a line end.  */
  if (it->current_x < it->first_visible_x + x_incr)
    {
      enum move_it_result move_result;

      this_line_min_pos = row->start.pos;
      if (hscroll_this_line)
	{
	  it->first_visible_x += x_incr;
	  it->last_visible_x  += x_incr;
	}
      if (current_buffer->long_line_optimizations_p
	  && it->line_wrap == TRUNCATE
	  && window_hscroll_limited (it->w, it->f) > large_hscroll_threshold)
	{
	  /* Special optimization for very long and truncated lines
	     which are hscrolled far to the left: jump directly to the
	     (approximate) position that is visible, instead of slowly
	     walking there.  */
	  [...let's ignore this for the moment...]
	}
      else
	move_result = move_it_in_display_line_to (it, ZV, it->first_visible_x,
						  MOVE_TO_POS | MOVE_TO_X);

The call to move_it_in_display_line_to is the one which does what I
described above.

> Then we could do the same for a display property that starts
> in the part of the frame covered by a child frame but extends to the
> visible part following the child frame.

We could.  But the problem here is that the above code runs waaay
before we combine glyph matrices, and doesn't know anything about the
special case of TTY child frames.

And frankly, I don't understand the reason for the problem: by the
time we combine frame matrices, the glyphs produced from the display
property are already in the matrix, so it sounds like all we need is
to leave those glyphs alone where the child matrix doesn't conceal the
parent?  Or what am I missing?



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

* Re: "Final" version of tty child frames
  2025-01-17  9:09                                                                               ` martin rudalics
@ 2025-01-17 12:44                                                                                 ` Eli Zaretskii
  2025-01-17 15:09                                                                                   ` martin rudalics
  0 siblings, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-17 12:44 UTC (permalink / raw)
  To: martin rudalics; +Cc: gerd.moellmann, jared, emacs-devel

> Date: Fri, 17 Jan 2025 10:09:21 +0100
> Cc: jared@finder.org, emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
> 
>  > So if we bump into a glyph from the child frame in the middle of
>  > examining glyphs from a string, we could fail to notice that until
>  > later.
> 
> I would have to understand how that happens.  How, in practice, are we
> "in the middle of examining glyphs from a string"?

Imagine a line that has a 'display' property whose value is a string
in the middle of the line.  Then that line on display will look like
this:

   BBBBBBBBBBBBBSSSSSSSSSBBBBBBBBB

where the B's are glyphs produced from buffer text, and S's are glyphs
produced from the display string.  Now arrange for a child frame to be
displayed such that its left border crosses the above line somewhere
in the middle of the S's.  That's the situation I was talking about.



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

* Re: "Final" version of tty child frames
  2025-01-17 12:14                                                                                     ` Eli Zaretskii
@ 2025-01-17 13:21                                                                                       ` Eli Zaretskii
  2025-01-17 15:14                                                                                         ` Gerd Möllmann
  0 siblings, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-17 13:21 UTC (permalink / raw)
  To: gerd.moellmann; +Cc: rudalics, jared, emacs-devel

> Date: Fri, 17 Jan 2025 14:14:15 +0200
> From: Eli Zaretskii <eliz@gnu.org>
> Cc: rudalics@gmx.at, jared@finder.org, emacs-devel@gnu.org
> 
> > Yes it's fixed, and the fix is the problem. It modifies the root's
> > current matrix outside of the area that a child occupies, by changing
> > glyphs to padding glyphs.
> 
> I guess the child's matrix should save and restore mote that it
> overwrites?                                        ^^^^^^^^^

"more than"

Sorry.



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

* Re: "Final" version of tty child frames
  2025-01-17 12:26                                                                         ` Eli Zaretskii
@ 2025-01-17 15:08                                                                           ` martin rudalics
  2025-01-17 19:56                                                                             ` Eli Zaretskii
  0 siblings, 1 reply; 194+ messages in thread
From: martin rudalics @ 2025-01-17 15:08 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gerd.moellmann, jared, emacs-devel

 > Since is_tty_selected means the window set_cursor_from_row is called
 > for is the one from the parent frame (right?), any condition that
 > looks at glyph->charpos without first verifying the glyph is from that
 > window's frame must be wrong.  Do you agree?

I do.

 > If the cursor should be hidden because of an overlapping child frame,
 > how come you didn't detect that when you found the first glyph whose
 > FRAME is not the window's frame?

I do detect that here

	if (is_tty_selected && f != glyph->frame)

The original code didn't.

 > Anyway, this is wrong for bidirectional display, where a glyph to the
 > right of another one could have its charpos smaller.

This must be handled in row->reversed_p.

martin



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

* Re: "Final" version of tty child frames
  2025-01-17 12:29                                                                         ` Eli Zaretskii
@ 2025-01-17 15:08                                                                           ` martin rudalics
  2025-01-17 19:58                                                                             ` Eli Zaretskii
  0 siblings, 1 reply; 194+ messages in thread
From: martin rudalics @ 2025-01-17 15:08 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gerd.moellmann, jared, emacs-devel

 > Can you explain why you need the is_tty_selected flag in the first
 > place?  What problem does it solve that the rest of the code in
 > set_cursor_from_row cannot?

It does not solve any problem.  It's there to avoid compromising the
rest of the code.

 > IOW, why should set_cursor_from_row care
 > whether the 'w' argument is the selected window or not?

Because nowhere else the tty cursor can appear.

martin



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

* Re: "Final" version of tty child frames
  2025-01-17 12:40                                                                             ` Eli Zaretskii
@ 2025-01-17 15:09                                                                               ` martin rudalics
  2025-01-17 20:02                                                                                 ` Eli Zaretskii
  0 siblings, 1 reply; 194+ messages in thread
From: martin rudalics @ 2025-01-17 15:09 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gerd.moellmann, jared, emacs-devel

 > You mean, display properties and overlay strings are not shown on
 > child frames?  Why not?

They can be shown anywhere.  But they cannot be always handled correctly
by the cursor placement code whenever they cross a coordinate where the
matrix was recombined.

 > It is shown because we iterate from the beginning of the screen line,
 > even if it is obscured.  During that iteration, we pay attention to
 > all the display and overlay properties, but do not produce glyphs.

That's the iterator code.  But here we talk about a cursor producing
code which starts, if I'm not totally mistaken, at the first glyph of a
row.

 > We could.  But the problem here is that the above code runs waaay
 > before we combine glyph matrices, and doesn't know anything about the
 > special case of TTY child frames.

Agreed.  Then the problem must be solved in the iterator code _before_
glyphs are produced.

 > And frankly, I don't understand the reason for the problem: by the
 > time we combine frame matrices, the glyphs produced from the display
 > property are already in the matrix, so it sounds like all we need is
 > to leave those glyphs alone where the child matrix doesn't conceal the
 > parent?  Or what am I missing?

The problem is a very tiny one: When setting the cursor from the row we
should be able to set it in the visible part right of a child frame (with
RL text left of it) that obscures the basic frame.

martin



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

* Re: "Final" version of tty child frames
  2025-01-17 12:44                                                                                 ` Eli Zaretskii
@ 2025-01-17 15:09                                                                                   ` martin rudalics
  2025-01-17 16:51                                                                                     ` Eli Zaretskii
  0 siblings, 1 reply; 194+ messages in thread
From: martin rudalics @ 2025-01-17 15:09 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gerd.moellmann, jared, emacs-devel

 >> I would have to understand how that happens.  How, in practice, are we
 >> "in the middle of examining glyphs from a string"?
 >
 > Imagine a line that has a 'display' property whose value is a string
 > in the middle of the line.  Then that line on display will look like
 > this:
 >
 >     BBBBBBBBBBBBBSSSSSSSSSBBBBBBBBB
 >
 > where the B's are glyphs produced from buffer text, and S's are glyphs
 > produced from the display string.  Now arrange for a child frame to be
 > displayed such that its left border crosses the above line somewhere
 > in the middle of the S's.  That's the situation I was talking about.

When set_cursor_from_row is on an S glyph, what is the glyph's object?

martin



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

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

Eli Zaretskii <eliz@gnu.org> writes:

>> Date: Fri, 17 Jan 2025 14:14:15 +0200
>> From: Eli Zaretskii <eliz@gnu.org>
>> Cc: rudalics@gmx.at, jared@finder.org, emacs-devel@gnu.org
>> 
>> > Yes it's fixed, and the fix is the problem. It modifies the root's
>> > current matrix outside of the area that a child occupies, by changing
>> > glyphs to padding glyphs.
>> 
>> I guess the child's matrix should save and restore mote that it
>> overwrites?                                        ^^^^^^^^^
>
> "more than"

Sorry, that's not the problem. Wide characters and child frames work
just fine currently, although in a different way than you seem to think.
Whatever.

What I am saying is that set_cursor_from_row must be aware that a root's
current matrix may contain glyphs that have glyph->frame == f, where f
is the root frame, and which are padding glyphs, but really were part of a
wide character.

I propose that we let this thread rest until I've made progress on my
branch with separate matrices.



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

* Re: "Final" version of tty child frames
  2025-01-17 15:09                                                                                   ` martin rudalics
@ 2025-01-17 16:51                                                                                     ` Eli Zaretskii
  2025-01-17 18:25                                                                                       ` martin rudalics
  0 siblings, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-17 16:51 UTC (permalink / raw)
  To: martin rudalics; +Cc: gerd.moellmann, jared, emacs-devel

> Date: Fri, 17 Jan 2025 16:09:37 +0100
> Cc: gerd.moellmann@gmail.com, jared@finder.org, emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
> 
>  >> I would have to understand how that happens.  How, in practice, are we
>  >> "in the middle of examining glyphs from a string"?
>  >
>  > Imagine a line that has a 'display' property whose value is a string
>  > in the middle of the line.  Then that line on display will look like
>  > this:
>  >
>  >     BBBBBBBBBBBBBSSSSSSSSSBBBBBBBBB
>  >
>  > where the B's are glyphs produced from buffer text, and S's are glyphs
>  > produced from the display string.  Now arrange for a child frame to be
>  > displayed such that its left border crosses the above line somewhere
>  > in the middle of the S's.  That's the situation I was talking about.
> 
> When set_cursor_from_row is on an S glyph, what is the glyph's object?

The Lisp string from which the glyph came.



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

* Re: "Final" version of tty child frames
  2025-01-17 16:51                                                                                     ` Eli Zaretskii
@ 2025-01-17 18:25                                                                                       ` martin rudalics
  0 siblings, 0 replies; 194+ messages in thread
From: martin rudalics @ 2025-01-17 18:25 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gerd.moellmann, jared, emacs-devel

 >>   > Imagine a line that has a 'display' property whose value is a string
 >>   > in the middle of the line.  Then that line on display will look like
 >>   > this:
 >>   >
 >>   >     BBBBBBBBBBBBBSSSSSSSSSBBBBBBBBB
 >>   >
 >>   > where the B's are glyphs produced from buffer text, and S's are glyphs
 >>   > produced from the display string.  Now arrange for a child frame to be
 >>   > displayed such that its left border crosses the above line somewhere
 >>   > in the middle of the S's.  That's the situation I was talking about.
 >>
 >> When set_cursor_from_row is on an S glyph, what is the glyph's object?
 >
 > The Lisp string from which the glyph came.

If every string glyph's frame has been set up correctly I see no
particular problem.  For a visible string glyph of the underlying frame
on either side of the child frame we would do the same as we do if the
child frame didn't exist at all.  An invisible string glyph would be
skipped.  Maybe step 2 would need some fixing as well but in the worst
case this would move the cursor to the end of the line where it is now.

More annoying is that we should examine the glyph_before and glyph_after
assignments because the involved glyphs might be part of different
frames.  But let's see what Gerd accomplishes.

martin



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

* Re: "Final" version of tty child frames
  2025-01-17 15:08                                                                           ` martin rudalics
@ 2025-01-17 19:56                                                                             ` Eli Zaretskii
  2025-01-18  9:03                                                                               ` martin rudalics
  0 siblings, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-17 19:56 UTC (permalink / raw)
  To: martin rudalics; +Cc: gerd.moellmann, jared, emacs-devel

> Date: Fri, 17 Jan 2025 16:08:04 +0100
> Cc: gerd.moellmann@gmail.com, jared@finder.org, emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
> 
>  > Anyway, this is wrong for bidirectional display, where a glyph to the
>  > right of another one could have its charpos smaller.
> 
> This must be handled in row->reversed_p.

No, these are two different aspects of the bidirectional display.
row->reversed_p means that the whole glyph row was reversed for
display, so it starts at the right window edge and proceeds to the
left.  What I'm talking about is reordering of characters for display
even if row->reversed_p is zero.  This reordering could cause the
glyphs corresponding to 10 characters in buffer positions 1 to 10 to
be in the following order on display:

  1 2 8 7 5 6 4 3 9 10

Which means you cannot assume any relation between the right/left
direction on display and increasing/decreasing order of buffer
positions.



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

* Re: "Final" version of tty child frames
  2025-01-17 15:08                                                                           ` martin rudalics
@ 2025-01-17 19:58                                                                             ` Eli Zaretskii
  2025-01-18  9:04                                                                               ` martin rudalics
  0 siblings, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-17 19:58 UTC (permalink / raw)
  To: martin rudalics; +Cc: gerd.moellmann, jared, emacs-devel

> Date: Fri, 17 Jan 2025 16:08:29 +0100
> Cc: gerd.moellmann@gmail.com, jared@finder.org, emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
> 
>  > Can you explain why you need the is_tty_selected flag in the first
>  > place?  What problem does it solve that the rest of the code in
>  > set_cursor_from_row cannot?
> 
> It does not solve any problem.  It's there to avoid compromising the
> rest of the code.
> 
>  > IOW, why should set_cursor_from_row care
>  > whether the 'w' argument is the selected window or not?
> 
> Because nowhere else the tty cursor can appear.

But the same code works today on TTY frames for non-selected windows.
Emacs knows to turn on the cursor only in the selected window.  So why
should child frames change that?



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

* Re: "Final" version of tty child frames
  2025-01-17 15:09                                                                               ` martin rudalics
@ 2025-01-17 20:02                                                                                 ` Eli Zaretskii
  2025-01-18  9:04                                                                                   ` martin rudalics
  0 siblings, 1 reply; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-17 20:02 UTC (permalink / raw)
  To: martin rudalics; +Cc: gerd.moellmann, jared, emacs-devel

> Date: Fri, 17 Jan 2025 16:09:22 +0100
> Cc: gerd.moellmann@gmail.com, jared@finder.org, emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
> 
>  > It is shown because we iterate from the beginning of the screen line,
>  > even if it is obscured.  During that iteration, we pay attention to
>  > all the display and overlay properties, but do not produce glyphs.
> 
> That's the iterator code.  But here we talk about a cursor producing
> code which starts, if I'm not totally mistaken, at the first glyph of a
> row.

The code in set_cursor_from_row only examines the glyphs, yes.  The
part hidden by hscrolling doesn't produce any glyphs.

>  > We could.  But the problem here is that the above code runs waaay
>  > before we combine glyph matrices, and doesn't know anything about the
>  > special case of TTY child frames.
> 
> Agreed.  Then the problem must be solved in the iterator code _before_
> glyphs are produced.

What problem is that?

>  > And frankly, I don't understand the reason for the problem: by the
>  > time we combine frame matrices, the glyphs produced from the display
>  > property are already in the matrix, so it sounds like all we need is
>  > to leave those glyphs alone where the child matrix doesn't conceal the
>  > parent?  Or what am I missing?
> 
> The problem is a very tiny one: When setting the cursor from the row we
> should be able to set it in the visible part right of a child frame (with
> RL text left of it) that obscures the basic frame.

And why is that a problem?  (It's an honest question: I don't
understand what prevents you from finding the correct cursor position
in this case.  I guess some details of the problem were not described
yet, at least not in terms that I could understand it.)



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

* Re: "Final" version of tty child frames
  2025-01-17 19:56                                                                             ` Eli Zaretskii
@ 2025-01-18  9:03                                                                               ` martin rudalics
  0 siblings, 0 replies; 194+ messages in thread
From: martin rudalics @ 2025-01-18  9:03 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gerd.moellmann, jared, emacs-devel

 > What I'm talking about is reordering of characters for display
 > even if row->reversed_p is zero.  This reordering could cause the
 > glyphs corresponding to 10 characters in buffer positions 1 to 10 to
 > be in the following order on display:
 >
 >    1 2 8 7 5 6 4 3 9 10
 >
 > Which means you cannot assume any relation between the right/left
 > direction on display and increasing/decreasing order of buffer
 > positions.

So if some of these glyphs are obscured by a child frame, the standard
mechanism for determining the cursor position will be broken.  One would
have to see how code that does not use set_cursor_from_row handles that.
Whatever it is, I doubt that placing the cursor on some arbitrary
position here could confuse a user more than putting the cursor at the
end of the line.

martin




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

* Re: "Final" version of tty child frames
  2025-01-17 19:58                                                                             ` Eli Zaretskii
@ 2025-01-18  9:04                                                                               ` martin rudalics
  2025-01-18 11:40                                                                                 ` Eli Zaretskii
  0 siblings, 1 reply; 194+ messages in thread
From: martin rudalics @ 2025-01-18  9:04 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gerd.moellmann, jared, emacs-devel

 >> Because nowhere else the tty cursor can appear.
 >
 > But the same code works today on TTY frames for non-selected windows.
 > Emacs knows to turn on the cursor only in the selected window.  So why
 > should child frames change that?

Because as before I would not want to to compromise the normal way of
handling things more than absolutely necessary.  What's the purpose of
running set_cursor_from_row in a non-selected window of a tty frame
anyway?

martin



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

* Re: "Final" version of tty child frames
  2025-01-17 20:02                                                                                 ` Eli Zaretskii
@ 2025-01-18  9:04                                                                                   ` martin rudalics
  2025-01-18 11:53                                                                                     ` Eli Zaretskii
  0 siblings, 1 reply; 194+ messages in thread
From: martin rudalics @ 2025-01-18  9:04 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gerd.moellmann, jared, emacs-devel

 > The code in set_cursor_from_row only examines the glyphs, yes.  The
 > part hidden by hscrolling doesn't produce any glyphs.

So how does set_cursor_from_row handle the case where

    BBBBBBBBBBBBBSSSSSSSSSBBBBBBBBB

is hscrolled somewhere into the middle of the "S"?  That situation is
the same as if a child frame ended in the middle of these "S".

 >>   > We could.  But the problem here is that the above code runs waaay
 >>   > before we combine glyph matrices, and doesn't know anything about the
 >>   > special case of TTY child frames.
 >>
 >> Agreed.  Then the problem must be solved in the iterator code _before_
 >> glyphs are produced.
 >
 > What problem is that?

The problem that set_cursor_from_row doesn't know anything about what
caused the iterator code to produce glyphs in a specific way or order
and consequently where to place the cursor within these glyphs.

 >> The problem is a very tiny one: When setting the cursor from the row we
 >> should be able to set it in the visible part right of a child frame (with
 >> RL text left of it) that obscures the basic frame.
 >
 > And why is that a problem?  (It's an honest question: I don't
 > understand what prevents you from finding the correct cursor position
 > in this case.  I guess some details of the problem were not described
 > yet, at least not in terms that I could understand it.)

Have you tried the simple example file I attached previously with the
recipe I proposed?  Then you should see the problem.  When moving via
C-f in a normal frame that is partially hidden by a child frame, the
cursor in the normal frame cannot appear at any position on the right of
the child frame but on the last character of the row.  And this happens
because set_cursor_from_row has no knowledge of child frames and chokes
at the first glyph with a nil object.

martin



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

* Re: "Final" version of tty child frames
  2025-01-18  9:04                                                                               ` martin rudalics
@ 2025-01-18 11:40                                                                                 ` Eli Zaretskii
  0 siblings, 0 replies; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-18 11:40 UTC (permalink / raw)
  To: martin rudalics; +Cc: gerd.moellmann, jared, emacs-devel

> Date: Sat, 18 Jan 2025 10:04:02 +0100
> Cc: gerd.moellmann@gmail.com, jared@finder.org, emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
> 
>  >> Because nowhere else the tty cursor can appear.
>  >
>  > But the same code works today on TTY frames for non-selected windows.
>  > Emacs knows to turn on the cursor only in the selected window.  So why
>  > should child frames change that?
> 
> Because as before I would not want to to compromise the normal way of
> handling things more than absolutely necessary.  What's the purpose of
> running set_cursor_from_row in a non-selected window of a tty frame
> anyway?

ISTR that this function is also part of deciding whether point is
visible in a window in some cases; if point is not visible, Emacs will
scroll the window, and that is relevant to non-selected windows as
well.



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

* Re: "Final" version of tty child frames
  2025-01-18  9:04                                                                                   ` martin rudalics
@ 2025-01-18 11:53                                                                                     ` Eli Zaretskii
  0 siblings, 0 replies; 194+ messages in thread
From: Eli Zaretskii @ 2025-01-18 11:53 UTC (permalink / raw)
  To: martin rudalics; +Cc: gerd.moellmann, jared, emacs-devel

> Date: Sat, 18 Jan 2025 10:04:46 +0100
> Cc: gerd.moellmann@gmail.com, jared@finder.org, emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
> 
>  > The code in set_cursor_from_row only examines the glyphs, yes.  The
>  > part hidden by hscrolling doesn't produce any glyphs.
> 
> So how does set_cursor_from_row handle the case where
> 
>     BBBBBBBBBBBBBSSSSSSSSSBBBBBBBBB
> 
> is hscrolled somewhere into the middle of the "S"?  That situation is
> the same as if a child frame ended in the middle of these "S".

Why does it matter whether the line is hscrolled or not?  The object
of each glyph is available as glyph->object, and you will see that
set_cursor_from_row processes the case of the object being a buffer or
a string in different ways.  In the case you describe, the first glyph
the function sees will have its object a Lisp string, that's all.
That's not different from a case where there's no hscroll, but a line
begins with a display string at its very beginning.

>  >>   > We could.  But the problem here is that the above code runs waaay
>  >>   > before we combine glyph matrices, and doesn't know anything about the
>  >>   > special case of TTY child frames.
>  >>
>  >> Agreed.  Then the problem must be solved in the iterator code _before_
>  >> glyphs are produced.
>  >
>  > What problem is that?
> 
> The problem that set_cursor_from_row doesn't know anything about what
> caused the iterator code to produce glyphs in a specific way or order
> and consequently where to place the cursor within these glyphs.

set_cursor_from_row works with what it has: the object recorded by
each glyph and the position in that object.  If there's a gap in
buffer positions recorded in the glyphs, the code will decide to put
the cursor at the first glyph whose buffer position is the closest to
point.  That could be either the one at the right edge of the child
frame or at its left edge (I think the row's reversed_p flag
determines that).

>  >> The problem is a very tiny one: When setting the cursor from the row we
>  >> should be able to set it in the visible part right of a child frame (with
>  >> RL text left of it) that obscures the basic frame.
>  >
>  > And why is that a problem?  (It's an honest question: I don't
>  > understand what prevents you from finding the correct cursor position
>  > in this case.  I guess some details of the problem were not described
>  > yet, at least not in terms that I could understand it.)
> 
> Have you tried the simple example file I attached previously with the
> recipe I proposed?  Then you should see the problem.

Sorry, I don't have time for this right now.

> When moving via
> C-f in a normal frame that is partially hidden by a child frame, the
> cursor in the normal frame cannot appear at any position on the right of
> the child frame but on the last character of the row.  And this happens
> because set_cursor_from_row has no knowledge of child frames and chokes
> at the first glyph with a nil object.

If the glyph with a nil object belongs to a different frame, the code
should not stop there (I presume by "choke" you mean that the code
exits the loop), but instead try to find another glyph whose object is
either a buffer or a string and whose frame is the window's frame.



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

end of thread, other threads:[~2025-01-18 11:53 UTC | newest]

Thread overview: 194+ 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
2025-01-04 22:12                       ` Jared Finder
2025-01-05  4:03                         ` Gerd Möllmann
2025-01-05  7:07                         ` Eli Zaretskii
2025-01-06  0:05                           ` Jared Finder
2025-01-06  4:27                             ` Gerd Möllmann
2025-01-06 13:30                             ` Eli Zaretskii
2025-01-07  5:40                               ` Jared Finder
2025-01-07  7:36                                 ` 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
2025-01-04 22:09             ` Jared Finder
2025-01-05  3:48               ` Gerd Möllmann
2025-01-13  6:32                 ` Jared Finder
2025-01-13  7:13                   ` Gerd Möllmann
2025-01-13 13:13                   ` Eli Zaretskii
2025-01-14  9:11                   ` martin rudalics
2025-01-14  9:37                     ` Gerd Möllmann
2025-01-14  9:55                       ` martin rudalics
2025-01-14 10:12                         ` Gerd Möllmann
2025-01-14 13:58                         ` Eli Zaretskii
2025-01-14 17:33                           ` martin rudalics
2025-01-14 19:25                             ` Gerd Möllmann
2025-01-15  4:29                               ` Gerd Möllmann
2025-01-15  8:41                                 ` martin rudalics
2025-01-15  8:58                                   ` Gerd Möllmann
2025-01-15 15:29                                     ` Eli Zaretskii
2025-01-15 15:38                                       ` Gerd Möllmann
2025-01-15 15:27                                   ` Eli Zaretskii
2025-01-15 17:01                                     ` martin rudalics
2025-01-15 17:29                                       ` Gerd Möllmann
2025-01-15 17:35                                         ` Gerd Möllmann
2025-01-15 19:43                                       ` Eli Zaretskii
2025-01-16  4:35                                         ` Gerd Möllmann
2025-01-16  8:16                                           ` Eli Zaretskii
2025-01-16  8:42                                             ` Gerd Möllmann
2025-01-16  9:47                                               ` Eli Zaretskii
2025-01-16 10:22                                                 ` Gerd Möllmann
2025-01-16 11:24                                                   ` Eli Zaretskii
2025-01-16 10:02                                               ` martin rudalics
2025-01-16 10:23                                                 ` Gerd Möllmann
2025-01-16 10:50                                                   ` martin rudalics
2025-01-16 10:55                                                     ` Gerd Möllmann
2025-01-16 11:42                                                     ` Eli Zaretskii
2025-01-16 12:15                                                       ` Gerd Möllmann
2025-01-16 12:50                                                         ` Eli Zaretskii
2025-01-16 13:41                                                           ` Gerd Möllmann
2025-01-16 15:22                                                             ` Eli Zaretskii
2025-01-16 15:46                                                               ` Gerd Möllmann
2025-01-16 16:50                                                                 ` Eli Zaretskii
2025-01-16 18:00                                                                   ` martin rudalics
2025-01-16 19:05                                                                     ` Gerd Möllmann
2025-01-17  9:08                                                                       ` martin rudalics
2025-01-17  9:35                                                                         ` Gerd Möllmann
2025-01-17 12:29                                                                         ` Eli Zaretskii
2025-01-17 15:08                                                                           ` martin rudalics
2025-01-17 19:58                                                                             ` Eli Zaretskii
2025-01-18  9:04                                                                               ` martin rudalics
2025-01-18 11:40                                                                                 ` Eli Zaretskii
2025-01-16 19:22                                                                     ` Eli Zaretskii
2025-01-16 19:32                                                                       ` Gerd Möllmann
2025-01-16 19:55                                                                         ` Eli Zaretskii
2025-01-16 21:03                                                                           ` Gerd Möllmann
2025-01-17  6:48                                                                             ` Eli Zaretskii
2025-01-17  7:50                                                                               ` Gerd Möllmann
2025-01-17  8:34                                                                                 ` Eli Zaretskii
2025-01-17  8:50                                                                                   ` Gerd Möllmann
2025-01-17 12:14                                                                                     ` Eli Zaretskii
2025-01-17 13:21                                                                                       ` Eli Zaretskii
2025-01-17 15:14                                                                                         ` Gerd Möllmann
2025-01-17  9:09                                                                               ` martin rudalics
2025-01-17 12:44                                                                                 ` Eli Zaretskii
2025-01-17 15:09                                                                                   ` martin rudalics
2025-01-17 16:51                                                                                     ` Eli Zaretskii
2025-01-17 18:25                                                                                       ` martin rudalics
2025-01-17  9:09                                                                           ` martin rudalics
2025-01-17 12:40                                                                             ` Eli Zaretskii
2025-01-17 15:09                                                                               ` martin rudalics
2025-01-17 20:02                                                                                 ` Eli Zaretskii
2025-01-18  9:04                                                                                   ` martin rudalics
2025-01-18 11:53                                                                                     ` Eli Zaretskii
2025-01-17  9:08                                                                       ` martin rudalics
2025-01-17 12:26                                                                         ` Eli Zaretskii
2025-01-17 15:08                                                                           ` martin rudalics
2025-01-17 19:56                                                                             ` Eli Zaretskii
2025-01-18  9:03                                                                               ` martin rudalics
2025-01-16 11:19                                                 ` Eli Zaretskii
2025-01-14 20:29                             ` Eli Zaretskii
2025-01-14 13:38                       ` Eli Zaretskii
2025-01-05  6:46               ` Eli Zaretskii
2025-01-06  0:18                 ` Jared Finder
2025-01-06 13:35                   ` Eli Zaretskii
2025-01-08  9:56               ` martin rudalics
2025-01-08 10:29                 ` Gerd Möllmann
2025-01-09 10:24                   ` martin rudalics
2025-01-09 10:45                     ` Gerd Möllmann
2025-01-11 10:13                       ` martin rudalics
2025-01-11 12:52                         ` Gerd Möllmann
2025-01-11 17:23                           ` martin rudalics
2025-01-11 17:57                             ` Gerd Möllmann
2025-01-12  5:26                               ` Gerd Möllmann
2025-01-12  6:49                                 ` Gerd Möllmann
2025-01-12  7:36                                   ` Gerd Möllmann
2025-01-12  8:14                                     ` Eli Zaretskii
2025-01-12  8:27                                       ` martin rudalics
2025-01-12  8:59                                         ` Gerd Möllmann
2025-01-12  8:57                                       ` Gerd Möllmann
2025-01-12  8:22                                     ` martin rudalics
2025-01-12  9:03                                       ` Gerd Möllmann
2025-01-12  9:45                                         ` martin rudalics
2025-01-12 10:31                                           ` Gerd Möllmann
2025-01-14  9:11                                             ` martin rudalics
2025-01-14  9:25                                               ` Gerd Möllmann
2025-01-14  9:10                                         ` martin rudalics
2025-01-14  9:24                                           ` Gerd Möllmann
2025-01-11 10:51                       ` Eli Zaretskii
2025-01-11 15:55                         ` 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 external index

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

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.