Fix drag-handoff bugs: gate FX re-resolve on outside-panel, cache unresolvable OS-drag verdict, block double FX-add retry
This commit is contained in:
@@ -64,10 +64,11 @@ HGLOBAL buildHDrop(const std::vector<std::string>& paths) {
|
||||
|
||||
// COM reference counts MUST be interlocked. A CF_HDROP target is free to marshal the data
|
||||
// object into another apartment and finish the copy on a background thread AFTER DoDragDrop
|
||||
// has returned (Explorer's async file copy does exactly this). A plain ++/-- there races the
|
||||
// source thread's post-DoDragDrop Release: one lost increment destroys the object — and with
|
||||
// it the source HGLOBAL — before the target reads it, and the drop lands with no file. That
|
||||
// race is intermittent and a retry usually wins it; do not "simplify" these back.
|
||||
// has returned; Explorer's async file copy is suspected to do exactly this, though that
|
||||
// specific behavior is not confirmed by experiment. A plain ++/-- there races the source
|
||||
// thread's post-DoDragDrop Release: one lost increment destroys the object — and with it the
|
||||
// source HGLOBAL — before the target reads it, and the drop lands with no file. That race is
|
||||
// intermittent and a retry usually wins it; do not "simplify" these back.
|
||||
inline ULONG comAddRef(volatile LONG& refs) {
|
||||
return static_cast<ULONG>(InterlockedIncrement(&refs));
|
||||
}
|
||||
@@ -234,6 +235,15 @@ bool initiateDragOut(HWND__* /*panelHwnd*/, const std::vector<std::string>& abso
|
||||
return hr == DRAGDROP_S_DROP && effect == DROPEFFECT_COPY;
|
||||
}
|
||||
|
||||
bool canInitiateDragOut(const std::vector<std::string>& absolutePaths) {
|
||||
if (absolutePaths.empty()) return false;
|
||||
if (!ensureOleForThisThread()) return false;
|
||||
HGLOBAL hdrop = buildHDrop(absolutePaths);
|
||||
if (!hdrop) return false;
|
||||
GlobalFree(hdrop); // probe only — initiateDragOut builds its own on the real attempt
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace reasampler
|
||||
|
||||
#else // ---- macOS / Linux (SWELL) -----------------------------------------------------
|
||||
@@ -260,6 +270,13 @@ bool initiateDragOut(HWND__* panelHwnd, const std::vector<std::string>& absolute
|
||||
return true; // fire-and-forget; SWELL owns the drag from here (no accept/cancel return)
|
||||
}
|
||||
|
||||
// SWELL exposes no readiness probe ahead of SWELL_InitiateDragDropOfFileList (which itself
|
||||
// reports no accept/cancel outcome) — the non-empty check is the only thing knowable in
|
||||
// advance on this platform.
|
||||
bool canInitiateDragOut(const std::vector<std::string>& absolutePaths) {
|
||||
return !absolutePaths.empty();
|
||||
}
|
||||
|
||||
} // namespace reasampler
|
||||
|
||||
#endif
|
||||
|
||||
@@ -26,4 +26,13 @@ namespace reasampler {
|
||||
// (DROPEFFECT_COPY); the return is advisory — a failed drag surfaces no error.
|
||||
bool initiateDragOut(HWND__* panelHwnd, const std::vector<std::string>& absolutePaths);
|
||||
|
||||
// Cheap, side-effect-free readiness probe: true iff initiateDragOut would actually be able to
|
||||
// START a drag for `absolutePaths` right now. Call this BEFORE tearing down internal drag
|
||||
// state (release capture, clear drag fields) — an OS that isn't ready (OLE unavailable, or the
|
||||
// path list can't build a CF_HDROP) must not consume the gesture the same way an empty payload
|
||||
// would. On Windows this re-runs the same OLE-init + HDROP-build checks initiateDragOut does,
|
||||
// freeing the probe HGLOBAL immediately; SWELL exposes no such probe, so macOS/Linux reduces to
|
||||
// the non-empty check alone.
|
||||
bool canInitiateDragOut(const std::vector<std::string>& absolutePaths);
|
||||
|
||||
} // namespace reasampler
|
||||
|
||||
@@ -104,16 +104,23 @@ bool loadInstrumentOntoTrack(MediaTrack* track, const std::vector<std::uint8_t>&
|
||||
const std::string fxName = "VST3:" + vstPluginName();
|
||||
|
||||
// An EXPLICIT top-level insertion position (instantiate <= -1000 IS the position, -1000
|
||||
// = first in chain), not the bare -1. Both always create a new instance; the bare form
|
||||
// additionally leaves placement to REAPER's ambient FX-chain insert point, which a drop
|
||||
// onto an FX container/chain-window moves — so the index handed to TrackFX_SetPreset and
|
||||
// the instance just created stop denoting the same FX and the capture never lands. The
|
||||
// bare-form retry keeps the reference path alive if the positional form is ever refused.
|
||||
// = first in chain), not the bare -1 — this form is documented in the SDK header. Both
|
||||
// always create a new instance; the bare form additionally leaves placement to REAPER's
|
||||
// ambient FX-chain insert point, which a drop onto an FX container/chain-window is
|
||||
// suspected (unconfirmed by experiment) to move — if so, the index handed to
|
||||
// TrackFX_SetPreset and the instance just created would stop denoting the same FX and the
|
||||
// capture would never land. The bare-form retry keeps the reference path alive if the
|
||||
// positional form is ever refused.
|
||||
// recFX = false: a normal track FX chain instance, not a record/monitoring FX.
|
||||
const int insertPos = TrackFX_GetCount(track);
|
||||
int fxIndex = TrackFX_AddByName(track, fxName.c_str(), /*recFX=*/false,
|
||||
/*instantiate=*/-1000 - insertPos);
|
||||
if (fxIndex < 0)
|
||||
// Only retry with the bare form when the chain is PROVABLY unchanged (count still
|
||||
// insertPos): a negative return with the count grown means the positional add DID create
|
||||
// an instance and just reported -1 — retrying then would add a SECOND instance, leaving
|
||||
// the first orphaned (no preset applied, unreachable for rollback), which is exactly the
|
||||
// all-or-nothing violation this contract forbids.
|
||||
if (fxIndex < 0 && TrackFX_GetCount(track) == insertPos)
|
||||
fxIndex = TrackFX_AddByName(track, fxName.c_str(), /*recFX=*/false, /*instantiate=*/-1);
|
||||
|
||||
// u8string() gives UTF-8 bytes on MSVC (not ACP-converted), so a temp dir under
|
||||
|
||||
Reference in New Issue
Block a user