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
+40
View File
@@ -0,0 +1,40 @@
#pragma once
// window_origin — whose code implements a window, and whether the window chain under the pointer
// is a plugin's own editor rather than REAPER's chrome. The shell reads the facts (owning process,
// the module holding the window CLASS's procedure); this module decides, so the rule is provable
// without a DAW. Evidence feeding ui::DropContext::pointerOverPluginEditor.
#include <string>
#include <vector>
namespace reasampler::ui {
enum class WindowCode {
Host, // the host executable itself — REAPER's own chrome and containers
System, // an OS module (standard controls, dialog class): says nothing about who placed it
Plugin, // any other code — a plugin binary, or a window of another process (bridged plugin)
Unknown, // the procedure resolved to no loaded module — no evidence either way
};
// What the shell observed about ONE window. The CLASS procedure, not the instance one, because an
// extension subclassing a REAPER window swaps only the instance procedure — reading that would
// make REAPER's own chrome look like foreign code.
struct WindowFacts {
bool otherProcess = false; // the window belongs to a process other than the host's
bool procResolved = false; // the class procedure lies inside a loaded module
bool hostModule = false; // ...and that module is the host executable
std::string modulePath; // that module's file path, UTF-8
};
// `systemRoot` is the OS directory (GetSystemWindowsDirectory); a module beneath it is System.
// An empty root makes every non-host module Unknown rather than Plugin: without it a standard
// button in REAPER's chrome would be indistinguishable from plugin code.
WindowCode classifyWindowCode(const WindowFacts& facts, const std::string& systemRoot);
// `chain` runs from the window under the pointer up to and including its top-level root. True iff
// any window in it is Plugin code: a plugin's editor sits beneath REAPER's container, and a
// standard control INSIDE that editor is still under the plugin's own window. Unknown is never
// proof — the hand-off this gates is modal, so it needs positive evidence.
bool isPluginEditorChain(const std::vector<WindowCode>& chain);
} // namespace reasampler::ui