561 lines
12 KiB
C++
561 lines
12 KiB
C++
|
#include "precomp__gen_ff.h"
|
||
|
|
||
|
#include <api/core/api_core.h>
|
||
|
#include "main.h"
|
||
|
#include "AlbumArt.h"
|
||
|
#include "wa2frontend.h"
|
||
|
#include <api.h>
|
||
|
#include <tataki/bitmap/bitmap.h>
|
||
|
#include <api\wnd\notifmsg.h>
|
||
|
#include <api/script/scriptmgr.h>
|
||
|
#include <api/script/script.h>
|
||
|
|
||
|
#define ALBUMART_MAX_THREADS 4
|
||
|
|
||
|
const wchar_t albumArtXuiObjectStr[] = L"AlbumArt"; // This is the xml tag
|
||
|
char albumArtXuiSvcName[] = "Album Art XUI object"; // this is the name of the xuiservice
|
||
|
|
||
|
AlbumArtScriptController _albumartController;
|
||
|
AlbumArtScriptController *albumartController = &_albumartController;
|
||
|
|
||
|
BEGIN_SERVICES( wa2AlbumArt_Svcs );
|
||
|
DECLARE_SERVICE( XuiObjectCreator<AlbumArtXuiSvc> );
|
||
|
END_SERVICES( wa2AlbumArt_Svcs, _wa2AlbumArt_Svcs );
|
||
|
|
||
|
// --------------------------------------------------------
|
||
|
// Maki Script Object
|
||
|
// --------------------------------------------------------
|
||
|
|
||
|
// -- Functions table -------------------------------------
|
||
|
function_descriptor_struct AlbumArtScriptController::exportedFunction[] = {
|
||
|
{L"refresh", 0, (void *)AlbumArt::script_vcpu_refresh },
|
||
|
{L"onAlbumArtLoaded", 1, (void *)AlbumArt::script_vcpu_onAlbumArtLoaded },
|
||
|
{L"isLoading", 0, (void *)AlbumArt::script_vcpu_isLoading },
|
||
|
};
|
||
|
|
||
|
const wchar_t *AlbumArtScriptController::getClassName()
|
||
|
{
|
||
|
return L"AlbumArtLayer";
|
||
|
}
|
||
|
|
||
|
const wchar_t *AlbumArtScriptController::getAncestorClassName()
|
||
|
{
|
||
|
return L"Layer";
|
||
|
}
|
||
|
|
||
|
ScriptObject *AlbumArtScriptController::instantiate()
|
||
|
{
|
||
|
AlbumArt *a = new AlbumArt;
|
||
|
|
||
|
ASSERT( a != NULL );
|
||
|
|
||
|
return a->getScriptObject();
|
||
|
}
|
||
|
|
||
|
void AlbumArtScriptController::destroy( ScriptObject *o )
|
||
|
{
|
||
|
AlbumArt *a = static_cast<AlbumArt *>( o->vcpu_getInterface( albumArtGuid ) );
|
||
|
|
||
|
ASSERT( a != NULL );
|
||
|
|
||
|
delete a;
|
||
|
}
|
||
|
|
||
|
void *AlbumArtScriptController::encapsulate( ScriptObject *o )
|
||
|
{
|
||
|
return NULL; // no encapsulation yet
|
||
|
}
|
||
|
|
||
|
void AlbumArtScriptController::deencapsulate( void *o )
|
||
|
{}
|
||
|
|
||
|
int AlbumArtScriptController::getNumFunctions()
|
||
|
{
|
||
|
return sizeof( exportedFunction ) / sizeof( function_descriptor_struct );
|
||
|
}
|
||
|
|
||
|
const function_descriptor_struct *AlbumArtScriptController::getExportedFunctions()
|
||
|
{
|
||
|
return exportedFunction;
|
||
|
}
|
||
|
|
||
|
GUID AlbumArtScriptController::getClassGuid()
|
||
|
{
|
||
|
return albumArtGuid;
|
||
|
}
|
||
|
|
||
|
|
||
|
XMLParamPair AlbumArt::params[] =
|
||
|
{
|
||
|
{ALBUMART_NOTFOUNDIMAGE, L"NOTFOUNDIMAGE"},
|
||
|
{ALBUMART_SOURCE, L"SOURCE"},
|
||
|
{ALBUMART_VALIGN, L"VALIGN"},
|
||
|
{ALBUMART_ALIGN, L"ALIGN"},
|
||
|
{ALBUMART_STRETCHED, L"STRETCHED"},
|
||
|
{ALBUMART_NOREFRESH, L"NOAUTOREFRESH"},
|
||
|
};
|
||
|
|
||
|
class AlbumArtThreadContext
|
||
|
{
|
||
|
public:
|
||
|
AlbumArtThreadContext( const wchar_t *_filename, AlbumArt *_wnd )
|
||
|
{
|
||
|
/* lazy load these two handles */
|
||
|
if ( !_wnd->hMainThread )
|
||
|
_wnd->hMainThread = WASABI_API_APP->main_getMainThreadHandle();
|
||
|
|
||
|
if ( !_wnd->thread_semaphore )
|
||
|
_wnd->thread_semaphore = CreateSemaphore( 0, ALBUMART_MAX_THREADS, ALBUMART_MAX_THREADS, 0 );
|
||
|
|
||
|
wnd = _wnd;
|
||
|
iterator = wnd->iterator;
|
||
|
h = w = 0;
|
||
|
bits = 0;
|
||
|
filename = _wcsdup( _filename );
|
||
|
}
|
||
|
|
||
|
void FreeBits()
|
||
|
{
|
||
|
if ( bits )
|
||
|
WASABI_API_MEMMGR->sysFree( bits );
|
||
|
|
||
|
bits = 0;
|
||
|
}
|
||
|
|
||
|
~AlbumArtThreadContext()
|
||
|
{
|
||
|
if ( wnd )
|
||
|
{
|
||
|
if ( wnd->thread_semaphore )
|
||
|
ReleaseSemaphore( wnd->thread_semaphore, 1, 0 );
|
||
|
|
||
|
wnd->isLoading--;
|
||
|
}
|
||
|
|
||
|
free( filename );
|
||
|
}
|
||
|
|
||
|
static void CALLBACK AlbumArtNotifyAPC( ULONG_PTR p );
|
||
|
bool LoadArt();
|
||
|
|
||
|
int h;
|
||
|
int w;
|
||
|
ARGB32 *bits;
|
||
|
LONG iterator;
|
||
|
wchar_t *filename;
|
||
|
AlbumArt *wnd;
|
||
|
};
|
||
|
|
||
|
|
||
|
AlbumArt::AlbumArt()
|
||
|
{
|
||
|
getScriptObject()->vcpu_setInterface( albumArtGuid, ( void * )static_cast<AlbumArt *>( this ) );
|
||
|
getScriptObject()->vcpu_setClassName( L"AlbumArtLayer" );
|
||
|
getScriptObject()->vcpu_setController( albumartController );
|
||
|
|
||
|
WASABI_API_MEDIACORE->core_addCallback( 0, this );
|
||
|
|
||
|
w = 0;
|
||
|
h = 0;
|
||
|
iterator = 0;
|
||
|
bits = 0;
|
||
|
hMainThread = 0;
|
||
|
thread_semaphore = 0;
|
||
|
artBitmap = 0;
|
||
|
valign = 0;
|
||
|
align = 0;
|
||
|
stretched = false;
|
||
|
missing_art_image = L"winamp.cover.notfound"; // default to this.
|
||
|
src_file = L"";
|
||
|
forceRefresh = false;
|
||
|
noAutoRefresh = false;
|
||
|
noMakiCallback = false;
|
||
|
isLoading = 0;
|
||
|
|
||
|
/* register XML parameters */
|
||
|
xuihandle = newXuiHandle();
|
||
|
|
||
|
CreateXMLParameters( xuihandle );
|
||
|
}
|
||
|
|
||
|
void AlbumArt::CreateXMLParameters( int master_handle )
|
||
|
{
|
||
|
//ALBUMART_PARENT::CreateXMLParameters(master_handle);
|
||
|
int numParams = sizeof( params ) / sizeof( params[ 0 ] );
|
||
|
hintNumberOfParams( xuihandle, numParams );
|
||
|
|
||
|
for ( int i = 0; i < numParams; i++ )
|
||
|
addParam( xuihandle, params[ i ], XUI_ATTRIBUTE_IMPLIED );
|
||
|
}
|
||
|
|
||
|
|
||
|
AlbumArt::~AlbumArt()
|
||
|
{
|
||
|
WASABI_API_SYSCB->syscb_deregisterCallback( static_cast<MetadataCallbackI *>( this ) );
|
||
|
WASABI_API_MEDIACORE->core_delCallback( 0, this );
|
||
|
|
||
|
// wait for all of our threads to finish
|
||
|
InterlockedIncrement( &iterator ); // our kill switch (will invalidate iterator on all outstanding threads)
|
||
|
if ( thread_semaphore )
|
||
|
{
|
||
|
for ( int i = 0; i < ALBUMART_MAX_THREADS; i++ )
|
||
|
{
|
||
|
if ( WaitForMultipleObjectsEx( 1, &thread_semaphore, FALSE, INFINITE, TRUE ) != WAIT_OBJECT_0 )
|
||
|
i--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
delete artBitmap;
|
||
|
|
||
|
if ( bits )
|
||
|
WASABI_API_MEMMGR->sysFree( bits );
|
||
|
|
||
|
if ( thread_semaphore )
|
||
|
CloseHandle( thread_semaphore );
|
||
|
|
||
|
if ( hMainThread )
|
||
|
CloseHandle( hMainThread );
|
||
|
}
|
||
|
|
||
|
bool AlbumArt::layer_isInvalid()
|
||
|
{
|
||
|
return !bits;
|
||
|
}
|
||
|
|
||
|
void CALLBACK AlbumArtThreadContext::AlbumArtNotifyAPC( ULONG_PTR p )
|
||
|
{
|
||
|
AlbumArtThreadContext *context = (AlbumArtThreadContext *)p;
|
||
|
if ( context->wnd->iterator == context->iterator )
|
||
|
context->wnd->ArtLoaded( context->w, context->h, context->bits );
|
||
|
else
|
||
|
context->FreeBits();
|
||
|
|
||
|
delete context;
|
||
|
}
|
||
|
|
||
|
bool AlbumArtThreadContext::LoadArt()
|
||
|
{
|
||
|
if ( wnd->iterator != iterator )
|
||
|
return false;
|
||
|
|
||
|
if ( AGAVE_API_ALBUMART->GetAlbumArt( filename, L"cover", &w, &h, &bits ) != ALBUMART_SUCCESS )
|
||
|
{
|
||
|
bits = 0;
|
||
|
w = 0;
|
||
|
h = 0;
|
||
|
}
|
||
|
|
||
|
if ( wnd->iterator == iterator ) // make sure we're still valid
|
||
|
{
|
||
|
QueueUserAPC( AlbumArtNotifyAPC, wnd->hMainThread, (ULONG_PTR)this );
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
FreeBits();
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int AlbumArtThreadPoolFunc( HANDLE handle, void *user_data, intptr_t id )
|
||
|
{
|
||
|
AlbumArtThreadContext *context = (AlbumArtThreadContext *)user_data;
|
||
|
if ( context->LoadArt() == false )
|
||
|
{
|
||
|
delete context;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void AlbumArt::ArtLoaded( int _w, int _h, ARGB32 *_bits )
|
||
|
{
|
||
|
if ( bits )
|
||
|
WASABI_API_MEMMGR->sysFree( bits );
|
||
|
|
||
|
if ( artBitmap )
|
||
|
{
|
||
|
delete artBitmap;
|
||
|
artBitmap = 0;
|
||
|
}
|
||
|
|
||
|
bits = _bits;
|
||
|
w = _w;
|
||
|
h = _h;
|
||
|
|
||
|
if ( !bits )
|
||
|
{
|
||
|
SkinBitmap *albumart = missing_art_image.getBitmap();
|
||
|
if ( albumart )
|
||
|
{
|
||
|
w = albumart->getWidth();
|
||
|
h = albumart->getHeight();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
deleteRegion();
|
||
|
makeRegion();
|
||
|
notifyParent( ChildNotify::AUTOWHCHANGED );
|
||
|
invalidate();
|
||
|
|
||
|
// notify maki scripts that albumart has been found or not
|
||
|
if ( !noMakiCallback && _bits )
|
||
|
{
|
||
|
AlbumArt::script_vcpu_onAlbumArtLoaded( SCRIPT_CALL, this->getScriptObject(), MAKE_SCRIPT_BOOLEAN( (int)bits ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void AlbumArt::onSetVisible( int show )
|
||
|
{
|
||
|
if ( show )
|
||
|
{
|
||
|
corecb_onUrlChange( wa2.GetCurrentFile() );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( bits )
|
||
|
{
|
||
|
WASABI_API_MEMMGR->sysFree( bits );
|
||
|
|
||
|
delete artBitmap;
|
||
|
artBitmap = 0;
|
||
|
}
|
||
|
|
||
|
bits = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int AlbumArt::corecb_onUrlChange( const wchar_t *filename )
|
||
|
{
|
||
|
// Martin> if we call this from maki we want to do a refresh regardless of the albumartlayer being visible or not
|
||
|
if ( forceRefresh || ( !noAutoRefresh && isVisible() ) )
|
||
|
{
|
||
|
isLoading++;
|
||
|
|
||
|
// Martin > do a check for a specific file, defined via source param
|
||
|
if ( WCSICMP( src_file, L"" ) )
|
||
|
filename = src_file;
|
||
|
|
||
|
InterlockedIncrement( &iterator );
|
||
|
AlbumArtThreadContext *context = new AlbumArtThreadContext( filename, this );
|
||
|
|
||
|
// make sure we have an available thread free (wait for one if we don't)
|
||
|
while ( WaitForMultipleObjectsEx( 1, &thread_semaphore, FALSE, INFINITE, TRUE ) != WAIT_OBJECT_0 )
|
||
|
{}
|
||
|
|
||
|
// int vis__ = isVisible();
|
||
|
WASABI_API_THREADPOOL->RunFunction( 0, AlbumArtThreadPoolFunc, context, 0, api_threadpool::FLAG_LONG_EXECUTION );
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int AlbumArt::onInit()
|
||
|
{
|
||
|
int r = ALBUMART_PARENT::onInit();
|
||
|
WASABI_API_SYSCB->syscb_registerCallback( static_cast<MetadataCallbackI *>( this ) );
|
||
|
|
||
|
AlbumArt::corecb_onUrlChange( wa2.GetCurrentFile() );
|
||
|
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
int AlbumArt::skincb_onColorThemeChanged( const wchar_t *newcolortheme )
|
||
|
{
|
||
|
ALBUMART_PARENT::skincb_onColorThemeChanged( newcolortheme );
|
||
|
invalidate();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
SkinBitmap *AlbumArt::getBitmap()
|
||
|
{
|
||
|
if ( artBitmap )
|
||
|
return artBitmap;
|
||
|
|
||
|
if ( bits )
|
||
|
{
|
||
|
artBitmap = new HQSkinBitmap( bits, w, h ); //TH WDP2-212
|
||
|
|
||
|
return artBitmap;
|
||
|
}
|
||
|
|
||
|
return missing_art_image.getBitmap();
|
||
|
}
|
||
|
|
||
|
void AlbumArt::layer_adjustDest( RECT *r )
|
||
|
{
|
||
|
if ( !w || !h )
|
||
|
return;
|
||
|
|
||
|
if ( stretched )
|
||
|
return;
|
||
|
|
||
|
//getClientRect(r);
|
||
|
// maintain 'square' stretching
|
||
|
int dstW = r->right - r->left;
|
||
|
int dstH = r->bottom - r->top;
|
||
|
double aspX = (double)( dstW ) / (double)w;
|
||
|
double aspY = (double)( dstH ) / (double)h;
|
||
|
double asp = min( aspX, aspY );
|
||
|
int newW = (int)( w * asp );
|
||
|
int newH = (int)( h * asp );
|
||
|
|
||
|
// Align
|
||
|
int offsetX = ( dstW - newW ) / 2;
|
||
|
if ( align == 1 )
|
||
|
offsetX *= 2;
|
||
|
else if ( align == -1 )
|
||
|
offsetX = 0;
|
||
|
|
||
|
// Valign
|
||
|
int offsetY = ( dstH - newH ) / 2;
|
||
|
if ( valign == 1 )
|
||
|
offsetY *= 2;
|
||
|
else if ( valign == -1 )
|
||
|
offsetY = 0;
|
||
|
|
||
|
r->left += offsetX;
|
||
|
r->right = r->left + newW;
|
||
|
r->top += offsetY;
|
||
|
r->bottom = r->top + newH;
|
||
|
|
||
|
// This prevents parts of the image being cut off (if the img has the same dimensions as the rect) on moving/clicking winamp
|
||
|
// (they will just flicker, but at least they won't stay now)
|
||
|
// benski> CUT!!! no no no no no this is very bad because this gets called inside layer::onPaint
|
||
|
//invalidate();
|
||
|
}
|
||
|
/*
|
||
|
int AlbumArt::getWidth()
|
||
|
{
|
||
|
RECT r;
|
||
|
getClientRect(&r);
|
||
|
getDest(&r);
|
||
|
return r.right-r.left;
|
||
|
}
|
||
|
|
||
|
int AlbumArt::getHeight()
|
||
|
{
|
||
|
RECT r;
|
||
|
getClientRect(&r);
|
||
|
getDest(&r);
|
||
|
return r.bottom-r.top;
|
||
|
}
|
||
|
*/
|
||
|
/*
|
||
|
int AlbumArt::onPaint(Canvas *canvas)
|
||
|
{
|
||
|
ALBUMART_PARENT::onPaint(canvas);
|
||
|
if (bits)
|
||
|
{
|
||
|
SkinBitmap albumart(bits, w, h);
|
||
|
RECT dst;
|
||
|
getBufferPaintDest(&dst);
|
||
|
albumart.stretchToRectAlpha(canvas, &dst, getPaintingAlpha());
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
*/
|
||
|
int AlbumArt::setXuiParam( int _xuihandle, int attrid, const wchar_t *name, const wchar_t *strval )
|
||
|
{
|
||
|
if ( xuihandle != _xuihandle )
|
||
|
return ALBUMART_PARENT::setXuiParam( _xuihandle, attrid, name, strval );
|
||
|
|
||
|
switch ( attrid )
|
||
|
{
|
||
|
case ALBUMART_NOTFOUNDIMAGE:
|
||
|
missing_art_image = strval;
|
||
|
if ( !bits )
|
||
|
{
|
||
|
noMakiCallback = true;
|
||
|
ArtLoaded( 0, 0, 0 );
|
||
|
noMakiCallback = false;
|
||
|
}
|
||
|
break;
|
||
|
case ALBUMART_SOURCE:
|
||
|
src_file = strval;
|
||
|
AlbumArt::corecb_onUrlChange( wa2.GetCurrentFile() ); // This Param should _always_ hold our current file
|
||
|
break;
|
||
|
case ALBUMART_VALIGN:
|
||
|
if ( !WCSICMP( strval, L"top" ) )
|
||
|
valign = -1;
|
||
|
else if ( !WCSICMP( strval, L"bottom" ) )
|
||
|
valign = 1;
|
||
|
else
|
||
|
valign = 0;
|
||
|
|
||
|
deferedInvalidate();
|
||
|
break;
|
||
|
case ALBUMART_ALIGN:
|
||
|
if ( !WCSICMP( strval, L"left" ) )
|
||
|
align = -1;
|
||
|
else if ( !WCSICMP( strval, L"right" ) )
|
||
|
align = 1;
|
||
|
else
|
||
|
align = 0;
|
||
|
|
||
|
deferedInvalidate();
|
||
|
break;
|
||
|
case ALBUMART_STRETCHED:
|
||
|
if ( !WCSICMP( strval, L"0" ) || !WCSICMP( strval, L"" ) )
|
||
|
stretched = false;
|
||
|
else
|
||
|
stretched = true;
|
||
|
|
||
|
deferedInvalidate();
|
||
|
break;
|
||
|
case ALBUMART_NOREFRESH:
|
||
|
if ( !WCSICMP( strval, L"0" ) || !WCSICMP( strval, L"" ) )
|
||
|
noAutoRefresh = false;
|
||
|
else
|
||
|
noAutoRefresh = true;
|
||
|
break;
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
void AlbumArt::metacb_ArtUpdated( const wchar_t *filename )
|
||
|
{
|
||
|
// it'd be nice to do this, but we can't guarantee that our file didn't get updated in the process
|
||
|
//const wchar_t *curFn = wa2.GetCurrentFile();
|
||
|
// if (curFn && filename && *filename && *curFn && !_wcsicmp(filename, curFn))
|
||
|
AlbumArt::corecb_onUrlChange( wa2.GetCurrentFile() );
|
||
|
}
|
||
|
|
||
|
|
||
|
scriptVar AlbumArt::script_vcpu_refresh( SCRIPT_FUNCTION_PARAMS, ScriptObject *o )
|
||
|
{
|
||
|
SCRIPT_FUNCTION_INIT;
|
||
|
AlbumArt *a = static_cast<AlbumArt *>( o->vcpu_getInterface( albumArtGuid ) );
|
||
|
if ( a )
|
||
|
{
|
||
|
a->forceRefresh = true;
|
||
|
a->corecb_onUrlChange( wa2.GetCurrentFile() );
|
||
|
a->forceRefresh = false;
|
||
|
}
|
||
|
RETURN_SCRIPT_VOID;
|
||
|
}
|
||
|
|
||
|
scriptVar AlbumArt::script_vcpu_isLoading( SCRIPT_FUNCTION_PARAMS, ScriptObject *o )
|
||
|
{
|
||
|
SCRIPT_FUNCTION_INIT;
|
||
|
AlbumArt *a = static_cast<AlbumArt *>( o->vcpu_getInterface( albumArtGuid ) );
|
||
|
if ( a )
|
||
|
{
|
||
|
return MAKE_SCRIPT_BOOLEAN( !!a->isLoading );
|
||
|
}
|
||
|
|
||
|
return MAKE_SCRIPT_BOOLEAN( false );
|
||
|
}
|
||
|
|
||
|
scriptVar AlbumArt::script_vcpu_onAlbumArtLoaded( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar success )
|
||
|
{
|
||
|
SCRIPT_FUNCTION_INIT
|
||
|
PROCESS_HOOKS1( o, albumartController, success );
|
||
|
SCRIPT_FUNCTION_CHECKABORTEVENT;
|
||
|
SCRIPT_EXEC_EVENT1( o, success );
|
||
|
}
|