Fix drag-out giving live drags to OLE: prove the pointer left REAPER

OffReaper leaves the surface vocabulary; OsHandoff now gates on a window-ownership
proof resolved in the shell, so no in-REAPER surface can reach DoDragDrop.
This commit is contained in:
2026-08-03 15:50:25 -04:00
parent d5280687f3
commit 9599e96e0c
14 changed files with 221 additions and 109 deletions
+9 -2
View File
@@ -199,6 +199,12 @@ LiveDrop resolveLiveDrop(int x, int y) {
const DropProbe probe = probeDropTarget(sp.x, sp.y);
ctx.surface = probe.surface;
ctx.haveTrack = probe.track != nullptr;
// Two independent probes because they answer two independent questions: REAPER's
// hit-test names the surface, window ownership decides whether we are still in REAPER
// at all. The SDK hit-test cannot answer the second — GetThingFromPoint documents no
// off-REAPER return, so its silence over the transport or the toolbar used to read as
// "the user left" and gave the live drag away to OLE.
ctx.pointerOffHost = !pointerOverHostWindow(sp.x, sp.y);
out.track = probe.track;
out.screenX = sp.x;
}
@@ -219,11 +225,12 @@ LiveDrop resolveLiveDrop(int x, int y) {
// loop, so the internal drag must be fully wound down first, and only once the payload is known
// to be hand-off-able. This runs on every qualifying move — do not memoize a failed attempt.
//
// Residual (accepted): once the pointer has left REAPER, dragging back INTO a REAPER window
// Residual (accepted): once the pointer has genuinely left every REAPER window, dragging back IN
// mid-modal-loop delivers a CF_HDROP to REAPER's own file-import drop target rather than to our
// gesture law. NOT confirmed by experiment — inferred from REAPER's handling of external file
// drops, and the inferred outcome (an item at the drop point) coincides with what our own
// arrange path would have done.
// arrange path would have done. This is now the ONLY way to see that import ghost mid-drag;
// reaching it from a surface still inside REAPER was the defect pointerOffHost closed.
void handOffToOs() {
// Resolve BEFORE tearing anything down (the resolver reads the live drag payload), then let
// the pure rule couple the two side effects: an unresolvable payload leaves the internal