One undo-block opener for the whole tree: fourteen sites route through it, and the reclaim span can no longer swallow a user's act

The drain refuses a tick inside a foreign block; applyMode wakes the reclaimer
before it opens the span.
This commit is contained in:
2026-08-07 16:38:11 -04:00
parent b2f6b3f1d0
commit b7b93a7ac3
25 changed files with 561 additions and 313 deletions
+5 -25
View File
@@ -1,7 +1,8 @@
// bake_land.cpp — see bake_land.h. main.cpp owns the API pointers; this TU gets them
// extern. REAPER symbols used here (EnumProjects, EnumProjExtState, GetProjExtState,
// SetProjExtState, ShowConsoleMsg, Undo_BeginBlock2/EndBlock2) are verified against
// vendor/reaper-sdk/sdk/reaper_plugin_functions.h. Landing one request is bake_landing's.
// SetProjExtState, ShowConsoleMsg) are verified against
// vendor/reaper-sdk/sdk/reaper_plugin_functions.h. Landing one request is
// bake_landing's; the undo block is shell/undo's.
#include "shell/capture/bake_land.h"
@@ -19,6 +20,7 @@
#include "shell/capture/bake_landing.h"
#include "shell/panel/panel_input.h" // bankPanelRefresh
#include "shell/persist/session.h"
#include "shell/undo/undo_block.h" // the ONE undo-block opener
#include "reaper_plugin.h" // UNDO_STATE_MISCCFG
@@ -28,8 +30,6 @@
#define REAPERAPI_WANT_GetProjExtState
#define REAPERAPI_WANT_SetProjExtState
#define REAPERAPI_WANT_ShowConsoleMsg
#define REAPERAPI_WANT_Undo_BeginBlock2
#define REAPERAPI_WANT_Undo_EndBlock2
#include "reaper_plugin_functions.h"
namespace reasampler::capture {
@@ -107,26 +107,6 @@ struct ScannedKey {
wire::BakeKeyOutcome report;
};
// Undo_BeginBlock2/EndBlock2 as stack RAII, the FxBypassGuard discipline: a landing reads a
// whole WAV and hashes it, so a bad_alloc between the two calls would otherwise leave an
// open undo block in the user's project. Closing with an empty description records no undo
// point, which is what every path that landed nothing wants anyway.
class UndoBlock {
public:
UndoBlock() { Undo_BeginBlock2(nullptr); }
~UndoBlock() { Undo_EndBlock2(nullptr, description_.c_str(), flags_); }
void recordPoint(std::string description, int flags) {
description_ = std::move(description);
flags_ = flags;
}
UndoBlock(const UndoBlock&) = delete;
UndoBlock& operator=(const UndoBlock&) = delete;
private:
std::string description_;
int flags_ = 0;
};
// One entry's write-back, and the proof of whether it took. Guarded whole: it encodes into
// a fresh string and readKey grows a buffer toward 16 MB, and a throw escaping here would
// skip the ShowConsoleMsg for EVERY key — the silent console this action exists to make
@@ -206,7 +186,7 @@ void RunResampleBake(ReaSamplerSession& session) {
// the answers already buffered — the exact no-answer-with-a-silent-console this action
// exists to make impossible. UndoBlock is inside the try, so unwinding still closes it.
try {
UndoBlock undo;
UndoBlock undo(nullptr);
for (const OpenProject& open : openProjects()) {
++tally.tabsScanned;
const bool activeTab = static_cast<const void*>(open.proj) == active;