11#include " core/atkaudio/atkaudio.h"
22
33#include < atkaudio/AudioProcessorGraphMT/RealtimeThreadPool.h>
4- #include < atkaudio/JuceApp.h>
54#include < atkaudio/LookAndFeel.h>
6- #include < atkaudio/MessagePump.h>
75#include < atkaudio/ModuleInfrastructure/AudioServer/AudioServer.h>
86#include < atkaudio/ModuleInfrastructure/MidiServer/MidiServer.h>
7+ #include < atkaudio/ObsJucePluginFormatLifecycle.h>
98#include < atkaudio/UpdateCheck.h>
109
1110#include < juce_audio_utils/juce_audio_utils.h>
1817#include < QWindow>
1918#endif
2019
20+ #include < obs-module.h>
2121#include < obs-frontend-api.h>
2222
2323UpdateCheck* updateCheck = nullptr ;
24- atk::MessagePump* g_messagePump = nullptr ;
2524
2625// Store Qt main window handle for per-instance parent creation (lazy-initialized)
2726static void * g_qtMainWindowHandle = nullptr ;
2827static bool g_qtMainWindowInitialized = false ;
2928
30- void atk::create ( )
29+ static juce::File getFallbackSettingsFile ( const juce::String& name )
3130{
32- // Set createInstance to make JUCE think this is a standalone app.
33- // This enables per-monitor DPI awareness on Windows via setDPIAwareness().
34- #ifndef JUCE_MAC
35- juce::JUCEApplicationBase::createInstance = []() -> juce::JUCEApplicationBase* { return nullptr ; };
36- #endif
37- juce::initialiseJuce_GUI ();
31+ juce::PropertiesFile::Options opts;
32+ opts.applicationName = name;
33+ opts.filenameSuffix = " settings" ;
34+ opts.osxLibrarySubFolder = " Application Support" ;
35+ opts.folderName = " atkAudio Plugin" ;
36+ return opts.getDefaultFile ();
37+ }
38+
39+ juce::File atk::getSettingsFile (const juce::String& name)
40+ {
41+ auto * module = obs_current_module ();
42+ if (module == nullptr )
43+ return getFallbackSettingsFile (name);
44+
45+ auto filename = name + " .settings" ;
46+ char * obsPath = obs_module_get_config_path (module , filename.toRawUTF8 ());
47+ if (obsPath == nullptr )
48+ return getFallbackSettingsFile (name);
49+
50+ juce::File result (obsPath);
51+ bfree (obsPath);
52+ return result;
53+ }
3854
39- juce::MessageManager::getInstance ()->setCurrentThreadAsMessageThread ();
55+ bool atk::create ()
56+ {
57+ auto & lifecycle = atk::ObsJucePluginFormatLifecycle::getInstance ();
58+ if (!lifecycle.initialize ())
59+ {
60+ DBG (" create: failed to initialize OBS JUCE format lifecycle" );
61+ return false ;
62+ }
4063
4164 // Initialize LookAndFeel singleton
4265 juce::SharedResourcePointer<atk::LookAndFeel> lookAndFeel;
4366
44- // Sync OBS theme colors to JUCE LookAndFeel
67+ // Sync OBS theme colors to JUCE LookAndFeel.
68+ // Bypass only true Debug builds (_DEBUG without NDEBUG), but keep this enabled
69+ // in RelWithDebInfo where _DEBUG may still be defined by toolchain settings.
70+ #if !(defined(_DEBUG) && !defined(NDEBUG))
4571 getQtMainWindowHandle ();
72+ #endif
4673
4774 // Initialize MIDI server
4875 if (auto * midiServer = atk::MidiServer::getInstance ())
@@ -55,49 +82,40 @@ void atk::create()
5582 // Initialize RealtimeThreadPool synchronously so it's ready when filters are created
5683 if (auto * threadPool = atk::RealtimeThreadPool::getInstance ())
5784 threadPool->initialize ();
85+
86+ return true ;
5887}
5988
6089void atk::pump ()
6190{
62- #if JUCE_LINUX
63- // On Linux, we need to pump the JUCE message loop
64- // Use dispatchPendingMessages() instead of runDispatchLoopUntil() to avoid
65- // conflicts with Qt's event loop - we just want to process pending JUCE messages
66- // without polling/blocking for new ones
67- if (auto * mm = juce::MessageManager::getInstanceWithoutCreating ())
68- {
69- // Only dispatch if we're on the message thread
70- if (mm->isThisTheMessageThread ())
71- {
72- // Process any pending async callbacks
73- mm->runDispatchLoopUntil (0 ); // 0ms = just process pending, don't wait
74- }
75- }
76- #endif
91+ atk::ObsJucePluginFormatLifecycle::getInstance ().pumpPendingMessages ();
7792}
7893
79- void atk::startMessagePump (QObject* qtParent)
94+ bool atk::startMessagePump (QObject* qtParent)
8095{
81- #if JUCE_LINUX
82- // On Linux, we need a Qt timer-based message pump for JUCE
83- if (g_messagePump)
96+ auto & lifecycle = atk::ObsJucePluginFormatLifecycle::getInstance ();
97+ if (!lifecycle.startMessagePump (qtParent))
8498 {
85- DBG (" startMessagePump: MessagePump already started " );
86- return ;
99+ DBG (" startMessagePump: failed to start OBS JUCE format message pump " );
100+ return false ;
87101 }
88102
89- g_messagePump = new atk::MessagePump (qtParent);
90- #else
91- // On macOS and Windows, we don't need a message pump - JUCE integrates with native event loops
92- (void )qtParent;
93- #endif
103+ return true ;
94104}
95105
96- void atk::destroy ()
106+ bool atk::isReady ()
97107{
98- juce::MessageManager::getInstance ()->setCurrentThreadAsMessageThread ();
108+ return atk::ObsJucePluginFormatLifecycle::getInstance ().isReady ();
109+ }
99110
100- g_messagePump = nullptr ;
111+ bool atk::isShuttingDown ()
112+ {
113+ return atk::ObsJucePluginFormatLifecycle::getInstance ().isShuttingDown ();
114+ }
115+
116+ void atk::destroy ()
117+ {
118+ auto & lifecycle = atk::ObsJucePluginFormatLifecycle::getInstance ();
101119
102120 if (auto * midiServer = atk::MidiServer::getInstance ())
103121 {
@@ -117,7 +135,7 @@ void atk::destroy()
117135 atk::RealtimeThreadPool::deleteInstance ();
118136 }
119137
120- juce::shutdownJuce_GUI ();
138+ lifecycle. shutdown ();
121139}
122140
123141void atk::update ()
0 commit comments