Never ask for activation with timestamp 0

present_toplevel() already asked for focus with a server timestamp, but only when
the widget happened to be realized. An unrealized widget has no GdkWindow, so
there was nothing to read a timestamp from and control fell through to
wxFrame::Raise() — which asks for activation with GDK_CURRENT_TIME, i.e. 0.

Zero is exactly what focus-stealing prevention discards. metacity says it out loud
when a sketch value field opens:

    Buggy client sent a _NET_ACTIVE_WINDOW message with a timestamp of 0

and mutter, same lineage, refuses it silently on the user's desktop. That refusal
is the reported defect: the field is visible, never receives the keyboard, and
Enter commits the as-drawn prefill.

So realize the widget and retry, and do NOT fall back to Raise() on X11 — a
timestamp-0 activation is refused anyway, and on some window managers it only
marks the window as demanding attention.

Not yet confirmed end to end: both window managers with focus-stealing prevention
available here abort on this frame — metacity at frames.c:1239, xfwm4 with
BadWindow on SetInputFocus as the frame is destroyed under it — so the ladder
cannot yet return a trustworthy verdict under one. Two WMs crashing on the same
borderless, repeatedly re-mapped STAY_ON_TOP frame is its own signal about this
design. Tracked in projects-1p5 and projects-40m.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA
This commit is contained in:
Tommaso Bianchi
2026-09-06 10:39:50 +02:00
co-authored by Claude Opus 5
parent 9134299233
commit 0fee4494e2
+22 -1
View File
@@ -56,6 +56,22 @@ void present_toplevel(wxFrame* frame)
GtkWidget* widget = static_cast<GtkWidget*>(frame->GetHandle());
if (widget && GTK_IS_WIDGET(widget)) {
GdkWindow* gdkwin = gtk_widget_get_window(widget);
// REALIZE, then retry, rather than dropping to the fallback below. An unrealized
// widget has no GdkWindow, so there is nothing to read a server timestamp from —
// and the fallback is wxFrame::Raise(), which asks for activation with
// GDK_CURRENT_TIME. Zero is precisely the value focus-stealing prevention throws
// away; metacity says so out loud:
//
// Buggy client sent a _NET_ACTIVE_WINDOW message with a timestamp of 0
//
// and mutter, same lineage, refuses it silently on the user's desktop. That refusal
// IS the reported bug: the field is on screen, never gets the keyboard, and Enter
// commits the as-drawn prefill. The timestamped call was already here; this is the
// path that was quietly bypassing it.
if (gdkwin == nullptr) {
gtk_widget_realize(widget);
gdkwin = gtk_widget_get_window(widget);
}
if (gdkwin) {
gtk_window_present_with_time(GTK_WINDOW(widget),
gdk_x11_get_server_time(gdkwin));
@@ -63,8 +79,13 @@ void present_toplevel(wxFrame* frame)
}
}
}
#endif
// Deliberately NOT falling back to Raise() on X11: a timestamp-0 activation is worse than
// no activation — it is refused anyway, and on some WMs it marks the window as demanding
// attention instead.
return;
#else
if (frame) frame->Raise();
#endif
}
#else
void present_toplevel(wxFrame* frame)