b782fb2731
One switch (REASAMPLER_UNDO_TRACE) gates every line. Traces span open/close reasons, FX writes, UndoBlock depth, timer standoffs, and load-state hooks.
78 lines
3.4 KiB
C++
78 lines
3.4 KiB
C++
// See undo_block.h. Compiled into the reaper_reasampler module; includes
|
|
// reaper_plugin_functions.h without REAPERAPI_IMPLEMENT (main.cpp owns that).
|
|
|
|
#include "shell/undo/undo_block.h"
|
|
|
|
#include <utility>
|
|
|
|
#include "shell/undo/undo_trace.h" // TEMPORARY — see undo_trace.h; revert together
|
|
#include "shell/view/fx_reclaim.h" // fxReclaimEndUndoSpan — the span this must not nest over
|
|
|
|
#define REAPERAPI_MINIMAL
|
|
#define REAPERAPI_WANT_Undo_BeginBlock2
|
|
#define REAPERAPI_WANT_Undo_EndBlock2
|
|
#define REAPERAPI_WANT_ValidatePtr2
|
|
#include "reaper_plugin_functions.h"
|
|
|
|
namespace reasampler {
|
|
|
|
namespace {
|
|
|
|
// Extension-owned nesting depth only — REAPER's own counter is not readable, and the
|
|
// reclaim span is deliberately NOT counted here (it is the thing this stands off).
|
|
int g_depth = 0;
|
|
|
|
} // namespace
|
|
|
|
bool undoBlockOpen() { return g_depth > 0; }
|
|
|
|
UndoBlock::UndoBlock(ReaProject* proj) : project_(proj) {
|
|
// ONLY the outermost block ends the span, which mechanizes the whole nesting rule.
|
|
// At depth 0 the span is the only block that can be open, so ending it mints the
|
|
// switch's point whole, at depth 0, before this block exists. At depth > 0 the
|
|
// enclosing UndoBlock already did it, and a close from here would land at depth
|
|
// 2->1 — minting nothing and hijacking the enclosing block's label instead. That is
|
|
// the same criterion applyMode's reapply is gated on.
|
|
//
|
|
// The whole body is one try: fxReclaimEndUndoSpan's closeSpan copies a std::string
|
|
// and can throw. A throwing constructor runs no destructor, so an uncompensated
|
|
// ++g_depth would stick forever — undoBlockOpen() reads true for the rest of the
|
|
// session, fxReclaimTick refuses every tick, and no later UndoBlock is ever
|
|
// outermost. Roll the depth back on any throw so the type degrades to "this one
|
|
// block failed to open," not "every block after it is now poisoned."
|
|
try {
|
|
if (g_depth++ == 0) fxReclaimEndUndoSpan("UndoBlock ctor");
|
|
Undo_BeginBlock2(proj);
|
|
} catch (...) {
|
|
--g_depth;
|
|
throw;
|
|
}
|
|
REASAMPLER_TRACE("[UndoBlock] CTOR proj=%s depth=%d", proj ? "real" : "active", g_depth);
|
|
}
|
|
|
|
UndoBlock::~UndoBlock() {
|
|
// Depth drops AFTER the SDK call, not before: the close that reaches depth 0 mints a
|
|
// point, and anything re-entered through that must still read a block as open rather
|
|
// than let the drain open a span underneath it.
|
|
//
|
|
// Guarded the same way fx_reclaim.cpp's closeSpan guards its own Undo_EndBlock2, but
|
|
// only for a REAL pinned pointer: `project_ == nullptr` means "the active project",
|
|
// the same convention Undo_BeginBlock2 was handed, and ValidatePtr2 does not accept
|
|
// nullptr as a valid object of any type — guarding it unconditionally would silently
|
|
// stop every `UndoBlock undo(nullptr)` site (most of them) from ever closing. A real
|
|
// pointer, by contrast, can go stale: several call sites allocate freely between open
|
|
// and close, and the project it named may have closed underneath by now.
|
|
if (!project_ || ValidatePtr2(nullptr, project_, "ReaProject*"))
|
|
Undo_EndBlock2(project_, label_.c_str(), mask_);
|
|
--g_depth;
|
|
REASAMPLER_TRACE("[UndoBlock] DTOR label=\"%s\" mask=%d depth=%d",
|
|
label_.empty() ? "(discard)" : label_.c_str(), mask_, g_depth);
|
|
}
|
|
|
|
void UndoBlock::recordPoint(std::string label, int mask) {
|
|
label_ = std::move(label);
|
|
mask_ = mask;
|
|
}
|
|
|
|
} // namespace reasampler
|