7d4f8a4cd2
extension-reaper-macos: split main.cpp into tracking.cpp (polls selected envelope/automation items, reads/writes D_BASELINE) and socket.cpp (basic UDP listener, non-blocking, drains all pending packets per tick). Nudging now applies to every selected automation item across every track in the project, not just the first one found. app-desktop-macos: new "Extension" column (mirrors the OSC/Remote columns) to enable/disable the UDP connection and point it at a host/port, plus a log window. FocusFaderWidget gets an OSC/Ext output-mode toggle (mirrors TransportWidget's MIDI/OSC toggle) so a fader can drive the extension directly instead of/alongside OSC. Also renamed the Remote/Tablet buttons for clarity and split their status indicator into its own column. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
113 lines
4.2 KiB
C++
113 lines
4.2 KiB
C++
// 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 <cstdio>
|
|
|
|
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;
|
|
}
|