Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/architecture/07-cursor.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ slightly during zoom ramps, nearest keeps it pixelated and steady. Pure rules:
cursor event back on DWM's (a 5-10 px shift at ~5x that snapped back when the zoom stopped). While
DWM centres, every level write is followed by a pixel-and-back nudge, so DWM re-centres by its own
rule in the same frame (`NudgeAfterLevelWrite`). Field-verified: shift gone, pans steady.
- **No write without its nudge (#381).** The nudge is skipped while a mouse button is held (it made
some clicks fail), so a pan write during a held click would leave Wind's centre on screen until the
next cursor event: drag-selects and held clicks shook. Pan-only writes are held for the click
window instead (`HoldWriteForClick`); level changes and forced writes still go out.
- `txDwmCentre=0` keeps the native pointer but lets Wind write the view itself (kill switch; pans
then wobble by speed x tick x zoom).
- **DWM's learned offset.** DWM learns the gap between `GetCursorPos` and its own cursor point and
Expand Down
12 changes: 12 additions & 0 deletions src/native_cursor.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,16 @@ inline bool NudgeAfterWrite(bool dwmCentring, bool wrote) {
return dwmCentring && wrote;
}

// NO WRITE WITHOUT ITS NUDGE (issue #381, field report 2026-10-08: zoomed, holding the left button
// for a drag-select or a held click made the view shake; plain panning was fine). The nudge is
// skipped while a click is in progress (it made some clicks fail), but the write it belongs to
// still went out, so during a hold every changed tick put Wind's centre on screen and the next
// cursor event put DWM's back: the view alternated between the two. While DWM centres, a pan-only
// write is held for the click instead; DWM keeps centring on every cursor event, and the first tick
// after the click window writes and nudges as usual. Level changes and forced writes still go out
// (a zoom during a drag must not freeze, and a centring switch needs its write).
inline bool HoldWriteForClick(bool dwmCentring, bool clickInProgress, bool levelMoved, bool forced) {
return dwmCentring && clickInProgress && !levelMoved && !forced;
}

} // namespace wind
5 changes: 4 additions & 1 deletion src/transform_model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,10 @@ void TransformModel::present(const MapResult& r, double level, const Config& cfg
ci.minOffsetPx = cfg.txMinOffsetPx;
ci.settleMs = kSettleMs;
const bool forceWrite = forceWrite_;
const bool writeNow = ShouldWriteTransform(ci) || forceWrite;
// A pan write while DWM centres must be followed by its nudge, which a click in progress
// forbids: hold it until the click window ends (HoldWriteForClick, issue #381).
const bool clickHold = HoldWriteForClick(dwmCentreOn_, ClickInProgress(), ci.levelMoved, forceWrite);
const bool writeNow = (ShouldWriteTransform(ci) || forceWrite) && !clickHold;

if (writeNow) {
lastOffX_ = m.offX; lastOffY_ = m.offY; lastTxX_ = m.txX; lastTxY_ = m.txY;
Expand Down
8 changes: 8 additions & 0 deletions tests/test_native_cursor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ TEST_CASE("while DWM centres, every write is followed by a cursor event (one cen
CHECK(NudgeAfterWrite(true, false) == false); // nothing written
}

TEST_CASE("A pan write is held while a click forbids its nudge (#381)") {
CHECK(HoldWriteForClick(true, true, false, false) == true); // held button, DWM centres: hold
CHECK(HoldWriteForClick(true, false, false, false) == false); // no click: write and nudge
CHECK(HoldWriteForClick(false, true, false, false) == false); // Wind owns the view: write
CHECK(HoldWriteForClick(true, true, true, false) == false); // a zoom during a drag still writes
CHECK(HoldWriteForClick(true, true, false, true) == false); // a forced write (centring switch)
}

TEST_CASE("DWM centring yields only near an armed wall, not everywhere it is reachable") {
// 12x: wall at 32000/12 = 2666.7 source px.
CHECK(NearWall(true, 1000.0, 500.0, 12.0, 32000.0, 64.0) == false); // middle of the screen
Expand Down
Loading