Skip to content

Commit 6ba0486

Browse files
andyclaude
andcommitted
Gateway: catch a zero-height window whenever it happens, not just once (#67)
Reproduced on beryllium 2026-09-22 -- the first repro this bug has had. The window comes up showing nothing but its title bar, with Gtk-CRITICAL: gtk_window_resize: assertion 'height > 0' failed on stderr, and no resize handle to recover with. Crucially it is INTERMITTENT: the same binary, same machine, same session starts correctly most times. That is the signature of the wx3.2/GTK3 layout reentrancy class CLAUDE.md describes, not of a deterministic path -- which also explains why it was never reproducible on the machine it was originally reported against. The existing safety net only ran once, at the end of the constructor, so it could only catch a bad size that was already in place by then. It now also runs on EVT_SIZE. Two things it deliberately does NOT do: it does not call SetSize() from inside the size handler, since that is the very reentrancy suspected of causing this -- the correction is deferred with CallAfter() to the next idle, after the in-flight layout pass has finished. And it guards against queueing more than one correction at a time. Still a safety net rather than a root cause: it makes the window usable instead of leaving a title bar the user cannot resize. The root cause needs someone with the repro to catch it in the act. Unrelated and still unexplained: the same machine also saw a one-off "free(): invalid size" at startup, which did not recur under gdb. Not addressed here; do not assume this commit fixes it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EFMUNbw2UpRPmgsqviAowb
1 parent e612041 commit 6ba0486

2 files changed

Lines changed: 51 additions & 0 deletions

File tree

‎src/apps/gateway/Gateway.C‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ BEGIN_EVENT_TABLE( Gateway, wxFrame )
5252

5353
EVT_CLOSE( Gateway::OnCloseWindow )
5454
EVT_ICONIZE( Gateway::OnIconize )
55+
EVT_SIZE( Gateway::OnSize )
5556
EVT_TIMER( wxID_ANY, Gateway::OnTimer )
5657

5758
END_EVENT_TABLE()
@@ -107,6 +108,7 @@ Gateway::Gateway( GatewayApp* app, wxWindow* parent,
107108
// has realized it. Constructing the timer first left it attached to an
108109
// unrealized wxFrame -- wx2.8's GTK timer path tolerated that silently,
109110
// but wx3.2 asserts on it (harmless spam, but noisy and worth not doing).
111+
p_fixingSize = false;
110112
p_timer = new wxTimer(this);
111113

112114
SetIcon(wxIcon(ewxBitmap::pixmapFile("gateway64.xpm"), wxBITMAP_TYPE_XPM));
@@ -134,6 +136,52 @@ Gateway::Gateway( GatewayApp* app, wxWindow* parent,
134136
}
135137

136138

139+
/**
140+
* Keep the window from being left with no usable height (GitHub #67).
141+
*
142+
* The constructor already clamps a non-positive size once, but that only
143+
* catches it if the bad size is in place by then. Reproduced on beryllium
144+
* 2026-09-22: the window comes up showing nothing but its title bar, with
145+
* "gtk_window_resize: assertion 'height > 0' failed" on stderr -- and it is
146+
* INTERMITTENT, which is the signature of the wx3.2/GTK3 layout reentrancy
147+
* class documented in CLAUDE.md rather than of a deterministic path. A
148+
* one-shot check at construction cannot catch a zero height that arrives
149+
* later.
150+
*
151+
* Deliberately does not call SetSize() from inside the size handler: that
152+
* is the very reentrancy that causes this. CallAfter() defers the
153+
* correction to the next idle, by which time the in-flight layout pass has
154+
* finished.
155+
*
156+
* This is still a safety net, not a root cause. It makes the window usable
157+
* instead of leaving the user with a title bar and no way to resize it.
158+
*/
159+
void Gateway::OnSize(wxSizeEvent& event)
160+
{
161+
event.Skip();
162+
163+
int width, height;
164+
GetSize(&width, &height);
165+
if (width > 0 && height > 0) return;
166+
167+
// One correction at a time -- without this, a window that keeps being
168+
// resized to zero would queue an unbounded number of them.
169+
if (p_fixingSize) return;
170+
p_fixingSize = true;
171+
172+
CallAfter([this, width, height]() {
173+
int w, h;
174+
GetSize(&w, &h);
175+
if (w <= 0 || h <= 0) {
176+
std::cerr << "Gateway: window came up " << w << "x" << h
177+
<< " (GitHub #67); forcing a usable size." << std::endl;
178+
SetSize(w <= 0 ? 68 : w, h <= 0 ? 68 : h);
179+
}
180+
p_fixingSize = false;
181+
});
182+
}
183+
184+
137185
/**
138186
* Destructor.
139187
*

‎src/apps/gateway/Gateway.H‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ protected:
9292

9393
void OnCloseWindow(wxCloseEvent& event);
9494
void OnIconize(wxIconizeEvent &event);
95+
// GitHub #67: clamp a zero-height window, deferred to avoid reentrancy.
96+
void OnSize(wxSizeEvent& event);
9597
void OnTimer(wxTimerEvent& event);
9698
void OnMenuClick(wxCommandEvent& event);
9799

@@ -107,6 +109,7 @@ protected:
107109
short p_pixmapIndex;
108110
GatewayPrefs * p_prefsDlg;
109111
wxTimer* p_timer;
112+
bool p_fixingSize;
110113

111114
GatewayApp* p_app;
112115

0 commit comments

Comments
 (0)