In pgtk_free_frame_resources transfer keyboard focus to parent (Bug#64625)

* src/pgtkterm.c (pgtk_new_focus_frame): Declare static.
(pgtk_free_frame_resources): If this frame currently holds
keyboard focus, explicitly transfer focus to its parent frame
before releasing resources (Bug#64625).

Copyright-paperwork-exempt: yes
This commit is contained in:
haiyang miao 2026-07-02 10:55:24 +02:00 committed by Martin Rudalics
parent 6f9ac7ffdb
commit e560eacf6d

View file

@ -109,6 +109,7 @@ static bool current_drop_context_drop;
static guint32 current_drop_time;
static void pgtk_delete_display (struct pgtk_display_info *);
static void pgtk_new_focus_frame (struct pgtk_display_info *, struct frame *);
static void pgtk_clear_frame_area (struct frame *, int, int, int, int);
static void pgtk_fill_rectangle (struct frame *, unsigned long, int, int,
int, int, bool);
@ -467,9 +468,62 @@ pgtk_free_frame_resources (struct frame *f)
#define CLEAR_IF_EQ(FIELD) \
do { if (f == dpyinfo->FIELD) dpyinfo->FIELD = 0; } while (false)
CLEAR_IF_EQ (x_focus_frame);
/* If this frame currently holds keyboard focus, explicitly transfer
focus to its parent frame before releasing resources.
On X11 the X server delivers a FocusIn event to the parent when a
child window is destroyed, so focus was restored automatically.
On Wayland there is no such mechanism: the compositor does nothing,
leaving Emacs with x_focus_frame == NULL permanently and ignoring
all keyboard input (bug#64625).
IMPORTANT: we must clear highlight_frame from f BEFORE calling
pgtk_new_focus_frame. pgtk_new_focus_frame calls
pgtk_frame_rehighlight, which calls frame_unhighlight(old_highlight)
where old_highlight = dpyinfo->highlight_frame. If that is still f,
frame_unhighlight -> gui_update_cursor(f) would try to use f's face
cache, which was already freed by free_frame_faces(f) above ->
segfault. By clearing highlight_frame first, pgtk_frame_rehighlight
sees old_highlight == NULL and skips frame_unhighlight entirely. */
if (f == dpyinfo->x_focus_frame)
{
struct frame *new_focus = FRAME_PARENT_FRAME (f);
/* Clear highlight_frame from f so pgtk_frame_rehighlight will not
call frame_unhighlight on the dying frame whose faces are freed. */
if (dpyinfo->highlight_frame == f)
dpyinfo->highlight_frame = NULL;
if (new_focus != NULL
&& FRAME_LIVE_P (new_focus)
&& FRAME_GTK_WIDGET (new_focus) != NULL
&& gtk_widget_get_realized (FRAME_GTK_WIDGET (new_focus)))
{
/* Transfer Emacs's internal focus/highlight to the parent. */
pgtk_new_focus_frame (dpyinfo, new_focus);
/* Physically move the GTK/Wayland keyboard focus to the parent
widget. The Wayland compositor will not do this automatically
when the child widget is destroyed. */
gtk_widget_grab_focus (FRAME_GTK_WIDGET (new_focus));
}
else
{
/* Parent is gone or unrealized; just clear focus state. */
dpyinfo->x_focus_frame = NULL;
pgtk_frame_rehighlight (dpyinfo);
}
}
/* CLEAR_IF_EQ is now a no-op for highlight_frame if we already cleared
it above, which is correct. */
CLEAR_IF_EQ (highlight_frame);
CLEAR_IF_EQ (x_focus_event_frame);
/* Clear x_focus_event_frame directly so that any focus-out event
emitted by gtk_widget_destroy below is a no-op in
pgtk_focus_changed, preventing a spurious
pgtk_new_focus_frame(dpyinfo, NULL) that would undo the transfer. */
if (f == dpyinfo->x_focus_event_frame)
dpyinfo->x_focus_event_frame = NULL;
CLEAR_IF_EQ (last_mouse_frame);
CLEAR_IF_EQ (last_mouse_motion_frame);
CLEAR_IF_EQ (last_mouse_glyph_frame);