diff --git a/Misc/Macros.cpp b/Misc/Macros.cpp index a48da1395..61c43049b 100644 --- a/Misc/Macros.cpp +++ b/Misc/Macros.cpp @@ -30,47 +30,47 @@ #include "stdafx.h" #include "Macros.h" -#ifdef _WIN32 // Sorry OSX users, win32 only. +// WaitUntil is not implemented for swell-generic (sws_util_generic.cpp) +#if defined(_WIN32) || defined(__APPLE__) +# define HAS_WAITACTION void WaitAction(COMMAND_T* t) { - int iPlayState = GetPlayState(); - if (iPlayState && !(iPlayState & 2)) // playing/recording, not paused - { - int iMeasure; - double dStart = GetPlayPosition(); - double dBeat = TimeMap2_timeToBeats(NULL, dStart, &iMeasure, NULL, NULL, NULL); - double dStop; - switch (t->user) - { - case 0: // Bar - iMeasure++; - dBeat = 0.0; - dStop = TimeMap2_beatsToTime(NULL, dBeat, &iMeasure); - break; - case 1: - dBeat = (double)((int)dBeat + 1); - dStop = TimeMap2_beatsToTime(NULL, dBeat, &iMeasure); - break; - case 2: - double t1, t2; - GetSet_LoopTimeRange(false, true, &t1, &t2, false); - if (t1 == t2) - return; - dStop = t2; - break; - } + const int iPlayState = GetPlayState(); + if (!iPlayState || (iPlayState & 2)) // playing/recording, not paused + return; - // Check for cursor going past stop, user stopping, and looping around - while(GetPlayPosition() < dStop && GetPlayState() && GetPlayPosition() >= dStart) - { - // Keep the UI updating - MSG msg; - while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) - DispatchMessage(&msg); - Sleep(1); - } + struct WaitData { double dStart, dStop; }; + WaitData wd; + wd.dStart = GetPlayPosition(); + int iMeasure; + double dBeat = TimeMap2_timeToBeats(NULL, wd.dStart, &iMeasure, NULL, NULL, NULL); + switch (t->user) + { + case 0: // Bar + iMeasure++; + dBeat = 0.0; + wd.dStop = TimeMap2_beatsToTime(NULL, dBeat, &iMeasure); + break; + case 1: + dBeat = (double)((int)dBeat + 1); + wd.dStop = TimeMap2_beatsToTime(NULL, dBeat, &iMeasure); + break; + case 2: + double t1, t2; + GetSet_LoopTimeRange(false, true, &t1, &t2, false); + if (t1 == t2) + return; + wd.dStop = t2; + break; } + + // Check for cursor going past stop, user stopping, and looping around + WaitUntil([](void *data) { + WaitData *wd = static_cast(data); + const double pos = GetPlayPosition(); + return !GetPlayState() || pos >= wd->dStop || pos < wd->dStart; + }, &wd); } //!WANT_LOCALIZE_1ST_STRING_BEGIN:sws_actions @@ -83,12 +83,13 @@ static COMMAND_T g_commandTable[] = { {}, LAST_COMMAND, }, // Denote end of table }; //!WANT_LOCALIZE_1ST_STRING_END +#endif int MacrosInit() { +#ifdef HAS_WAITACTION SWSRegisterCommands(g_commandTable); +#endif return 1; } - -#endif // #ifdef _WIN32 diff --git a/Misc/Misc.cpp b/Misc/Misc.cpp index b2f56b583..acc6cfb29 100644 --- a/Misc/Misc.cpp +++ b/Misc/Misc.cpp @@ -71,10 +71,8 @@ int MiscInit() return 0; if (!ItemSelInit()) return 0; -#ifdef _WIN32 if (!MacrosInit()) return 0; -#endif if (!ProjPrefsInit()) return 0; if (!RecordCheckInit()) diff --git a/sws_util.cpp b/sws_util.cpp index 0cb500ed3..c9560f3df 100644 --- a/sws_util.cpp +++ b/sws_util.cpp @@ -723,3 +723,15 @@ const char* SWS_GetSourceFileName(PCM_source* src) src = src->GetSource(); return src ? src->GetFileName() : nullptr; } + +#ifdef _WIN32 +void WaitUntil(bool(*predicate)(void *), void *data) +{ + while (!predicate(data)) { + MSG msg; + while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) + DispatchMessage(&msg); + Sleep(1); + } +} +#endif diff --git a/sws_util.h b/sws_util.h index 74894fe61..59eed1510 100644 --- a/sws_util.h +++ b/sws_util.h @@ -264,6 +264,7 @@ void SWS_GetSelectedMediaItems(WDL_TypedBuf* buf); void SWS_GetSelectedMediaItemsOnTrack(WDL_TypedBuf* buf, MediaTrack* tr); int SWS_GetModifiers(); bool SWS_IsWindow(HWND hwnd); +void WaitUntil(bool(*)(void *), void *); // cannot use std::function on pre-c++11 macOS // Localization, sws_util.cpp #define _SWS_LOCALIZATION @@ -278,4 +279,3 @@ TrackEnvelope* SWS_GetTrackEnvelopeByName(MediaTrack* track, const char* envname bool RegisterExportedFuncs(reaper_plugin_info_t* _rec); void UnregisterExportedFuncs(); bool RegisterExportedAPI(reaper_plugin_info_t* _rec); - diff --git a/sws_util.mm b/sws_util.mm index 7149113dd..b27b2ac6e 100644 --- a/sws_util.mm +++ b/sws_util.mm @@ -237,3 +237,39 @@ void SetMenuItemSwatch(HMENU hMenu, UINT pos, int iSize, COLORREF color) NSRectFill(NSMakeRect(0, 0, size.width, size.height)); [item.image unlockFocus]; } + +static bool g_miscTimerRunning; +void TestMiscTimerRunning() +{ + g_miscTimerRunning = true; + plugin_register("-timer", reinterpret_cast(&TestMiscTimerRunning)); +} + +void WaitUntil(bool(*predicate)(void *), void *data) +{ + g_miscTimerRunning = false; + plugin_register("timer", reinterpret_cast(&TestMiscTimerRunning)); + + bool firstRun = true; + while (!predicate(data)) { + // Waiting 30 ms instead of 1 ms (like on Windows) to match the normal + // frequency of REAPER's misc timer + constexpr int DEFER_TIMER_SPEED = 30, + MISC_TIMER = 0x29A; + + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:DEFER_TIMER_SPEED / 1000.f]]; + + // REAPER's misc timer (based on NSTimer), responsible for many many things + // from UI updates to updating GetPlayPosition(), won't be re-entered in the + // event loop iterations above while if we're blocking it's handler. + // + // Wait until we're 100% sure the misc timer is suspended + // (by waiting twice its normal fire rate) before manually triggering it. + if (firstRun) + firstRun = false; + else if (!g_miscTimerRunning) { + plugin_register("-timer", reinterpret_cast(&TestMiscTimerRunning)); + PostMessage(GetMainHwnd(), WM_TIMER, MISC_TIMER, 0); + } + } +} diff --git a/sws_util_generic.cpp b/sws_util_generic.cpp index 74c19b7b1..8eb39b536 100644 --- a/sws_util_generic.cpp +++ b/sws_util_generic.cpp @@ -67,3 +67,8 @@ HCURSOR SWS_Cursor::makeFromData() void mouse_event(DWORD dwFlags, DWORD dx, DWORD dy, DWORD dwData, ULONG_PTR dwExtraInfo) { } + +// not implemented, would be used by WaitAction in Macros.cpp +// void waitUntil(bool(*)(void *), void *) +// { +// }