mirror of
https://github.com/WinampDesktop/winamp.git
synced 2024-11-24 11:40:10 +01:00
568 lines
14 KiB
C++
568 lines
14 KiB
C++
#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;
|
|
}
|
|
}
|
|
}
|
|
}
|