aboutsummaryrefslogtreecommitdiff
path: root/Src/Winamp/JSAPI2_CallbackManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Winamp/JSAPI2_CallbackManager.cpp')
-rw-r--r--Src/Winamp/JSAPI2_CallbackManager.cpp567
1 files changed, 567 insertions, 0 deletions
diff --git a/Src/Winamp/JSAPI2_CallbackManager.cpp b/Src/Winamp/JSAPI2_CallbackManager.cpp
new file mode 100644
index 00000000..1272daa5
--- /dev/null
+++ b/Src/Winamp/JSAPI2_CallbackManager.cpp
@@ -0,0 +1,567 @@
+#include "JSAPI2_CallbackManager.h"
+#include "JSAPI2_TransportAPI.h"
+#include "JSAPI2_AsyncDownloader.h"
+#include "JSAPI2_MediaCore.h"
+#include "api.h"
+
+JSAPI2::CallbackManager JSAPI2::callbackManager;
+
+JSAPI2::CallbackManager::CallbackManager() : callbackGuard("JSAPI2::CallbackManager::callbackGuard")
+{}
+
+void JSAPI2::CallbackManager::Register( JSAPI2::TransportAPI *me )
+{
+ /* benski> important note:
+ even thought JSAPI2::Transport inherits from IUnknown,
+ we don't call AddRef here!
+ because this would introduce a circular reference.
+ JSAPI2::TransportAPI will call Deregister during it's
+ destructor.
+ */
+ Nullsoft::Utility::AutoLock lock( callbackGuard );
+ transports.push_back( new TransportCallback( me ) );
+}
+
+void JSAPI2::CallbackManager::Deregister( JSAPI2::TransportAPI *me )
+{
+ /* benski> important note:
+ even thought JSAPI2::Transport inherits from IUnknown,
+ we don't call Release here!
+ because this would introduce a circular reference.
+ JSAPI2::TransportAPI will call Deregister during it's
+ destructor.
+ */
+ Nullsoft::Utility::AutoLock lock( callbackGuard );
+ for ( size_t i = 0; i != transports.size(); i++ )
+ {
+ TransportCallback *callback = transports[ i ];
+ if ( callback->api == me )
+ {
+ delete callback;
+ transports.erase( transports.begin() + i );
+ i--;
+ }
+ }
+}
+
+
+void JSAPI2::CallbackManager::Register( JSAPI2::MediaCoreAPI *me )
+{
+ Nullsoft::Utility::AutoLock lock( callbackGuard );
+
+ //if (!mediaCores.contains(me))
+ if ( mediaCores.end() == std::find( mediaCores.begin(), mediaCores.end(), me ) )
+ {
+ mediaCores.push_back( me );
+ }
+}
+
+void JSAPI2::CallbackManager::Deregister( JSAPI2::MediaCoreAPI *me )
+{
+ Nullsoft::Utility::AutoLock lock( callbackGuard );
+
+ auto it = mediaCores.begin();
+ while ( it != mediaCores.end() )
+ {
+ if ( *it != me )
+ {
+ it++;
+ continue;
+ }
+
+ it = mediaCores.erase( it );
+ }
+}
+
+
+void JSAPI2::CallbackManager::Register( JSAPI2::AsyncDownloaderAPI *me )
+{
+ Nullsoft::Utility::AutoLock lock( callbackGuard );
+ asyncDownloaders.push_back( new AsyncDownloaderCallback( me ) );
+}
+
+void JSAPI2::CallbackManager::Deregister( JSAPI2::AsyncDownloaderAPI *me )
+{
+ Nullsoft::Utility::AutoLock lock( callbackGuard );
+ for ( size_t i = 0; i != asyncDownloaders.size(); i++ )
+ {
+ AsyncDownloaderCallback *callback = asyncDownloaders[ i ];
+ if ( callback->api == me )
+ {
+ delete callback;
+ asyncDownloaders.erase( asyncDownloaders.begin() + i );
+ i--;
+ }
+ }
+}
+
+/* --- OnStop --- */
+struct OnStopAPCData
+{
+ JSAPI2::TransportAPI *transport;
+ int position;
+ int is_full_stop;
+};
+
+static void CALLBACK CMGR_OnStopAPC(ULONG_PTR param)
+{
+ OnStopAPCData *data = (OnStopAPCData *)param;
+ data->transport->OnStop(data->position, data->is_full_stop);
+ data->transport->Release();
+ delete data;
+}
+
+void JSAPI2::CallbackManager::OnStop(int position, int is_full_stop)
+{
+ DWORD threadId = GetCurrentThreadId();
+ Nullsoft::Utility::AutoLock lock(callbackGuard);
+
+ for ( TransportCallback *l_transport : transports )
+ {
+ OnStopAPCData *data = new OnStopAPCData;
+ data->transport = l_transport->api;
+ data->position = position;
+ data->is_full_stop = is_full_stop;
+
+ data->transport->AddRef(); // so it doesn't disappear while we're switching threads
+
+ if ( threadId == l_transport->threadId )
+ {
+ // same thread! huzzah but I wonder how that happened :)
+ CMGR_OnStopAPC( (ULONG_PTR)data );
+ }
+ else
+ {
+ // different thread, do an APC
+ if ( QueueUserAPC( CMGR_OnStopAPC, l_transport->threadHandle, (ULONG_PTR)data ) == 0 )
+ {
+ data->transport->Release();
+ delete data;
+ }
+ }
+ }
+}
+/* --- --- */
+
+/* --- OnPlay --- */
+struct OnPlayAPC
+{
+ JSAPI2::TransportAPI *transport;
+ wchar_t *filename;
+};
+
+static void CALLBACK CMGR_OnPlayAPC(ULONG_PTR param)
+{
+ OnPlayAPC *data = (OnPlayAPC *)param;
+ data->transport->OnPlay(data->filename);
+
+ free(data->filename);
+
+ data->transport->Release();
+ delete data;
+}
+
+void JSAPI2::CallbackManager::OnPlay(const wchar_t *filename)
+{
+ DWORD threadId = GetCurrentThreadId();
+ Nullsoft::Utility::AutoLock lock(callbackGuard);
+
+ for ( TransportCallback *l_transport : transports )
+ {
+ OnPlayAPC *data = new OnPlayAPC;
+ data->transport = l_transport->api;
+ data->filename = _wcsdup(filename);
+
+ data->transport->AddRef(); // so it doesn't disappear while we're switching threads
+
+ if ( threadId == l_transport->threadId )
+ {
+ // same thread! huzzah but I wonder how that happened :)
+ CMGR_OnPlayAPC( (ULONG_PTR)data );
+ }
+ else
+ {
+ // different thread, do an APC
+ if ( QueueUserAPC( CMGR_OnPlayAPC, l_transport->threadHandle, (ULONG_PTR)data ) == 0 )
+ {
+ data->transport->Release();
+ free( data->filename );
+ delete data;
+ }
+ }
+ }
+}
+
+/* --- --- */
+
+
+struct OnPauseAPC
+{
+ JSAPI2::TransportAPI *transport;
+ bool pause_state;
+};
+
+static void CALLBACK CMGR_OnPauseAPC(ULONG_PTR param)
+{
+ OnPauseAPC *data = (OnPauseAPC *)param;
+ data->transport->OnPause(data->pause_state);
+ data->transport->Release();
+ delete data;
+}
+
+void JSAPI2::CallbackManager::OnPause(bool pause_state)
+{
+ DWORD threadId = GetCurrentThreadId();
+ Nullsoft::Utility::AutoLock lock( callbackGuard );
+
+ for ( TransportCallback *l_transport : transports )
+ {
+ OnPauseAPC *data = new OnPauseAPC;
+ data->transport = l_transport->api;
+ data->pause_state = pause_state;
+
+ data->transport->AddRef(); // so it doesn't disappear while we're switching threads
+
+ if (threadId == l_transport->threadId)
+ {
+ // same thread! huzzah but I wonder how that happened :)
+ CMGR_OnPauseAPC((ULONG_PTR)data);
+ }
+ else
+ {
+ // different thread, do an APC
+ if (QueueUserAPC(CMGR_OnPauseAPC, l_transport->threadHandle, (ULONG_PTR)data) == 0)
+ {
+ data->transport->Release();
+ delete data;
+ }
+ }
+ }
+}
+
+/* --- --- */
+bool JSAPI2::CallbackManager::OverrideMetadata( const wchar_t *filename, const wchar_t *tag, wchar_t *out, size_t outCch )
+{
+ if ( NULL != filename && NULL != tag && NULL != out )
+ {
+ Nullsoft::Utility::AutoLock lock( callbackGuard );
+ for ( MediaCoreAPI *l_mediaCore : mediaCores )
+ {
+ if ( l_mediaCore->OverrideMetadata( filename, tag, out, outCch ) )
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/* --- OnInit --- */
+struct OnInitAPC
+{
+ JSAPI2::AsyncDownloaderAPI *asyncDownloader;
+ wchar_t *url;
+};
+
+static void CALLBACK CMGR_OnInitAPC(ULONG_PTR param)
+{
+ OnInitAPC *data = (OnInitAPC *)param;
+ data->asyncDownloader->OnInit(data->url);
+ free(data->url);
+ data->asyncDownloader->Release();
+ delete data;
+}
+
+void JSAPI2::CallbackManager::OnInit( const wchar_t *url, const wchar_t *onlinesvcId )
+{
+ DWORD threadId = GetCurrentThreadId();
+ Nullsoft::Utility::AutoLock lock( callbackGuard );
+
+ for ( AsyncDownloaderCallback *l_downloader : asyncDownloaders )
+ {
+ if ( wcscmp( onlinesvcId, l_downloader->api->GetKey() ) )
+ continue; //only call back to the same online service that issued the download reqeust
+
+ OnInitAPC *data = new OnInitAPC;
+ data->asyncDownloader = l_downloader->api;
+ data->url = _wcsdup( url );
+
+ data->asyncDownloader->AddRef(); // so it doesn't disappear while we're switching threads
+
+ if ( threadId == l_downloader->threadId )
+ {
+ // same thread! huzzah but I wonder how that happened :)
+ CMGR_OnInitAPC( (ULONG_PTR)data );
+ }
+ else
+ {
+ // different thread, do an APC
+ if ( QueueUserAPC( CMGR_OnInitAPC, l_downloader->threadHandle, (ULONG_PTR)data ) == 0 )
+ {
+ data->asyncDownloader->Release();
+ free( data->url );
+ delete data;
+ }
+ }
+
+ }
+}
+
+
+/* --- OnConnect --- */
+struct OnConnectAPC
+{
+ JSAPI2::AsyncDownloaderAPI *asyncDownloader;
+ wchar_t *url;
+};
+
+static void CALLBACK CMGR_OnConnectAPC(ULONG_PTR param)
+{
+ OnConnectAPC *data = (OnConnectAPC *)param;
+ data->asyncDownloader->OnConnect(data->url);
+ free(data->url);
+ data->asyncDownloader->Release();
+ delete data;
+}
+
+void JSAPI2::CallbackManager::OnConnect( const wchar_t *url, const wchar_t *onlinesvcId )
+{
+ DWORD threadId = GetCurrentThreadId();
+ Nullsoft::Utility::AutoLock lock( callbackGuard );
+
+ for ( AsyncDownloaderCallback *l_downloader : asyncDownloaders )
+ {
+ if ( wcscmp( onlinesvcId, l_downloader->api->GetKey() ) )
+ continue; //only call back to the same online service that issued the download reqeust
+
+ OnConnectAPC *data = new OnConnectAPC;
+ data->asyncDownloader = l_downloader->api;
+ data->url = _wcsdup( url );
+
+ data->asyncDownloader->AddRef(); // so it doesn't disappear while we're switching threads
+
+ if ( threadId == l_downloader->threadId )
+ {
+ // same thread! huzzah but I wonder how that happened :)
+ CMGR_OnConnectAPC( (ULONG_PTR)data );
+ }
+ else
+ {
+ // different thread, do an APC
+ if ( QueueUserAPC( CMGR_OnConnectAPC, l_downloader->threadHandle, (ULONG_PTR)data ) == 0 )
+ {
+ data->asyncDownloader->Release();
+ free( data->url );
+ delete data;
+ }
+ }
+ }
+}
+
+
+/* --- OnCancel --- */
+struct OnCancelAPC
+{
+ JSAPI2::AsyncDownloaderAPI *asyncDownloader;
+ wchar_t *url;
+};
+
+static void CALLBACK CMGR_OnCancelAPC(ULONG_PTR param)
+{
+ OnCancelAPC *data = (OnCancelAPC *)param;
+ data->asyncDownloader->OnCancel(data->url);
+ free(data->url);
+ data->asyncDownloader->Release();
+ delete data;
+}
+
+void JSAPI2::CallbackManager::OnCancel( const wchar_t *url, const wchar_t *onlinesvcId )
+{
+ DWORD threadId = GetCurrentThreadId();
+ Nullsoft::Utility::AutoLock lock( callbackGuard );
+
+ for ( AsyncDownloaderCallback *l_downloader : asyncDownloaders )
+ {
+ if ( wcscmp( onlinesvcId, l_downloader->api->GetKey() ) )
+ continue; //only call back to the same online service that issued the download reqeust
+
+ OnCancelAPC *data = new OnCancelAPC;
+ data->asyncDownloader = l_downloader->api;
+ data->url = _wcsdup( url );
+
+ data->asyncDownloader->AddRef(); // so it doesn't disappear while we're switching threads
+
+ if ( threadId == l_downloader->threadId )
+ {
+ // same thread! huzzah but I wonder how that happened :)
+ CMGR_OnCancelAPC( (ULONG_PTR)data );
+ }
+ else
+ {
+ // different thread, do an APC
+ if ( QueueUserAPC( CMGR_OnCancelAPC, l_downloader->threadHandle, (ULONG_PTR)data ) == 0 )
+ {
+ data->asyncDownloader->Release();
+ free( data->url );
+ delete data;
+ }
+ }
+ }
+}
+
+
+/* --- OnData --- */
+struct OnDataAPC
+{
+ JSAPI2::AsyncDownloaderAPI *asyncDownloader;
+ wchar_t *url;
+ size_t downloadedlen;
+ size_t totallen;
+};
+
+static void CALLBACK CMGR_OnDataAPC(ULONG_PTR param)
+{
+ OnDataAPC *data = (OnDataAPC *)param;
+ data->asyncDownloader->OnData(data->url, data->downloadedlen, data->totallen);
+ free(data->url);
+ data->asyncDownloader->Release();
+ delete data;
+}
+
+void JSAPI2::CallbackManager::OnData(const wchar_t *url, size_t downloadedlen, size_t totallen, const wchar_t *onlinesvcId)
+{
+ DWORD threadId = GetCurrentThreadId();
+ Nullsoft::Utility::AutoLock lock(callbackGuard);
+
+ for ( AsyncDownloaderCallback *downloader : asyncDownloaders )
+ {
+ if ( wcscmp(onlinesvcId, downloader->api->GetKey()) )
+ continue; //only call back to the same online service that issued the download reqeust
+
+ OnDataAPC *data = new OnDataAPC;
+ data->asyncDownloader = downloader->api;
+ data->url = _wcsdup(url);
+ data->downloadedlen = downloadedlen;
+ data->totallen = totallen;
+ data->asyncDownloader->AddRef(); // so it doesn't disappear while we're switching threads
+ if (threadId == downloader->threadId)
+ {
+ // same thread! huzzah but I wonder how that happened :)
+ CMGR_OnDataAPC((ULONG_PTR)data);
+ }
+ else
+ {
+ // different thread, do an APC
+ if (QueueUserAPC(CMGR_OnDataAPC, downloader->threadHandle, (ULONG_PTR)data) == 0)
+ {
+ data->asyncDownloader->Release();
+ free(data->url);
+ delete data;
+ }
+ }
+ }
+}
+
+
+/* --- OnError --- */
+struct OnErrorAPC
+{
+ JSAPI2::AsyncDownloaderAPI *asyncDownloader;
+ wchar_t *url;
+ int error;
+};
+
+static void CALLBACK CMGR_OnErrorAPC(ULONG_PTR param)
+{
+ OnErrorAPC *data = (OnErrorAPC *)param;
+ data->asyncDownloader->OnError(data->url, data->error);
+ free(data->url);
+ data->asyncDownloader->Release();
+ delete data;
+}
+
+void JSAPI2::CallbackManager::OnError(const wchar_t *url, int error, const wchar_t *onlinesvcId)
+{
+ DWORD threadId = GetCurrentThreadId();
+ Nullsoft::Utility::AutoLock lock(callbackGuard);
+
+ for ( AsyncDownloaderCallback *downloader : asyncDownloaders )
+ {
+ if ( wcscmp(onlinesvcId, downloader->api->GetKey()) )
+ continue; //only call back to the same online service that issued the download reqeust
+
+ OnErrorAPC *data = new OnErrorAPC;
+ data->asyncDownloader = downloader->api;
+ data->url = _wcsdup(url);
+ data->error = error;
+ data->asyncDownloader->AddRef(); // so it doesn't disappear while we're switching threads
+ if (threadId == downloader->threadId)
+ {
+ // same thread! huzzah but I wonder how that happened :)
+ CMGR_OnErrorAPC((ULONG_PTR)data);
+ }
+ else
+ {
+ // different thread, do an APC
+ if (QueueUserAPC(CMGR_OnErrorAPC, downloader->threadHandle, (ULONG_PTR)data) == 0)
+ {
+ data->asyncDownloader->Release();
+ free(data->url);
+ delete data;
+ }
+ }
+ }
+}
+
+
+/* --- OnFinish --- */
+struct OnFinishAPC
+{
+ JSAPI2::AsyncDownloaderAPI *asyncDownloader;
+ wchar_t *url;
+ wchar_t *destfilename;
+};
+
+static void CALLBACK CMGR_OnFinishAPC(ULONG_PTR param)
+{
+ OnFinishAPC *data = (OnFinishAPC *)param;
+ data->asyncDownloader->OnFinish(data->url, data->destfilename);
+ free(data->url);
+ free(data->destfilename);
+ data->asyncDownloader->Release();
+ delete data;
+}
+
+void JSAPI2::CallbackManager::OnFinish(const wchar_t *url, const wchar_t *destfilename, const wchar_t *onlinesvcId)
+{
+ DWORD threadId = GetCurrentThreadId();
+ Nullsoft::Utility::AutoLock lock(callbackGuard);
+
+ for ( AsyncDownloaderCallback *downloader : asyncDownloaders )
+ {
+ if ( wcscmp(onlinesvcId, downloader->api->GetKey()) )
+ continue; //only call back to the same online service that issued the download reqeust
+
+ OnFinishAPC *data = new OnFinishAPC;
+ data->asyncDownloader = downloader->api;
+ data->url = _wcsdup(url);
+ data->destfilename = _wcsdup(destfilename);
+ data->asyncDownloader->AddRef(); // so it doesn't disappear while we're switching threads
+ if (threadId == downloader->threadId)
+ {
+ // same thread! huzzah but I wonder how that happened :)
+ CMGR_OnFinishAPC((ULONG_PTR)data);
+ }
+ else
+ {
+ // different thread, do an APC
+ if (QueueUserAPC(CMGR_OnFinishAPC, downloader->threadHandle, (ULONG_PTR)data) == 0)
+ {
+ data->asyncDownloader->Release();
+ free(data->url);
+ free(data->destfilename);
+ delete data;
+ }
+ }
+ }
+}