fix(audit): pass 4 — symmetric undo on whole-tab close, clipboard cb threading note
MEDIUM — removeSurface's whole-tab branch now pushes to undo: closeTab pushed pushTabUndo before removing a tab; removeSurface's "this surface is the whole tab" branch (libghostty's onCloseSurface callback path, fires when a child shell exits cleanly) skipped it, making undo behavior asymmetric — user-driven tab close was undoable, shell-exited tab close wasn't. Push the same snapshot with the same gates (>1 tab, !quick-terminal) so both paths behave identically. LOW — onReadClipboard threading clarification: Pass 4 reviewer flagged a potential cross-thread QClipboard access. Verified by inspecting libghostty: read_clipboard_cb is invoked behind ghostty_app_tick, which only runs from our GUI-thread frame timer. The "may arrive on a worker thread" comment was stale and defensive against a hazard that doesn't exist in this build. Updated the comment to document the actual GUI-thread invariant so future reviewers don't chase the same false alarm. Co-Authored-By: claude-flow <ruv@ruv.net>pull/12846/head
parent
4a7f07402b
commit
7cf505c0f8
|
|
@ -700,6 +700,13 @@ void MainWindow::removeSurface(GhosttySurface *surface) {
|
|||
|
||||
// Otherwise this surface is the whole tab.
|
||||
const int index = m_tabs->indexOf(parent);
|
||||
// Push to undo so a shell-exited tab close is symmetric with a
|
||||
// user-initiated tab close (closeTab pushes too). Skip the last
|
||||
// tab — its closeEvent runs pushWindowUndo and we don't want to
|
||||
// double-stack. Also skip the quick terminal (which doesn't push
|
||||
// to either stack by design).
|
||||
if (index >= 0 && m_tabs->count() > 1 && !m_quickTerminal)
|
||||
pushTabUndo(index);
|
||||
if (index >= 0) m_tabs->removeTab(index);
|
||||
if (parent) parent->deleteLater(); // page; destroys the surface too
|
||||
// The surface close was already confirmed; don't re-prompt on the
|
||||
|
|
@ -2812,10 +2819,14 @@ bool MainWindow::onAction(ghostty_app_t, ghostty_target_s target,
|
|||
|
||||
bool MainWindow::onReadClipboard(void *ud, ghostty_clipboard_e loc,
|
||||
void *state) {
|
||||
// surface userdata. Called synchronously when libghostty needs
|
||||
// clipboard contents (paste). May arrive on a worker thread, so
|
||||
// surfaceAlive validates the pointer first — the GhosttySurface
|
||||
// could be mid-destruction.
|
||||
// surface userdata. Called synchronously by libghostty when a
|
||||
// surface needs clipboard contents (paste). This runs on the GUI
|
||||
// thread by construction: every libghostty entry point that
|
||||
// surfaces a paste lives behind ghostty_app_tick, which the
|
||||
// process-wide frame timer drives — and that timer is on the GUI
|
||||
// thread. QClipboard is GUI-thread-only, so reading directly here
|
||||
// is safe; surfaceAlive still validates the pointer in case a
|
||||
// surface is mid-destruction on this same thread.
|
||||
auto *surface = static_cast<GhosttySurface *>(ud);
|
||||
if (!surfaceAlive(surface) || !surface->surface()) return false;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue