]> git.eshelyaron.com Git - emacs.git/commitdiff
Handle removal of selected tty child frame
authorMartin Rudalics <rudalics@gmx.at>
Tue, 14 Jan 2025 08:51:17 +0000 (09:51 +0100)
committerEshel Yaron <me@eshelyaron.com>
Tue, 14 Jan 2025 16:23:59 +0000 (17:23 +0100)
* src/dispextern.h (root_frame):
* src/frame.h (root_frame): Move declaration from dispextern.h
to frame.h.
(SET_FRAME_VISIBLE): Whend making the selected tty child frame
invisible, use mru_rooted_frame to find a frame to switch to.
* src/dispnew.c (root_frame): Move root_frame to frame.c.
* src/frame.c (do_switch_frame): On ttys don't change the
top frame when switching from a child frame to another frame
with the same root.
(root_frame): Move here from dispnew.c.
(Fframe_root_frame): New Lisp function.
(delete_frame): Whend deleting the selected tty child frame use,
mru_rooted_frame to find a frame to switch to.
* src/window.c (mru_rooted_frame): New function.
* src/window.h (mru_rooted_frame): Declare it.
* doc/lispref/frames.texi (Child Frames): Describe new function
'frame-root-frame'.

(cherry picked from commit d4aeb6bd230c42cf7b773ec9aebd80ad6d928d98)

doc/lispref/frames.texi
src/dispextern.h
src/dispnew.c
src/frame.c
src/frame.h
src/window.c
src/window.h

index 158e255ef0665f29697213c51d56426c1ed7b3f4..a0d0e489ad08835ab76cce309a53f8f99f7aa0b3 100644 (file)
@@ -3579,7 +3579,7 @@ work on all window-systems.  Some will drop the object on the parent
 frame or on some ancestor instead.
 @end itemize
 
-  The following two functions can be useful when working with child and
+  The following three functions can be useful when working with child and
 parent frames:
 
 @defun frame-parent &optional frame
@@ -3591,6 +3591,7 @@ exists, @var{frame} is considered a child frame of that frame.
 This function returns @code{nil} if @var{frame} has no parent frame.
 @end defun
 
+@cindex ancestor frame
 @defun frame-ancestor-p ancestor descendant
 This functions returns non-@code{nil} if @var{ancestor} is an ancestor
 of @var{descendant}.  @var{ancestor} is an ancestor of @var{descendant}
@@ -3599,6 +3600,16 @@ of @var{descendant}'s parent frame.  Both, @var{ancestor} and
 @var{descendant} must specify live frames.
 @end defun
 
+@cindex root frame
+@defun frame-root-frame &optional frame
+This function returns the root frame of the specified @var{frame}.
+@var{frame} must be a live frame and defaults to the selected one.  The
+root frame of @var{frame} is the frame obtained by following the chain
+of parent frames starting with @var{frame} until a frame is reached that
+has no parent.  If @var{frame} has no parent, its root frame is
+@var{frame} itself.
+@end defun
+
 Note also the function @code{window-largest-empty-rectangle}
 (@pxref{Coordinates and Windows}) which can be used to inscribe a child
 frame in the largest empty area of an existing window.  This can be
index e3e621e7318394b6f66a46615e2cb78fea9493de..1d90022a9bc5c445b65f0544234de4aeedcef0d7 100644 (file)
@@ -3949,7 +3949,6 @@ extern void gui_redo_mouse_highlight (Display_Info *);
 
 #endif /* HAVE_WINDOW_SYSTEM */
 
-struct frame *root_frame (struct frame *f);
 Lisp_Object frames_in_reverse_z_order (struct frame *f, bool visible);
 bool is_tty_frame (struct frame *f);
 bool is_tty_child_frame (struct frame *f);
index 66b5edae6dbb4e8f2ff43045794c44a8381dc373..c3c8b1123b7ef0c9c032abff48ccd04750504903 100644 (file)
@@ -3331,18 +3331,6 @@ frame_rect_abs (struct frame *f)
 
 #endif /* !HAVE_ANDROID */
 
-/* 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)
 {
index 3cfc9d0bf552df7af279aad4c748958df76f59f7..b2d6877e636d25bd7e9b329c8c77fa1282a8dc8b 100644 (file)
@@ -1775,7 +1775,7 @@ do_switch_frame (Lisp_Object frame, int track, int for_deletion, Lisp_Object nor
 
       /* Don't mark the frame garbaged if we are switching to the frame
         that is already the top frame of that TTY.  */
-      if (!EQ (frame, top_frame))
+      if (!EQ (frame, top_frame) && root_frame (f) != XFRAME (top_frame))
        {
          struct frame *new_root = root_frame (f);
          SET_FRAME_VISIBLE (new_root, true);
@@ -2021,6 +2021,39 @@ frame.  */)
   return frame_ancestor_p (af, df) ? Qt : Qnil;
 }
 
+
+/* 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;
+}
+
+
+DEFUN ("frame-root-frame", Fframe_root_frame, Sframe_root_frame,
+       0, 1, 0,
+       doc: /* Return root frame of specified FRAME.
+FRAME must be a live frame and defaults to the selected one.  The root
+frame of FRAME is the frame obtained by following the chain of parent
+frames starting with FRAME until a frame is reached that has no parent.
+If FRAME has no parent, its root frame is FRAME.  */)
+     (Lisp_Object frame)
+{
+  struct frame *f = decode_live_frame (frame);
+  struct frame *r = root_frame (f);
+  Lisp_Object root;
+
+  XSETFRAME (root, r);
+
+  return root;
+}
+
+
 /* 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.
    If MINIBUF is nil, do not consider minibuffer-only candidate.
@@ -2433,61 +2466,68 @@ delete_frame (Lisp_Object frame, Lisp_Object force)
   /* Don't let the frame remain selected.  */
   if (f == sf)
     {
-      Lisp_Object tail;
-      Lisp_Object frame1 UNINIT;  /* This line works around GCC bug 85563.  */
-      eassume (CONSP (Vframe_list));
-
-      /* Look for another visible frame on the same terminal.
-        Do not call next_frame here because it may loop forever.
-        See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=15025.  */
-      FOR_EACH_FRAME (tail, frame1)
+      if (is_tty_child_frame (f))
+       /* If F is a child frame on a tty and is the selected frame, try
+          to re-select the frame that was selected before F.  */
+       do_switch_frame (mru_rooted_frame (f), 0, 1, Qnil);
+      else
        {
-         struct frame *f1 = XFRAME (frame1);
-
-         if (!EQ (frame, frame1)
-             && !FRAME_TOOLTIP_P (f1)
-             && FRAME_TERMINAL (f) == FRAME_TERMINAL (f1)
-             && FRAME_VISIBLE_P (f1))
-           break;
-       }
+         Lisp_Object tail;
+         Lisp_Object frame1 UNINIT;  /* This line works around GCC bug 85563.  */
+         eassume (CONSP (Vframe_list));
 
-      /* If there is none, find *some* other frame.  */
-      if (NILP (frame1) || EQ (frame1, frame))
-       {
+         /* Look for another visible frame on the same terminal.
+            Do not call next_frame here because it may loop forever.
+            See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=15025.  */
          FOR_EACH_FRAME (tail, frame1)
            {
              struct frame *f1 = XFRAME (frame1);
 
              if (!EQ (frame, frame1)
-                 && FRAME_LIVE_P (f1)
-                 && !FRAME_TOOLTIP_P (f1))
+                 && !FRAME_TOOLTIP_P (f1)
+                 && FRAME_TERMINAL (f) == FRAME_TERMINAL (f1)
+                 && FRAME_VISIBLE_P (f1))
+               break;
+           }
+
+         /* If there is none, find *some* other frame.  */
+         if (NILP (frame1) || EQ (frame1, frame))
+           {
+             FOR_EACH_FRAME (tail, frame1)
                {
-                 if (FRAME_TERMCAP_P (f1) || FRAME_MSDOS_P (f1))
-                   {
-                     Lisp_Object top_frame = FRAME_TTY (f1)->top_frame;
+                 struct frame *f1 = XFRAME (frame1);
 
-                     if (!EQ (top_frame, frame))
-                       frame1 = top_frame;
+                 if (!EQ (frame, frame1)
+                     && FRAME_LIVE_P (f1)
+                     && !FRAME_TOOLTIP_P (f1))
+                   {
+                     if (FRAME_TERMCAP_P (f1) || FRAME_MSDOS_P (f1))
+                       {
+                         Lisp_Object top_frame = FRAME_TTY (f1)->top_frame;
+
+                         if (!EQ (top_frame, frame))
+                           frame1 = top_frame;
+                       }
+                     break;
                    }
-                 break;
                }
            }
-       }
 #ifdef NS_IMPL_COCOA
-      else
-       {
-         /* Under NS, there is no system mechanism for choosing a new
-            window to get focus -- it is left to application code.
-            So the portion of THIS application interfacing with NS
-            needs to make the frame we switch to the key window.  */
-         struct frame *f1 = XFRAME (frame1);
-         if (FRAME_NS_P (f1))
-           ns_make_frame_key_window (f1);
-       }
+         else
+           {
+             /* Under NS, there is no system mechanism for choosing a new
+                window to get focus -- it is left to application code.
+                So the portion of THIS application interfacing with NS
+                needs to make the frame we switch to the key window.  */
+             struct frame *f1 = XFRAME (frame1);
+             if (FRAME_NS_P (f1))
+               ns_make_frame_key_window (f1);
+           }
 #endif
 
-      do_switch_frame (frame1, 0, 1, Qnil);
-      sf = SELECTED_FRAME ();
+         do_switch_frame (frame1, 0, 1, Qnil);
+         sf = SELECTED_FRAME ();
+       }
     }
   else
     /* Ensure any minibuffers on FRAME are moved onto the selected
@@ -2583,11 +2623,11 @@ delete_frame (Lisp_Object frame, Lisp_Object force)
     f->terminal = 0;             /* Now the frame is dead.  */
     unblock_input ();
 
-  /* Clear markers and overlays set by F on behalf of an input
-     method.  */
+    /* Clear markers and overlays set by F on behalf of an input
+       method.  */
 #ifdef HAVE_TEXT_CONVERSION
-  if (FRAME_WINDOW_P (f))
-    reset_frame_state (f);
+    if (FRAME_WINDOW_P (f))
+      reset_frame_state (f);
 #endif
 
     /* If needed, delete the terminal that this frame was on.
@@ -7146,6 +7186,7 @@ iconify the top level frame instead.  */);
   defsubr (&Sframe_list);
   defsubr (&Sframe_parent);
   defsubr (&Sframe_ancestor_p);
+  defsubr (&Sframe_root_frame);
   defsubr (&Snext_frame);
   defsubr (&Sprevious_frame);
   defsubr (&Slast_nonminibuf_frame);
index bff610472c08fbbf47054adcc4a0b606fe466cdd..fea8baa73322f0485bd890a0f84051dfeae99df5 100644 (file)
@@ -1428,23 +1428,6 @@ FRAME_PARENT_FRAME (struct frame *f)
 /* 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)
@@ -1518,6 +1501,7 @@ 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 struct frame *root_frame (struct frame *f);
 extern Lisp_Object delete_frame (Lisp_Object, Lisp_Object);
 extern bool frame_inhibit_resize (struct frame *, bool, Lisp_Object);
 extern void adjust_frame_size (struct frame *, int, int, int, bool,
@@ -1541,6 +1525,27 @@ extern Lisp_Object Vframe_list;
       ? 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;
+    }
+  /* If F is a child frame on a tty and is the selected frame, try to
+     re-select the frame that was selected before F.  */
+  else if (is_tty_child_frame (f) && f == XFRAME (selected_frame))
+    do_switch_frame (mru_rooted_frame (f), 0, 0, Qnil);
+
+  f->visible = v;
+}
 \f
 /***********************************************************************
                        Display-related Macros
index ff58eb12ee00ada38f3d0f792bf18e6b0d9e3547..d7e6cd00c99c8494d6b66b04aeb399679b7cfb02 100644 (file)
@@ -3097,6 +3097,38 @@ be listed first but no error is signaled.  */)
 {
   return window_list_1 (window, minibuf, all_frames);
 }
+
+/** Return most recently selected frame that has the same root as a
+    given frame.  It's defined here because the static window_list_1 is
+    here too but in fact it's only needed in the frame code.  */
+Lisp_Object
+mru_rooted_frame (struct frame *f)
+{
+  Lisp_Object windows = window_list_1 (FRAME_SELECTED_WINDOW (f), Qnil, Qt);
+  struct frame *r = root_frame (f);
+  struct window *b = NULL;
+
+  for (; CONSP (windows); windows = XCDR (windows))
+    {
+      struct window *w = XWINDOW (XCAR (windows));
+      struct frame *wf = WINDOW_XFRAME (w);
+
+      if (wf != f && root_frame (wf) == r && FRAME_VISIBLE_P (wf)
+         && (!b || w->use_time > b->use_time))
+       b = w;
+    }
+
+  if (b)
+    return WINDOW_FRAME (b);
+  else
+    {
+      Lisp_Object root;
+
+      XSETFRAME (root, r);
+
+      return root;
+    }
+}
 \f
 /* Look at all windows, performing an operation specified by TYPE
    with argument OBJ.
index 39356f80df736afd0ee7ed6ed6bca28add9cd883..a48c370b1987c4c380cf414fb62ec84bd638da64 100644 (file)
@@ -1227,6 +1227,7 @@ extern void wset_buffer (struct window *, Lisp_Object);
 extern bool window_outdated (struct window *);
 extern ptrdiff_t window_point (struct window *w);
 extern void window_discard_buffer_from_dead_windows (Lisp_Object);
+extern Lisp_Object mru_rooted_frame (struct frame *);
 extern void init_window_once (void);
 extern void init_window (void);
 extern void syms_of_window (void);