Hand a card dragged onto a plugin's own editor to the OS as a file drop; withdraw the OLE drag when the pointer returns to REAPER

A code-ownership proof (window class procedure's module, via the pure window_origin) plus REAPER's FX-surface token routes a plugin editor to OLE; FX chrome keeps the instrument drop. The OLE loop re-asks the law each turn, so passing a plugin editor stays reversible.
This commit is contained in:
2026-09-25 13:39:39 -04:00
parent 9ff34b0a9e
commit fe1aec5925
14 changed files with 720 additions and 91 deletions
+21 -8
View File
@@ -24,17 +24,24 @@ DropClass decideDropClass(int px, int py, const PanelClientRect& client,
if (!ctx.drag.dragging || !ctx.drag.hasArmedSamples) return DropClass::None;
if (insideClient(px, py, client)) return DropClass::Internal;
// The ONE route to OsHandoff, ahead of the surface switch, and it needs BOTH halves. The
// shell's ownership proof alone is not sufficient: REAPER runs a bridged plugin's UI in a
// separate process, so a window failing that test can still be REAPER's own UI. A recognised
// token therefore VETOES the hand-off instead of producing one — a veto can only move the
// answer toward staying inside REAPER, so pointerOffHost remains the only member whose true
// value can reach OLE. An off-host point REAPER DID name falls through to that surface's own
// outcome below: over a bridged FX window that is the instrument drop, as it is for a native
// one.
// The two routes to OsHandoff, both ahead of the surface switch, each needing BOTH halves.
//
// Leaving REAPER. The process proof alone is not sufficient: REAPER runs a bridged plugin's UI
// in a separate process, so a window failing that test can still be inside REAPER. A
// recognised token therefore VETOES this route instead of producing it — a veto can only move
// the answer toward staying inside REAPER. An off-host point REAPER DID name falls through to
// the second route, then to that surface's own outcome below.
if (ctx.pointerOffHost && ctx.surface == ReaperSurface::Other && !ctx.haveTrack)
return DropClass::OsHandoff;
// A plugin's own editor, with the reverse pairing: REAPER's naming is REQUIRED here, not a
// veto. The code proof alone would hand off over any extension window docked in REAPER; the
// FX-surface token confines it to FX windows, and the proof picks the plugin's editor out of
// that window's REAPER chrome, which keeps its instrument outcome below. A bridged editor
// lands here too — the token that vetoed the first route admits this one.
if (ctx.pointerOverPluginEditor && ctx.surface == ReaperSurface::FxSurface)
return DropClass::OsHandoff;
switch (ctx.surface) {
case ReaperSurface::TrackPanel:
case ReaperSurface::FxSurface:
@@ -100,4 +107,10 @@ OsHandoff decideOsHandoff(const std::vector<std::string>& paths) {
return out;
}
OsDragTurn decideOsDragTurn(bool escapePressed, bool buttonDown, bool lawHandsOff) {
if (escapePressed) return OsDragTurn::Cancel;
if (buttonDown) return lawHandsOff ? OsDragTurn::Continue : OsDragTurn::Withdraw;
return lawHandsOff ? OsDragTurn::Drop : OsDragTurn::WithdrawAtRelease;
}
} // namespace reasampler::ui