faa513cafb
Panel drag no longer misfires on SWELL's always-zero wParam; stable/beta VST editors register distinct window classes against their own module instead of colliding on REAPER's; own-module basename matching moves to a tested pure helper.
92 lines
6.0 KiB
C++
92 lines
6.0 KiB
C++
#pragma once
|
|
// drag_out_win — the host/OS boundary of native OS drag-out: whether the pointer has
|
|
// crossed it (left the host process, or reached a plugin's own editor), whether the OS is
|
|
// ready to take the drag, and the hand-off itself, which
|
|
// gives a resolved existing-file path list to the OS's drag-drop machinery so the user can
|
|
// drop bank samples into Explorer / another app / another DAW. The pure gesture-boundary
|
|
// decision + path-list assembly live in drag_out.*.
|
|
//
|
|
// COPY-ONLY is STRUCTURAL on Windows: DoDragDrop's effect mask is DROPEFFECT_COPY
|
|
// alone — MOVE is never offered, so no target can pull a file out of the bank folder
|
|
// (only prune deletes bank bytes). macOS/Linux route through SWELL's file-list drag,
|
|
// which exposes no per-source effect mask, so there the copy-only guarantee rests on
|
|
// SWELL's copy semantics rather than an explicit mask.
|
|
//
|
|
// NON-DESTRUCTIVE: initiating a drag reads nothing but the path list; a cancelled or
|
|
// failed drag mutates no sample/index/selection.
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
struct HWND__; // avoid dragging windows.h into every includer; the shell casts as needed.
|
|
|
|
namespace reasampler {
|
|
|
|
// How the OS drag ended, as far as the caller must act on it. Whether a Concluded drag was
|
|
// accepted is not reported — nothing acts on it.
|
|
enum class OsDragEnd {
|
|
Concluded, // dropped, cancelled, or failed to start: the gesture is over
|
|
Withdrawn, // ui::OsDragTurn::Withdraw — the button is still down
|
|
WithdrawnAtRelease, // ui::OsDragTurn::WithdrawAtRelease — the button came up over REAPER
|
|
};
|
|
|
|
// `absolutePaths` must already be resolved/existing/de-duped (drag_out::assemblePathList
|
|
// output). No-op when empty. BLOCKING on Windows: OLE DoDragDrop runs its own modal
|
|
// message loop, re-asking `lawHandsOff` (the gesture law at the current pointer) on every
|
|
// IDropSource::QueryContinueDrag and withdrawing per ui::decideOsDragTurn. macOS/Linux: SWELL's
|
|
// file drag is fire-and-forget with no such hook, so it always reports Concluded — the hand-off
|
|
// stays irreversible there.
|
|
OsDragEnd initiateDragOut(HWND__* panelHwnd, const std::vector<std::string>& absolutePaths,
|
|
bool (*lawHandsOff)());
|
|
|
|
// Side-effect-free readiness probe for initiateDragOut. Call BEFORE tearing down internal drag
|
|
// state — an OS that isn't ready (OLE unavailable, HDROP build failure) must not consume the
|
|
// gesture like an empty payload would. Windows re-runs the OLE-init + HDROP checks and frees the
|
|
// probe HGLOBAL immediately; SWELL exposes no probe, so macOS/Linux reduces to the non-empty check.
|
|
bool canInitiateDragOut(const std::vector<std::string>& absolutePaths);
|
|
|
|
// True iff a window belonging to the host (REAPER) process sits under the screen point — the
|
|
// POSITIVE proof ui::DropContext::pointerOffHost is the negation of (that member owns the
|
|
// evidence rule). Nearly every REAPER surface qualifies whether or not GetThingFromPoint can
|
|
// name it, because the extension runs inside that process.
|
|
// The exception, and it ships: a plugin set to "Run as separate/dedicated process" draws its
|
|
// UI out of reaper_host32/64.exe, and WindowFromPoint descends to the deepest child — so this
|
|
// reports false over a bridged plugin's editor although the pointer never left REAPER. The
|
|
// law's hand-off gate absorbs that by also requiring REAPER's own hit-test to name nothing.
|
|
// Side-effect-free; safe on every mouse-move.
|
|
bool pointerOverHostWindow(int screenX, int screenY);
|
|
|
|
// True iff plugin code implements the window under the screen point or one of its ancestors —
|
|
// the POSITIVE proof behind ui::DropContext::pointerOverPluginEditor. Windows reads, per window
|
|
// from the hit window up to its root, the owning process and the module holding the window
|
|
// CLASS's procedure, and lets ui::window_origin decide. The failure modes, all toward REAPER's
|
|
// own outcome (the instrument drop) rather than toward OLE:
|
|
// - a plugin that draws straight into the parent REAPER hands it, creating no window of its
|
|
// own, leaves nothing of its code in the chain;
|
|
// - a plugin binary installed beneath the Windows directory reads as an OS module;
|
|
// - a class procedure that resolves to no module (a charset-mismatch handle) is no evidence;
|
|
// - an all-#32770 dialog-based editor (the generic Windows dialog class) resolves to no
|
|
// recognizable plugin module — falls to REAPER's outcome;
|
|
// - a .NET/WPF editor's class procedure is a CLR-thunked address with no resolvable module —
|
|
// also falls to REAPER's outcome; `[verify]`;
|
|
// - our own ReaSampler 9000 module (this channel's reasampler_9000[_beta].vst3, matched by
|
|
// basename) also falls to REAPER's outcome, deliberately — see ui::WindowFacts::ownModule;
|
|
// - ReaSampler 9000 itself run bridged/in a separate process reads as Plugin, same as any other
|
|
// bridged plugin — banner (the OS hand-off fires over our own editor), harmless;
|
|
// - a renamed ReaSampler 9000 binary (the basename no longer matches ownVstFileName()) misses
|
|
// the exemption and reads as Plugin — banner, harmless.
|
|
// The opposite direction is unguarded: a third-party hook that replaces REAPER's own chrome
|
|
// class procedure via SetClassLongPtr(GCLP_WNDPROC) would make REAPER's own window read as
|
|
// Plugin — toward OLE instead of away from it.
|
|
// [verify — DAW] that REAPER's GetThingFromPoint names an fx_* token over the plugin's own child
|
|
// window — the law requires it, so an unnamed editor would refuse rather than hand off.
|
|
// [verify — DAW] that OLE never walks up to an ancestor drop target (e.g. REAPER's own file
|
|
// import) when the plugin editor's window registers no IDropTarget of its own.
|
|
// [verify — DAW] that GetThingFromPoint cannot name a REAPER region (e.g. an fx_* token) when a
|
|
// foreign window overlaps it at the same screen point — this fn trusts WindowFromPoint's hit
|
|
// over whatever GetThingFromPoint separately reports.
|
|
// SWELL: always false (no plugin-editor hand-off off Windows). Side-effect-free.
|
|
bool pointerOverPluginEditor(int screenX, int screenY);
|
|
|
|
} // namespace reasampler
|