[I asked Claude to spin this issue off after it became a distraction from some interface work I was trying to accomplish. -jf]
When Quackle is built with -DQUACKLE_SANITIZE_ADDRESS=ON on macOS, the app receives no mouse-hover feedback: tooltips never appear, and the I-beam cursor does not appear over enabled QLineEdits. Rebuilding the identical source with QUACKLE_SANITIZE_ADDRESS=OFF restores both immediately.
This was noticed while adding tooltips to the simulation controls, but the tooltips are only the symptom that got noticed first. The two symptoms share a cause: on macOS, both mouse-moved events and cursor-shape updates are delivered through the same NSTrackingArea that QNSView installs. Hover-event delivery is what's broken; tooltips and cursor shape are downstream of it.
Root cause not yet identified.
Environment
- macOS 15.7.2 (24G325), arm64
- Qt 6.11.1 (macOS build), used for every build below
- Apple clang 17.0.0 (clang-1700.0.13.5)
- Quackle at 169b6f8
- ASan flags are just what
Settings.cmake adds: -fsanitize=address -fno-omit-frame-pointer -g for compile, -fsanitize=address for link. Nothing else differs between the two configurations.
Reproducing
cmake -S quacker -B build-asan -G Ninja -DCMAKE_PREFIX_PATH=<qt> -DQUACKLE_SANITIZE_ADDRESS=ON
cmake --build build-asan --target Quackle
Run it, start a game, go to the Choices tab, and hover the simulation controls. No tooltip, and no I-beam over the "Partial oppo rack" / "Log sim to file" fields when they are enabled. The same build with -DQUACKLE_SANITIZE_ADDRESS=OFF behaves correctly.
ASan prints no report; the app does not crash or misbehave otherwise.
What has been ruled out
All of the following were measured on ASan-ON vs ASan-OFF builds of the same source, same Qt 6.11.1:
-
The tooltip machinery is fine under ASan. Injecting a synthetic QEvent::MouseMove at the QWidgetWindow level (rather than at the widget, which does not reach the tooltip code) triggers a full, correct tooltip cycle in both builds: the 700 ms wake-up timer fires, QToolTip::isVisible() becomes true, the text is identical (322 chars), and the popup renders on screen — screenshots of the two builds are comparable pixel-for-pixel. So QToolTip, QTipLabel, the timer, and the popup window are all healthy under ASan. Something upstream of them is not delivering real hover events.
-
NSTrackingArea setup is identical, at least in a minimal Qt app (a bare QWidget + QLineEdit, built both ways). Both report one tracking area with options 0x287 — MouseEnteredAndExited | MouseMoved | CursorUpdate | ActiveInActiveApp | InVisibleRect — plus appActive=1, keyWindow=1, acceptsMouseMovedEvents=0. Not yet checked inside Quackle's own window, and not yet confirmed by hand whether the minimal app actually loses hover under ASan (see below).
-
Not event-loop starvation. Both builds sit at 0.0% CPU when idle, so the ASan build is not too busy to service the Cocoa event queue.
-
Nothing else in the build differs. Settings.cmake adds only the sanitizer flags; no bundling, optimization-level, or Qt-configuration difference between the two.
Note on methodology: attempts to drive a real hover programmatically failed and are not evidence about ASan. QCursor::setPos warps the pointer without generating mouseMoved: events (measured: 0 QEvent::MouseMove after 40 warps, in both builds), and posting synthetic HID events via CGEventPost requires Accessibility permission. Every automated result above therefore comes from injection below the platform layer; the ASan failure itself has only been observed by hand.
Suggested next step
Build a minimal Qt app (bare QWidget + QLineEdit with a tooltip) twice, differing only in -fsanitize=address, and hover each by hand:
- If the ASan build also loses the I-beam and tooltip, this is a Qt/ASan/macOS interaction with nothing to do with Quackle. Worth a note in the build docs so nobody chases a phantom UI bug, and possibly worth an upstream Qt report.
- If the ASan build behaves correctly, something in Quackle is implicated — most likely a latent memory bug whose behavior changes under ASan's allocator and redzones — and bisecting Quackle's own startup/widget code is the way in.
If it turns out to be the first case, the practical impact is limited: ASan builds are used for headless regression testing in CI, where hover is irrelevant. The trap is a developer running an ASan build interactively and concluding the GUI is broken.
[I asked Claude to spin this issue off after it became a distraction from some interface work I was trying to accomplish. -jf]
When Quackle is built with
-DQUACKLE_SANITIZE_ADDRESS=ONon macOS, the app receives no mouse-hover feedback: tooltips never appear, and the I-beam cursor does not appear over enabledQLineEdits. Rebuilding the identical source withQUACKLE_SANITIZE_ADDRESS=OFFrestores both immediately.This was noticed while adding tooltips to the simulation controls, but the tooltips are only the symptom that got noticed first. The two symptoms share a cause: on macOS, both mouse-moved events and cursor-shape updates are delivered through the same
NSTrackingAreathatQNSViewinstalls. Hover-event delivery is what's broken; tooltips and cursor shape are downstream of it.Root cause not yet identified.
Environment
Settings.cmakeadds:-fsanitize=address -fno-omit-frame-pointer -gfor compile,-fsanitize=addressfor link. Nothing else differs between the two configurations.Reproducing
Run it, start a game, go to the Choices tab, and hover the simulation controls. No tooltip, and no I-beam over the "Partial oppo rack" / "Log sim to file" fields when they are enabled. The same build with
-DQUACKLE_SANITIZE_ADDRESS=OFFbehaves correctly.ASan prints no report; the app does not crash or misbehave otherwise.
What has been ruled out
All of the following were measured on ASan-ON vs ASan-OFF builds of the same source, same Qt 6.11.1:
The tooltip machinery is fine under ASan. Injecting a synthetic
QEvent::MouseMoveat theQWidgetWindowlevel (rather than at the widget, which does not reach the tooltip code) triggers a full, correct tooltip cycle in both builds: the 700 ms wake-up timer fires,QToolTip::isVisible()becomes true, the text is identical (322 chars), and the popup renders on screen — screenshots of the two builds are comparable pixel-for-pixel. SoQToolTip,QTipLabel, the timer, and the popup window are all healthy under ASan. Something upstream of them is not delivering real hover events.NSTrackingAreasetup is identical, at least in a minimal Qt app (a bareQWidget+QLineEdit, built both ways). Both report one tracking area with options0x287—MouseEnteredAndExited | MouseMoved | CursorUpdate | ActiveInActiveApp | InVisibleRect— plusappActive=1,keyWindow=1,acceptsMouseMovedEvents=0. Not yet checked inside Quackle's own window, and not yet confirmed by hand whether the minimal app actually loses hover under ASan (see below).Not event-loop starvation. Both builds sit at 0.0% CPU when idle, so the ASan build is not too busy to service the Cocoa event queue.
Nothing else in the build differs.
Settings.cmakeadds only the sanitizer flags; no bundling, optimization-level, or Qt-configuration difference between the two.Note on methodology: attempts to drive a real hover programmatically failed and are not evidence about ASan.
QCursor::setPoswarps the pointer without generatingmouseMoved:events (measured: 0QEvent::MouseMoveafter 40 warps, in both builds), and posting synthetic HID events viaCGEventPostrequires Accessibility permission. Every automated result above therefore comes from injection below the platform layer; the ASan failure itself has only been observed by hand.Suggested next step
Build a minimal Qt app (bare
QWidget+QLineEditwith a tooltip) twice, differing only in-fsanitize=address, and hover each by hand:If it turns out to be the first case, the practical impact is limited: ASan builds are used for headless regression testing in CI, where hover is irrelevant. The trap is a developer running an ASan build interactively and concluding the GUI is broken.