// extension-reaper-macos — bare-minimum REAPER extension. // // Loads, registers one test action ("extension-reaper-macos: Hello"), and // prints a confirmation to the REAPER console. // // Top-level story: // - We tell the compiler which REAPER functions we need a box for. // - REAPER loads our .dylib and calls our one required function, once. // - We check we're actually loading, not unloading, and the version matches. // - We fill our function box(es) with their real address. // - We describe a new action and hand it to REAPER — it creates it, gives // us back a number. // - We subscribe our own function to REAPER's "every action, any trigger" // stream. // - We print "loaded successfully" — proof of everything up to that point, // but NOT proof the action/subscription actually works. // - Setup's done. We now just sit in memory, doing nothing. // - Later, any action anywhere in REAPER (click, key, MIDI, OSC) calls our // subscribed function. // - It checks if the action was ours. If yes, react. If no, ignore. // We tell the compiler which REAPER functions we need a box for. This file // owns the real storage (REAPERAPI_IMPLEMENT), so this list has to cover // everything used anywhere in the project, including tracking.cpp. #define REAPERAPI_MINIMAL #define REAPERAPI_WANT_ShowConsoleMsg #define REAPERAPI_WANT_CountAutomationItems #define REAPERAPI_WANT_GetSetAutomationItemInfo #define REAPERAPI_WANT_CountTracks #define REAPERAPI_WANT_GetTrack #define REAPERAPI_WANT_CountTrackEnvelopes #define REAPERAPI_WANT_GetTrackEnvelope #define REAPERAPI_IMPLEMENT #include "reaper_plugin.h" #include "reaper_plugin_functions.h" #include "tracking.h" #include "socket.h" #include static int action1_id = 0; static const char *kAction1IdStr = "EXTENSION_REAPER_MACOS_HELLO"; static const char *kAction1Name = "Hello Action List Display Name"; // Testing hookcommand2 again (REAPER's docs specifically recommend it for // custom_action-registered actions), this time with debug prints kept in. static bool Action1(KbdSectionInfo *sec, int command, int val, int val2, int relmode, HWND hwnd) { // Unconditional — proves whether this callback is being reached at all, // and for which command IDs, regardless of whether it's ours. char buf[128]; snprintf(buf, sizeof(buf), "[extension-reaper-macos] Action1 called: command=%d action1_id=%d\n", command, action1_id); ShowConsoleMsg(buf); if (command == action1_id) { ShowConsoleMsg("[extension-reaper-macos] Hello action triggered\n"); return true; } ShowConsoleMsg("[extension-reaper-macos] ignored — not ours\n"); return false; } // REAPER loads our .dylib and calls this once. extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(REAPER_PLUGIN_HINSTANCE hInstance, reaper_plugin_info_t *rec) { // Check we're actually loading, not unloading, and the version matches. if (!rec) return 0; if (rec->caller_version != REAPER_PLUGIN_VERSION) return 0; // Fill our function box(es) with their real address. if (REAPERAPI_LoadAPI(rec->GetFunc) != 0) return 0; // Describe a new action and hand it to REAPER — it creates it, gives us // back a number, which we save. custom_action_register_t actionDescription = { 0, kAction1IdStr, kAction1Name, NULL, }; action1_id = rec->Register("custom_action", &actionDescription); // Verify registration actually succeeded — Register returns 0 on failure, // and we've never checked that until now. char buf[128]; snprintf(buf, sizeof(buf), "[extension-reaper-macos] action1_id = %d\n", action1_id); ShowConsoleMsg(buf); // Subscribe Action1 via hookcommand2 — REAPER's documented pairing for // custom_action-registered actions specifically. rec->Register("hookcommand2", (void *)Action1); // Envelope/automation-item polling now lives in tracking.cpp. RegisterTracking(rec); // Basic UDP socket listener lives in socket.cpp. RegisterSocket(rec); // Print "loaded successfully" — proof of everything above, but NOT proof // the action/subscription actually works. ShowConsoleMsg("[extension-reaper-macos] loaded successfully\n"); // Setup's done. Return 1 = "keep me loaded." We now just sit in memory, // doing nothing, until Action1 gets called later. return 1; }