Don't resurrect invisible child frames when rebuilding parent links

On the NS port, -[EmacsWindow setParentChildRelationships] reattaches
every child frame to its parent via -addChildWindow:ordered:, which also
orders the child window onto the screen.  This runs whenever the
parent/child relationships are rebuilt, e.g., when entering non-native
fullscreen, which allocates a fresh EmacsWindow whose initializer
rebuilds the relationships.  A child frame that Emacs had made invisible
(e.g. a corfu/company completion popup) was thereby brought back as a
stale, non-responsive child frame.  Emacs never repaints to clear it
because frame_redisplay_p trusts FRAME_VISIBLE_P on the NS port and
avoids dealing with the child frame when it is marked as invisible.

Native fullscreen does not trigger this: -toggleFullScreen: hands off to
AppKit without allocating a new window, so the rebuild never runs.

A hidden child frame is normally detached from its parent already: Emacs
hides it with -orderOut: (ns_make_frame_invisible), which per Apple's
documentation removes a child window from its parent before ordering it
out.  The fix is therefore not to re-attach a child while it is
invisible; ns_make_frame_visible already reinstates the parent/child link
when the frame is shown again.

* src/nsterm.m ([EmacsWindow setParentChildRelationships]): Only
re-attach a child window when the frame is marked visible.
(ns_make_frame_visible): Explain, with a reference to Apple's
documentation, why the parent/child link must be reinstated on show.
This commit is contained in:
Andrea Alberti 2026-05-29 10:42:47 +02:00 committed by Alan Third
parent e4350c538f
commit 3801c09ae2

View file

@ -1651,8 +1651,13 @@ -(void)remove
unblock_input ();
}
/* Making a frame invisible seems to break the parent->child
relationship, so reinstate it. */
/* A child window cannot remain attached while hidden. Per Apple's
documentation, "Calling orderOut(_:) on a child window causes the
window to be removed from its parent window before being removed"
(https://developer.apple.com/documentation/appkit/nswindow/orderout(_:)),
and ns_make_frame_invisible hides the frame with -orderOut:. The
parent->child relationship is therefore broken while invisible, so
reinstate it now that we are making the frame visible again. */
if ([window parentWindow] == nil && FRAME_PARENT_FRAME (f) != NULL)
{
block_input ();
@ -9979,8 +9984,15 @@ - (void)setParentChildRelationships
[ourView toggleFullScreen:self];
#endif
[parentWindow addChildWindow:self
ordered:NSWindowAbove];
/* -addChildWindow: also orders the child window onto the screen, so
attaching a child frame Emacs considers invisible is what
resurrects a dismissed completion popup (corfu, company-box, ...)
when relationships are rebuilt. Only attach a visible child; a
hidden one is re-attached by ns_make_frame_visible when it is
shown again. */
if (FRAME_VISIBLE_P (ourFrame))
[parentWindow addChildWindow:self
ordered:NSWindowAbove];
}
/* Check our child windows are configured correctly. */