diff options
Diffstat (limited to 'Src/Plugins/Library/ml_nowplaying')
26 files changed, 2466 insertions, 0 deletions
diff --git a/Src/Plugins/Library/ml_nowplaying/common.cpp b/Src/Plugins/Library/ml_nowplaying/common.cpp new file mode 100644 index 00000000..f6d2f03c --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/common.cpp @@ -0,0 +1,147 @@ +#include "./common.h" +#include "./wasabi.h" + +#include <strsafe.h> + +LPWSTR Plugin_MallocString(size_t cchLen) +{ + return (LPWSTR)calloc(cchLen, sizeof(WCHAR)); +} + +void Plugin_FreeString(LPWSTR pszString) +{ + if (NULL != pszString) + { + free(pszString); + } +} + +LPWSTR Plugin_ReAllocString(LPWSTR pszString, size_t cchLen) +{ + return (LPWSTR)realloc(pszString, sizeof(WCHAR) * cchLen); +} + +LPWSTR Plugin_CopyString(LPCWSTR pszSource) +{ + if (NULL == pszSource) + return NULL; + + INT cchSource = lstrlenW(pszSource) + 1; + + LPWSTR copy = Plugin_MallocString(cchSource); + if (NULL != copy) + { + CopyMemory(copy, pszSource, sizeof(WCHAR) * cchSource); + } + return copy; +} + +LPSTR Plugin_MallocAnsiString(size_t cchLen) +{ + return (LPSTR)calloc(cchLen, sizeof(CHAR)); +} + +LPSTR Plugin_CopyAnsiString(LPCSTR pszSource) +{ + if (NULL == pszSource) + return NULL; + + INT cchSource = lstrlenA(pszSource) + 1; + + LPSTR copy = Plugin_MallocAnsiString(cchSource); + if (NULL != copy) + { + CopyMemory(copy, pszSource, sizeof(CHAR) * cchSource); + } + return copy; + +} +void Plugin_FreeAnsiString(LPSTR pszString) +{ + Plugin_FreeString((LPWSTR)pszString); +} + +LPSTR Plugin_WideCharToMultiByte(UINT codePage, DWORD dwFlags, LPCWSTR lpWideCharStr, INT cchWideChar, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar) +{ + INT cchBuffer = WideCharToMultiByte(codePage, dwFlags, lpWideCharStr, cchWideChar, NULL, 0, lpDefaultChar, lpUsedDefaultChar); + if (0 == cchBuffer) return NULL; + + LPSTR buffer = Plugin_MallocAnsiString(cchBuffer); + if (NULL == buffer) return NULL; + + if (0 == WideCharToMultiByte(codePage, dwFlags, lpWideCharStr, cchWideChar, buffer, cchBuffer, lpDefaultChar, lpUsedDefaultChar)) + { + Plugin_FreeAnsiString(buffer); + return NULL; + } + return buffer; +} + +LPWSTR Plugin_MultiByteToWideChar(UINT codePage, DWORD dwFlags, LPCSTR lpMultiByteStr, INT cbMultiByte) +{ + if (NULL == lpMultiByteStr) return NULL; + INT cchBuffer = MultiByteToWideChar(codePage, dwFlags, lpMultiByteStr, cbMultiByte, NULL, 0); + if (NULL == cchBuffer) return NULL; + + if (cbMultiByte > 0) cchBuffer++; + + LPWSTR buffer = Plugin_MallocString(cchBuffer); + if (NULL == buffer) return NULL; + + if (0 == MultiByteToWideChar(codePage, dwFlags, lpMultiByteStr, cbMultiByte, buffer, cchBuffer)) + { + Plugin_FreeString(buffer); + return NULL; + } + + if (cbMultiByte > 0) + { + buffer[cchBuffer - 1] = L'\0'; + } + return buffer; +} + + +LPWSTR Plugin_DuplicateResString(LPCWSTR pszResource) +{ + return (IS_INTRESOURCE(pszResource)) ? + (LPWSTR)pszResource : + Plugin_CopyString(pszResource); +} + +void Plugin_FreeResString(LPWSTR pszResource) +{ + if (!IS_INTRESOURCE(pszResource)) + Plugin_FreeString(pszResource); +} + +HRESULT Plugin_CopyResString(LPWSTR pszBuffer, INT cchBufferMax, LPCWSTR pszString) +{ + if (NULL == pszBuffer) + return E_INVALIDARG; + + HRESULT hr = S_OK; + + if (NULL == pszString) + { + pszBuffer[0] = L'\0'; + } + else if (IS_INTRESOURCE(pszString)) + { + if (NULL == WASABI_API_LNG) + hr = E_FAIL; + else + WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)pszString, pszBuffer, cchBufferMax); + } + else + { + hr = StringCchCopy(pszBuffer, cchBufferMax, pszString); + } + return hr; +} + +void Plugin_SafeRelease(IUnknown *pUnk) +{ + if (NULL != pUnk) + pUnk->Release(); +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_nowplaying/common.h b/Src/Plugins/Library/ml_nowplaying/common.h new file mode 100644 index 00000000..8e93665d --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/common.h @@ -0,0 +1,58 @@ +#ifndef NULLSOFT_NOWPLAYING_PLUGIN_COMMON_HEADER +#define NULLSOFT_NOWPLAYING_PLUGIN_COMMON_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../nu/trace.h" + +#define CSTR_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) + +#ifndef ARRAYSIZE +#define ARRAYSIZE(blah) (sizeof(blah)/sizeof(*blah)) +#endif + +#ifndef LONGX86 +#ifdef _WIN64 + #define LONGX86 LONG_PTR +#else /*_WIN64*/ + #define LONGX86 LONG +#endif /*_WIN64*/ +#endif // LONGX86 + +#ifdef __cplusplus + #define SENDMSG(__hwnd, __msgId, __wParam, __lParam) ::SendMessageW((__hwnd), (__msgId), (__wParam), (__lParam)) +#else + #define SENDMSG(__hwnd, __msgId, __wParam, __lParam) SendMessageW((__hwnd), (__msgId), (__wParam), (__lParam)) +#endif // __cplusplus + +#define SENDMLIPC(__hwndML, __ipcMsgId, __param) SENDMSG((__hwndML), WM_ML_IPC, (WPARAM)(__param), (LPARAM)(__ipcMsgId)) +#define SENDWAIPC(__hwndWA, __ipcMsgId, __param) SENDMSG((__hwndWA), WM_WA_IPC, (WPARAM)(__param), (LPARAM)(__ipcMsgId)) + +#define MSGRESULT(__hwnd, __result) { SetWindowLongPtrW((__hwnd), DWL_MSGRESULT, ((LONGX86)(LONG_PTR)(__result))); return TRUE; } + +#define SENDCMD(__hwnd, __ctrlId, __eventId, __hctrl) (SENDMSG((__hwnd), WM_COMMAND, MAKEWPARAM(__ctrlId, __eventId), (LPARAM)(__hctrl))) + + +LPWSTR Plugin_MallocString(size_t cchLen); +LPWSTR Plugin_ReAllocString(LPWSTR pszString, size_t cchLen); +void Plugin_FreeString(LPWSTR pszString); +LPWSTR Plugin_CopyString(LPCWSTR pszSource); + +LPSTR Plugin_MallocAnsiString(size_t cchLen); +LPSTR Plugin_CopyAnsiString(LPCSTR pszSource); +void Plugin_FreeAnsiString(LPSTR pszString); + +LPWSTR Plugin_DuplicateResString(LPCWSTR pszResource); +void Plugin_FreeResString(LPWSTR pszResource); +HRESULT Plugin_CopyResString(LPWSTR pszBuffer, INT cchBufferMax, LPCWSTR pszString); + +LPSTR Plugin_WideCharToMultiByte(UINT codePage, DWORD dwFlags, LPCWSTR lpWideCharStr, INT cchWideChar, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar); +LPWSTR Plugin_MultiByteToWideChar(UINT codePage, DWORD dwFlags, LPCSTR lpMultiByteStr, INT cbMultiByte); + +void Plugin_SafeRelease(IUnknown *pUnk); + + +#endif //NULLSOFT_NOWPLAYING_PLUGIN_COMMON_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_nowplaying/external.cpp b/Src/Plugins/Library/ml_nowplaying/external.cpp new file mode 100644 index 00000000..add50723 --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/external.cpp @@ -0,0 +1,110 @@ +#include "main.h" +#include "./resource.h" +#include "./external.h" + +ExternalDispatch::ExternalDispatch() + : ref(1) +{ +} + +ExternalDispatch::~ExternalDispatch() +{ +} + +HRESULT ExternalDispatch::CreateInstance(ExternalDispatch **instance) +{ + if (NULL == instance) return E_POINTER; + + *instance = new ExternalDispatch(); + if (NULL == *instance) return E_OUTOFMEMORY; + + return S_OK; +} + +LPCWSTR ExternalDispatch::GetName() +{ + return L"NowPlaying"; +} + + +ULONG ExternalDispatch::AddRef(void) +{ + return InterlockedIncrement((LONG*)&ref); +} + + +ULONG ExternalDispatch::Release(void) +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +STDMETHODIMP ExternalDispatch::QueryInterface(REFIID riid, void **ppvObject) +{ + if (NULL == ppvObject) return E_POINTER; + + if (IsEqualIID(riid, IID_IDispatch)) + *ppvObject = static_cast<IDispatch*>(this); + else if (IsEqualIID(riid, IID_IUnknown)) + *ppvObject = static_cast<IUnknown*>(this); + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +HRESULT ExternalDispatch::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid) +{ + bool unknowns = false; + for (unsigned int i = 0;i != cNames;i++) + { + if (_wcsicmp(rgszNames[i], L"hidden") == 0) + rgdispid[i] = DISPATCH_HIDDEN; + else + { + rgdispid[i] = DISPID_UNKNOWN; + unknowns = true; + } + } + if (unknowns) + return DISP_E_UNKNOWNNAME; + else + return S_OK; +} + +HRESULT ExternalDispatch::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo) +{ + return E_NOTIMPL; +} + +HRESULT ExternalDispatch::GetTypeInfoCount(unsigned int FAR * pctinfo) +{ + return E_NOTIMPL; +} + +HRESULT ExternalDispatch::Invoke(DISPID dispId, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr) +{ + switch(dispId) + { + case DISPATCH_HIDDEN: + if (NULL != pvarResult) + { + HWND hLibrary = Plugin_GetLibrary(); + VariantInit(pvarResult); + V_VT(pvarResult) = VT_BOOL; + V_BOOL(pvarResult) = (NULL == hLibrary || FALSE == SENDMLIPC(hLibrary, ML_IPC_IS_VISIBLE, 0)); + } + return S_OK; + } + return DISP_E_MEMBERNOTFOUND; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_nowplaying/external.h b/Src/Plugins/Library/ml_nowplaying/external.h new file mode 100644 index 00000000..fe9e4a9e --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/external.h @@ -0,0 +1,44 @@ +#ifndef NULLSOFT_NOWPLAYING_PLUGIN_EXTERNAL_HEADER +#define NULLSOFT_NOWPLAYING_PLUGIN_EXTERNAL_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +class ExternalDispatch : public IDispatch +{ + +public: + typedef enum + { + DISPATCH_HIDDEN = 777, + } DispatchCodes; + +protected: + ExternalDispatch(); + ~ExternalDispatch(); + +public: + static HRESULT CreateInstance(ExternalDispatch **instance); + static LPCWSTR GetName(); + +public: + STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject); + STDMETHOD_(ULONG, AddRef)(void); + STDMETHOD_(ULONG, Release)(void); + + // *** IDispatch Methods *** + STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid); + STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo); + STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo); + STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr); + +protected: + ULONG ref; + +}; + + +#endif //NULLSOFT_NOWPLAYING_PLUGIN_EXTERNAL_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_nowplaying/handler.cpp b/Src/Plugins/Library/ml_nowplaying/handler.cpp new file mode 100644 index 00000000..2d47a94e --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/handler.cpp @@ -0,0 +1,47 @@ +#include "main.h" +#include "service.h" +#include "navigation.h" +#include "handler.h" +#include "../Agave/URIHandler/svc_urihandler.h" +#include <api/service/waservicefactory.h> +#include "api.h" +#include "../ml_online/config.h" +#include "../replicant/nu/Autowide.h" + +int NowPlayingURIHandler::ProcessFilename(const wchar_t *filename) +{ + if (!_wcsnicmp(filename, L"winamp://Now Playing", 20) || !_wcsnicmp(filename, L"winamp://Now%20Playing", 22)) + { + size_t index = 0; + if (filename[12] == L' ') + index = 20; + else + index = 22; + + wchar_t fullUrl[1024] = L"http://client.winamp.com/nowplaying"; + lstrcpynW(fullUrl, AutoWide(g_config->ReadString("nowplayingurl", "http://client.winamp.com/nowplaying")), ARRAYSIZE(fullUrl)); + + if (filename[index] != 0) + { + StringCchCatW(fullUrl, 1024, filename + index); + } + Navigation_ShowService(SERVICE_ID, fullUrl, NAVFLAG_FORCEACTIVE | NAVFLAG_ENSUREMLVISIBLE | NAVFLAG_ENSUREITEMVISIBLE); + return HANDLED_EXCLUSIVE; + } + return NOT_HANDLED; +} + +int NowPlayingURIHandler::IsMine(const wchar_t *filename) +{ + if (!_wcsnicmp(filename, L"winamp://Now Playing", 20 ) || !_wcsnicmp(filename, L"winamp://Now%20Playing", 22)) + return HANDLED; + else + return NOT_HANDLED; +} + +#define CBCLASS NowPlayingURIHandler +START_DISPATCH; +CB(PROCESSFILENAME, ProcessFilename); +CB(ISMINE, IsMine); +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_nowplaying/handler.h b/Src/Plugins/Library/ml_nowplaying/handler.h new file mode 100644 index 00000000..d5021422 --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/handler.h @@ -0,0 +1,19 @@ +#pragma once + +#include "../Agave/URIHandler/svc_urihandler.h" + +// {7A8BAF83-3995-4550-B15B-12D297A9220E} +static const GUID ml_nowplaying_uri_handler = +{ 0x7a8baf83, 0x3995, 0x4550, { 0xb1, 0x5b, 0x12, 0xd2, 0x97, 0xa9, 0x22, 0xe } }; + +class NowPlayingURIHandler : public svc_urihandler +{ +public: + static const char *getServiceName() { return "Now Playing URI Handler"; } + static GUID getServiceGuid() { return ml_nowplaying_uri_handler; } + int ProcessFilename(const wchar_t *filename); + int IsMine(const wchar_t *filename); // just like ProcessFilename but don't actually process + +protected: + RECVS_DISPATCH; +};
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_nowplaying/local_menu.cpp b/Src/Plugins/Library/ml_nowplaying/local_menu.cpp new file mode 100644 index 00000000..33d3da7f --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/local_menu.cpp @@ -0,0 +1,68 @@ +#include "main.h" +#include "./local_menu.h" +#include "./wasabi.h" +#include "./resource.h" +#include "./navigation.h" +#include "../gen_ml/ml_ipc_0313.h" +#include "../nu/menuHelpers.h" + +#define SUBMENU_NAVIGATIONCONTEXT 0 + +static HMENU Menu_GetNavigationContext(HMENU baseMenu) +{ + HMENU hMenu = GetSubMenu(baseMenu, SUBMENU_NAVIGATIONCONTEXT); + if (NULL == hMenu) return NULL; + + hMenu = MenuHelper_DuplcateMenu(hMenu); + if (NULL == hMenu) return NULL; + + HNAVITEM hActive = Navigation_GetActive(NULL); + if (NULL != hActive) + { + EnableMenuItem(hMenu, ID_NAVIGATION_OPEN, MF_BYCOMMAND | MF_GRAYED | MF_DISABLED); + } + else + { + EnableMenuItem(hMenu, ID_NAVIGATION_OPEN, MF_BYCOMMAND | MF_ENABLED); + SetMenuDefaultItem(hMenu, ID_NAVIGATION_OPEN, FALSE); + } + + return hMenu; +} + +HMENU Menu_GetMenu(UINT menuKind) +{ + HMENU baseMenu = WASABI_API_LOADMENUW(IDR_CONTEXTMENU); + if (NULL == baseMenu) + return NULL; + + switch(menuKind) + { + case MENU_NAVIGATIONCONTEXT: + { + HMENU menu = Menu_GetNavigationContext(baseMenu); + if (!GetModuleHandle(L"ml_online.dll")) + { + if (DeleteMenu(menu, ID_PLUGIN_PREFERENCES, MF_BYCOMMAND)) + { + DeleteMenu(menu, 2, MF_BYPOSITION); + } + } + return menu; + } + } + + return NULL; +} + +BOOL Menu_ReleaseMenu(HMENU hMenu, UINT menuKind) +{ + if (NULL == hMenu) return FALSE; + + switch(menuKind) + { + case MENU_NAVIGATIONCONTEXT: + return DestroyMenu(hMenu); + } + return FALSE; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_nowplaying/local_menu.h b/Src/Plugins/Library/ml_nowplaying/local_menu.h new file mode 100644 index 00000000..bd33edcc --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/local_menu.h @@ -0,0 +1,15 @@ +#ifndef NULLSOFT_NOWPLAYING_PLUGIN_MENU_HEADER +#define NULLSOFT_NOWPLAYING_PLUGIN_MENU_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +#define MENU_NAVIGATIONCONTEXT 0 + +HMENU Menu_GetMenu(UINT menuKind); +BOOL Menu_ReleaseMenu(HMENU hMenu, UINT menuKind); + +#endif //NULLSOFT_NOWPLAYING_PLUGIN_MENU_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_nowplaying/main.cpp b/Src/Plugins/Library/ml_nowplaying/main.cpp new file mode 100644 index 00000000..4b5f2944 --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/main.cpp @@ -0,0 +1,160 @@ +#include "main.h" +#include "./navigation.h" +#include "./wasabi.h" +#include "./resource.h" +#include "./external.h" +#include "./wasabiCallback.h" +#include "../nu/MediaLibraryInterface.h" +#include "../replicant/nu/AutoChar.h" +#include "../winamp/wa_ipc.h" +#include "handler.h" +#include "../nu/Singleton.h" +#include "../ml_online/config.h" +#include <strsafe.h> + +static NowPlayingURIHandler uri_handler; +static SingletonServiceFactory<svc_urihandler, NowPlayingURIHandler> uri_handler_factory; + +static DWORD externalCookie = 0; +static SysCallback *wasabiCallback = NULL; +C_Config *g_config = NULL; + +static INT Plugin_Init(void); +static void Plugin_Quit(void); +static INT_PTR Plugin_MessageProc(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3); + +EXTERN_C winampMediaLibraryPlugin plugin = +{ + MLHDR_VER, + "nullsoft(ml_nowplaying.dll)", + Plugin_Init, + Plugin_Quit, + Plugin_MessageProc, + 0, + 0, + 0, +}; + +HINSTANCE Plugin_GetInstance(void) +{ + return plugin.hDllInstance; +} + +HWND Plugin_GetWinamp(void) +{ + return plugin.hwndWinampParent; +} + +HWND Plugin_GetLibrary(void) +{ + return plugin.hwndLibraryParent; +} + +void initConfigCache() +{ + wchar_t iniFileName[2048] = {0}; + mediaLibrary.BuildPath(L"Plugins\\ml", iniFileName, 2048); + CreateDirectory(iniFileName, NULL); + mediaLibrary.BuildPath(L"Plugins\\ml\\ml_online.ini", iniFileName, 2048); + AutoChar charFn(iniFileName); + g_config = new C_Config(AutoChar(iniFileName)); +} + +static INT Plugin_Init(void) +{ + if (!WasabiApi_Initialize(Plugin_GetInstance())) + return 1; + + if (NULL == OMBROWSERMNGR) + { + WasabiApi_Release(); + return 2; + } + + mediaLibrary.library = plugin.hwndLibraryParent; + mediaLibrary.winamp = plugin.hwndWinampParent; + mediaLibrary.instance = plugin.hDllInstance; + + initConfigCache(); + + if (NULL != WASABI_API_LNG) + { + static wchar_t szDescription[256]; + StringCchPrintf(szDescription, ARRAYSIZE(szDescription), WASABI_API_LNGSTRINGW(IDS_PLUGIN_NAME), + PLUGIN_VERSION_MAJOR, PLUGIN_VERSION_MINOR); + plugin.description = (char*)szDescription; + } + + ExternalDispatch *externalDispatch; + if (SUCCEEDED(ExternalDispatch::CreateInstance(&externalDispatch))) + { + DispatchInfo dispatchInfo; + dispatchInfo.id = 0; + dispatchInfo.name =(LPWSTR)externalDispatch->GetName(); + dispatchInfo.dispatch = externalDispatch; + + if (0 == SENDWAIPC(Plugin_GetWinamp(), IPC_ADD_DISPATCH_OBJECT, (WPARAM)&dispatchInfo)) + externalCookie = dispatchInfo.id; + + externalDispatch->Release(); + } + + if(NULL != WASABI_API_SYSCB && NULL == wasabiCallback && + SUCCEEDED(WasabiCallback::CreateInstance((WasabiCallback**)&wasabiCallback))) + { + WASABI_API_SYSCB->syscb_registerCallback(wasabiCallback, 0); + for (;;) + { + SysCallback *callback = WASABI_API_SYSCB->syscb_enum(SysCallback::BROWSER, 0); + if (NULL == callback || callback == wasabiCallback) + { + if (NULL != callback) + callback->Release(); + break; + } + + WASABI_API_SYSCB->syscb_deregisterCallback(callback); + WASABI_API_SYSCB->syscb_registerCallback(callback, 0); + callback->Release(); + } + } + + uri_handler_factory.Register(plugin.service, &uri_handler); + Navigation_Initialize(); + + return 0; +} + +static void Plugin_Quit(void) +{ + if (NULL != wasabiCallback) + { + if (NULL != WASABI_API_SYSCB) + WASABI_API_SYSCB->syscb_deregisterCallback(wasabiCallback); + wasabiCallback->Release(); + wasabiCallback = NULL; + } + + if (0 != externalCookie) + { + HWND hWinamp = Plugin_GetWinamp(); + SENDWAIPC(hWinamp, IPC_REMOVE_DISPATCH_OBJECT, (WPARAM)externalCookie); + externalCookie = 0; + } + uri_handler_factory.Deregister(plugin.service); + WasabiApi_Release(); +} + +static INT_PTR Plugin_MessageProc(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3) +{ + INT_PTR result = 0; + if (FALSE != Navigation_ProcessMessage(msg, param1, param2, param3, &result)) + return result; + + return FALSE; +} + +EXTERN_C __declspec(dllexport) winampMediaLibraryPlugin *winampGetMediaLibraryPlugin() +{ + return &plugin; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_nowplaying/main.h b/Src/Plugins/Library/ml_nowplaying/main.h new file mode 100644 index 00000000..2141525e --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/main.h @@ -0,0 +1,22 @@ +#ifndef NULLSOFT_NOWPLAYING_PLUGIN_MAIN_HEADER +#define NULLSOFT_NOWPLAYING_PLUGIN_MAIN_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../gen_ml/ml.h" +#include "./common.h" + +#define PLUGIN_VERSION_MAJOR 4 +#define PLUGIN_VERSION_MINOR 1 + +HINSTANCE Plugin_GetInstance(void); +HWND Plugin_GetWinamp(void); +HWND Plugin_GetLibrary(void); + +#include "../ml_online/config.h" +extern C_Config *g_config; + +#endif //NULLSOFT_NOWPLAYING_PLUGIN_MAIN_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.rc b/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.rc new file mode 100644 index 00000000..fea60a30 --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.rc @@ -0,0 +1,99 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_CONTEXTMENU MENU +BEGIN + POPUP "Navigation" + BEGIN + MENUITEM "&Open", ID_NAVIGATION_OPEN + MENUITEM "Open in &New Window", ID_NAVIGATION_OPENNEWWINDOW + MENUITEM SEPARATOR + MENUITEM "&Preferences", 40006 + MENUITEM SEPARATOR + MENUITEM "Help", ID_NAVIGATION_HELP + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_PLUGIN_NAME "Nullsoft Now Playing v%d.%02d" + 65535 "{7F31F590-6602-45c9-B3F8-F61AE05BD1D3}" +END + +STRINGTABLE +BEGIN + IDS_SERVICE_NAME "Now Playing" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.sln b/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.sln new file mode 100644 index 00000000..cf52a91e --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.sln @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29613.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ml_nowplaying", "ml_nowplaying.vcxproj", "{CB59733D-8D69-4DC4-AB67-C831DAE5A80F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CB59733D-8D69-4DC4-AB67-C831DAE5A80F}.Debug|Win32.ActiveCfg = Debug|Win32 + {CB59733D-8D69-4DC4-AB67-C831DAE5A80F}.Debug|Win32.Build.0 = Debug|Win32 + {CB59733D-8D69-4DC4-AB67-C831DAE5A80F}.Debug|x64.ActiveCfg = Debug|x64 + {CB59733D-8D69-4DC4-AB67-C831DAE5A80F}.Debug|x64.Build.0 = Debug|x64 + {CB59733D-8D69-4DC4-AB67-C831DAE5A80F}.Release|Win32.ActiveCfg = Release|Win32 + {CB59733D-8D69-4DC4-AB67-C831DAE5A80F}.Release|Win32.Build.0 = Release|Win32 + {CB59733D-8D69-4DC4-AB67-C831DAE5A80F}.Release|x64.ActiveCfg = Release|x64 + {CB59733D-8D69-4DC4-AB67-C831DAE5A80F}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0DF91B23-5CCB-4CD1-8A07-03B96BB9324E} + EndGlobalSection +EndGlobal diff --git a/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.vcxproj b/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.vcxproj new file mode 100644 index 00000000..0c9effee --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.vcxproj @@ -0,0 +1,280 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{CB59733D-8D69-4DC4-AB67-C831DAE5A80F}</ProjectGuid> + <RootNamespace>ml_nowplaying</RootNamespace> + <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + </PropertyGroup> + <PropertyGroup Label="Vcpkg"> + <VcpkgEnableManifest>true</VcpkgEnableManifest> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <VcpkgInstalledDir>..\..\..\external_dependencies\vcpkg</VcpkgInstalledDir> + <VcpkgUseStatic>true</VcpkgUseStatic> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <VcpkgInstalledDir>..\..\..\external_dependencies\vcpkg</VcpkgInstalledDir> + <VcpkgUseStatic>true</VcpkgUseStatic> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <VcpkgInstalledDir>..\..\..\external_dependencies\vcpkg</VcpkgInstalledDir> + <VcpkgUseStatic>true</VcpkgUseStatic> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <VcpkgInstalledDir>..\..\..\external_dependencies\vcpkg</VcpkgInstalledDir> + <VcpkgUseStatic>true</VcpkgUseStatic> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>..;..\..\..\;..\..\..\wasabi;..\..\..\agave;..\..\..\ombrowser;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;TEST_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>shlwapi.lib;comctl32.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName).dll</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)*.dll ..\..\..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\plugins\ +xcopy /Y /D $(OutDir)*.pdb ..\..\..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)*.dll ..\..\..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\plugins\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>..;..\..\..\;..\..\..\wasabi;..\..\..\agave;..\..\..\ombrowser;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_USRDLL;TEST_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>shlwapi.lib;comctl32.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName).dll</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)*.dll ..\..\..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\plugins\ +xcopy /Y /D $(OutDir)*.pdb ..\..\..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)*.dll ..\..\..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\plugins\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>..;..\..\..\;..\..\..\wasabi;..\..\..\agave;..\..\..\ombrowser;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_WIN32_WINNT=0x601;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <BufferSecurityCheck>false</BufferSecurityCheck> + <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>shlwapi.lib;comctl32.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName).dll</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)*.dll ..\..\..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)*.dll ..\..\..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\plugins\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>..;..\..\..\;..\..\..\wasabi;..\..\..\agave;..\..\..\ombrowser;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_WIN32_WINNT=0x601;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <BufferSecurityCheck>false</BufferSecurityCheck> + <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>shlwapi.lib;comctl32.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName).dll</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)*.dll ..\..\..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)*.dll ..\..\..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\plugins\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\..\General\gen_ml\menu.cpp" /> + <ClCompile Include="..\ml_online\config.cpp" /> + <ClCompile Include="..\..\..\nu\MediaLibraryInterface.cpp" /> + <ClCompile Include="..\..\..\nu\menuHelpers.cpp" /> + <ClCompile Include="..\..\..\nu\trace.cpp" /> + <ClCompile Include="common.cpp" /> + <ClCompile Include="external.cpp" /> + <ClCompile Include="handler.cpp" /> + <ClCompile Include="local_menu.cpp" /> + <ClCompile Include="main.cpp" /> + <ClCompile Include="navigation.cpp" /> + <ClCompile Include="service.cpp" /> + <ClCompile Include="wasabi.cpp" /> + <ClCompile Include="wasabiCallback.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\General\gen_ml\menu.h" /> + <ClInclude Include="..\ml_online\config.h" /> + <ClInclude Include="..\..\..\nu\MediaLibraryInterface.h" /> + <ClInclude Include="common.h" /> + <ClInclude Include="external.h" /> + <ClInclude Include="handler.h" /> + <ClInclude Include="local_menu.h" /> + <ClInclude Include="main.h" /> + <ClInclude Include="navigation.h" /> + <ClInclude Include="resource.h" /> + <ClInclude Include="service.h" /> + <ClInclude Include="wasabi.h" /> + <ClInclude Include="wasabiCallback.h" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="ml_nowplaying.rc" /> + <ResourceCompile Include="png.rc" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\..\omBrowser\omBrowser.vcxproj"> + <Project>{fc74db95-5008-4d22-9147-1c052f43cdd3}</Project> + </ProjectReference> + <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj"> + <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project> + </ProjectReference> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.vcxproj.filters b/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.vcxproj.filters new file mode 100644 index 00000000..046d44c4 --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.vcxproj.filters @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <ClCompile Include="common.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\ml_online\config.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="external.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="handler.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="local_menu.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\nu\MediaLibraryInterface.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\gen_ml\menu.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\nu\menuHelpers.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="navigation.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="service.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\nu\trace.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="wasabi.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="wasabiCallback.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="common.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\ml_online\config.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="external.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="handler.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="local_menu.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="main.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\nu\MediaLibraryInterface.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\gen_ml\menu.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="navigation.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="resource.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="service.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="wasabi.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="wasabiCallback.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="png.rc"> + <Filter>Header Files</Filter> + </ResourceCompile> + <ResourceCompile Include="ml_nowplaying.rc"> + <Filter>Ressource Files</Filter> + </ResourceCompile> + </ItemGroup> + <ItemGroup> + <Filter Include="Header Files"> + <UniqueIdentifier>{a1f12bb1-c2c0-49d3-b74a-5f8bbac3dee6}</UniqueIdentifier> + </Filter> + <Filter Include="Ressource Files"> + <UniqueIdentifier>{8fef1ee3-d193-47aa-872e-843f5178bcc5}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files"> + <UniqueIdentifier>{f17e7245-8101-4787-b60f-874085ddb8a7}</UniqueIdentifier> + </Filter> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_nowplaying/navigation.cpp b/Src/Plugins/Library/ml_nowplaying/navigation.cpp new file mode 100644 index 00000000..8f662db5 --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/navigation.cpp @@ -0,0 +1,684 @@ +#include "main.h" +#include "./navigation.h" +#include "./resource.h" +#include "./wasabi.h" +#include "./service.h" +#include "../omBrowser/browserView.h" +#include "../winamp/wa_ipc.h" +#include "../replicant/nu/Autowide.h" +#include "../gen_ml/ml_ipc_0313.h" +#include "./local_menu.h" +#include "../gen_ml/menu.h" +#include <strsafe.h> + +#define NAVITEM_PREFIX L"nowplaying_svc_" + +#define E_NAVITEM_UNKNOWN E_NOINTERFACE + +typedef struct __NAVENUMRESULT +{ + HNAVITEM hItem; + OmService *service; + UINT serviceId; + LPCWSTR pszPrefix; + INT cchPrefix; + HWND hLibrary; + NAVITEM itemInfo; + WCHAR szBuffer[256]; +} NAVENUMRESULT; + +typedef struct __FORCEURLDATA +{ + UINT serviceId; + LPWSTR url; +} FORCEURLDATA; + +#define FORCEURLPROP L"MLNOWPLAYING_FORCEURL" + +static void Navigation_RemoveForceUrl() +{ + HWND hLibrary = Plugin_GetLibrary(); + if (NULL == hLibrary) return; + + FORCEURLDATA *data = (FORCEURLDATA*)GetProp(hLibrary, FORCEURLPROP); + RemoveProp(hLibrary, FORCEURLPROP); + if (NULL != data) + { + Plugin_FreeString(data->url); + free(data); + } +} + +static HRESULT Navigation_SetForceUrl(UINT serviceId, LPCWSTR pszUrl) +{ + if (NULL == pszUrl) return E_INVALIDARG; + + HWND hLibrary = Plugin_GetLibrary(); + if (NULL == hLibrary) return E_FAIL; + + FORCEURLDATA *data = (FORCEURLDATA*)GetProp(hLibrary, FORCEURLPROP); + + if (NULL != data) + { + Plugin_FreeString(data->url); + if (data->serviceId != serviceId) + { + free(data); + data = NULL; + } + } + + if (NULL == data) + { + data = (FORCEURLDATA*)calloc(1, sizeof(FORCEURLDATA)); + if (NULL == data) return E_OUTOFMEMORY; + data->serviceId = serviceId; + } + + data->url = Plugin_CopyString(pszUrl); + if (NULL == data->url || FALSE == SetProp(hLibrary, FORCEURLPROP, data)) + { + Navigation_RemoveForceUrl(); + return E_FAIL; + } + + return S_OK; +} + +static HRESULT Navigation_GetForceUrl(UINT serviceId, const wchar_t **ppszUrl) +{ + if (NULL == ppszUrl) return E_POINTER; + *ppszUrl = NULL; + + HWND hLibrary = Plugin_GetLibrary(); + if (NULL == hLibrary) return E_FAIL; + + FORCEURLDATA *data = (FORCEURLDATA*)GetProp(hLibrary, FORCEURLPROP); + + if (NULL == data || data->serviceId != serviceId) + return E_NOINTERFACE; + + *ppszUrl = data->url; + return S_OK; +} + +static INT Navigation_GetIconIndex(LPCWSTR pszImage) +{ + HWND hLibrary = Plugin_GetLibrary(); + if (NULL == hLibrary) return -1; + + HMLIMGLST hmlilNavigation = MLNavCtrl_GetImageList(hLibrary); + if (NULL == hmlilNavigation) return -1; + + MLIMAGESOURCE mlis; + ZeroMemory(&mlis, sizeof(mlis)); + mlis.cbSize = sizeof(mlis); + mlis.hInst = NULL; + mlis.bpp = 24; + mlis.lpszName = pszImage; + mlis.type = SRC_TYPE_PNG; + mlis.flags = ISF_FORCE_BPP | ISF_PREMULTIPLY | ISF_LOADFROMFILE; + + MLIMAGELISTITEM item; + ZeroMemory(&item, sizeof(item)); + item.cbSize = sizeof(item); + item.hmlil = hmlilNavigation; + item.filterUID = MLIF_FILTER3_UID; + item.pmlImgSource = &mlis; + + return MLImageList_Add(hLibrary, &item); +} + +static HNAVITEM Navigation_CreateItem(HWND hLibrary, HNAVITEM hParent, OmService *service) +{ + if (NULL == hLibrary || NULL == service) + return NULL; + + WCHAR szName[256] = {0}, szInvariant[64] = {0}; + if (FAILED(service->GetName(szName, ARRAYSIZE(szName)))) + return NULL; + + if (FAILED(StringCchPrintf(szInvariant, ARRAYSIZE(szInvariant), NAVITEM_PREFIX L"%u", service->GetId()))) + return NULL; + + NAVINSERTSTRUCT nis = {0}; + nis.hInsertAfter = NULL; + nis.hParent = hParent; + + WCHAR szIcon[512] = {0}; + INT iIcon = (SUCCEEDED(service->GetIcon(szIcon, ARRAYSIZE(szIcon)))) ? + Navigation_GetIconIndex(szIcon) : -1; + + nis.item.cbSize = sizeof(NAVITEM); + nis.item.mask = NIMF_TEXT | NIMF_STYLE | NIMF_TEXTINVARIANT | NIMF_PARAM; + if (-1 != iIcon) + nis.item.mask |= (NIMF_IMAGE | NIMF_IMAGESEL); + + nis.item.id = 0; + nis.item.pszText = szName; + nis.item.pszInvariant = szInvariant; + nis.item.style = NIS_ALLOWCHILDMOVE; + nis.item.styleMask = nis.item.style; + nis.item.lParam = (LPARAM)service; + nis.item.iImage = iIcon; + nis.item.iSelectedImage = iIcon; + + HNAVITEM hItem = MLNavCtrl_InsertItem(hLibrary, &nis); + if (NULL != hItem) + service->AddRef(); + + return hItem; +} + +static HNAVITEM Navigation_GetMessageItem(INT msg, INT_PTR param1) +{ + HWND hLibrary = Plugin_GetLibrary(); + HNAVITEM hItem = (msg < ML_MSG_NAVIGATION_FIRST) ? MLNavCtrl_FindItemById(hLibrary, param1) : (HNAVITEM)param1; + return hItem; +} + +static HRESULT Navigation_GetService(HWND hLibrary, HNAVITEM hItem, OmService **service) +{ + WCHAR szBuffer[64] = {0}; + + if (NULL == service) return E_POINTER; + *service = NULL; + + if (NULL == hLibrary || NULL == hItem) return E_INVALIDARG; + + NAVITEM itemInfo = {0}; + itemInfo.cbSize = sizeof(NAVITEM); + itemInfo.hItem = hItem; + itemInfo.pszInvariant = szBuffer; + itemInfo.cchInvariantMax = ARRAYSIZE(szBuffer); + itemInfo.mask = NIMF_PARAM | NIMF_TEXTINVARIANT; + + if (FALSE == MLNavItem_GetInfo(hLibrary, &itemInfo)) + return E_FAIL; + + INT cchInvariant = lstrlen(szBuffer); + INT cchPrefix = ARRAYSIZE(NAVITEM_PREFIX) - 1; + if (cchInvariant <= cchPrefix || + CSTR_EQUAL != CompareString(CSTR_INVARIANT, 0, NAVITEM_PREFIX, cchPrefix, szBuffer, cchPrefix)) + { + return E_NAVITEM_UNKNOWN; + } + + *service = (OmService*)itemInfo.lParam; + (*service)->AddRef(); + return S_OK; +} + + +static BOOL CALLBACK Navigation_ItemEnumerator(HNAVITEM hItem, LPARAM param) +{ + if (NULL == hItem) return TRUE; + NAVENUMRESULT *result = (NAVENUMRESULT*)param; + if (NULL == result) return FALSE; + + result->itemInfo .hItem = hItem; + if (FALSE != MLNavItem_GetInfo(result->hLibrary, &result->itemInfo) && + CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, result->itemInfo.pszInvariant, result->cchPrefix, + result->pszPrefix, result->cchPrefix)) + { + OmService *service = (OmService*)result->itemInfo.lParam; + if (NULL != service && service->GetId() == result->serviceId) + { + result->hItem = hItem; + result->service = service; + service->AddRef(); + return FALSE; + } + } + + return TRUE; +} + +static HRESULT Navigation_CreateView(HNAVITEM hItem, HWND hParent, HWND *hView) +{ + if (NULL == hView) return E_POINTER; + *hView = NULL; + + HWND hLibrary = Plugin_GetLibrary(); + if (NULL == hLibrary) return E_UNEXPECTED; + + if (NULL == hItem || NULL == hParent) return E_INVALIDARG; + + HRESULT hr; + + OmService *service = NULL; + hr = Navigation_GetService(hLibrary, hItem, &service); + if (SUCCEEDED(hr)) + { + if (NULL == OMBROWSERMNGR) + hr = E_UNEXPECTED; + + if (SUCCEEDED(hr)) + { + hr = OMBROWSERMNGR->Initialize(NULL, Plugin_GetWinamp()); + if (SUCCEEDED(hr)) + { + LPCWSTR forceUrl; + if (FAILED(Navigation_GetForceUrl(service->GetId(), &forceUrl))) + forceUrl = NULL; + + hr = OMBROWSERMNGR->CreateView(service, hParent, forceUrl, 0, hView); + Navigation_RemoveForceUrl(); + } + } + + wchar_t nowplayingurl[1024] = {0}; + // May 2022 - this service url is dead and would need either fixing up or replacing + lstrcpynW(nowplayingurl, AutoWide(g_config->ReadString("nowplayingurl", "http://client.winamp.com/nowplaying")), ARRAYSIZE(nowplayingurl)); + service->SetUrl(nowplayingurl[0] ? nowplayingurl : SERVICE_HOMEURL); + service->Release(); + } + return hr; +} + +static BOOL Navigation_GetViewRect(RECT *rect) +{ + if (NULL == rect) return FALSE; + + HWND hWinamp = Plugin_GetWinamp(); + HWND hLibrary = Plugin_GetLibrary(); + if (NULL == hWinamp || NULL == hLibrary) + return FALSE; + + HWND hFrame = (HWND)SENDMLIPC(hLibrary, ML_IPC_GETCURRENTVIEW, 0); + if (NULL == hFrame) + hFrame = hLibrary; + + return GetWindowRect(hFrame, rect); +} + +static HRESULT Navigation_CreatePopup(HNAVITEM hItem, HWND *hWindow) +{ + if (NULL == hWindow) return E_POINTER; + *hWindow = NULL; + + HWND hLibrary = Plugin_GetLibrary(); + if (NULL == hLibrary) return E_UNEXPECTED; + + if (NULL == hItem) return E_INVALIDARG; + + HRESULT hr; + + OmService *service; + hr = Navigation_GetService(hLibrary, hItem, &service); + if (SUCCEEDED(hr)) + { + HWND hWinamp = Plugin_GetWinamp(); + + if (NULL == OMBROWSERMNGR) + hr = E_UNEXPECTED; + + if (SUCCEEDED(hr)) + { + hr = OMBROWSERMNGR->Initialize(NULL, hWinamp); + if (SUCCEEDED(hr)) + { + RECT rect; + if (FALSE == Navigation_GetViewRect(&rect)) + hr = E_FAIL; + + if (SUCCEEDED(hr)) + { + rect.left += 16; + rect.top += 16; + + hr = OMBROWSERMNGR->CreatePopup(service, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, hWinamp, NULL, 0, hWindow); + } + } + } + + service->Release(); + } + + return hr; +} + + +static void Navigation_OnDestroy() +{ + Navigation_RemoveForceUrl(); + + if (NULL != OMBROWSERMNGR) + { + OMBROWSERMNGR->Finish(); + } +} + +static void Navigation_OpenPreferences() +{ + winampMediaLibraryPlugin *(*gp)(); + gp = (winampMediaLibraryPlugin * (__cdecl *)(void))GetProcAddress(GetModuleHandle(L"ml_online.dll"), "winampGetMediaLibraryPlugin"); + if (gp) + { + winampMediaLibraryPlugin *mlplugin = gp(); + if (mlplugin && (mlplugin->version >= MLHDR_VER_OLD && mlplugin->version <= MLHDR_VER)) + { + mlplugin->MessageProc(ML_MSG_CONFIG, 0, 0, 0); + } + else + SendMessage(Plugin_GetWinamp(), WM_WA_IPC, (WPARAM)-1, IPC_OPENPREFSTOPAGE); + } + else + SendMessage(Plugin_GetWinamp(), WM_WA_IPC, (WPARAM)-1, IPC_OPENPREFSTOPAGE); +} + +static HRESULT Navigation_ShowContextMenu(HNAVITEM hItem, HWND hHost, POINTS pts) +{ + if (NULL == hItem || NULL == hHost) + return E_INVALIDARG; + + HWND hLibrary = Plugin_GetLibrary(); + if (NULL == hLibrary) return E_UNEXPECTED; + + HRESULT hr; + + OmService *service; + hr = Navigation_GetService(hLibrary, hItem, &service); + if (FAILED(hr)) return hr; + + POINT pt; + POINTSTOPOINT(pt, pts); + if (-1 == pt.x || -1 == pt.y) + { + NAVITEMGETRECT itemRect; + itemRect.fItem = FALSE; + itemRect.hItem = hItem; + if (MLNavItem_GetRect(hLibrary, &itemRect)) + { + MapWindowPoints(hHost, HWND_DESKTOP, (POINT*)&itemRect.rc, 2); + pt.x = itemRect.rc.left + 2; + pt.y = itemRect.rc.top + 2; + } + } + + HMENU hMenu = Menu_GetMenu(MENU_NAVIGATIONCONTEXT); + if (NULL != hMenu) + { + INT commandId = Menu_TrackPopup(hLibrary, hMenu, + TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD, + pt.x, pt.y, hHost, NULL); + + Menu_ReleaseMenu(hMenu, MENU_NAVIGATIONCONTEXT); + + switch(commandId) + { + case ID_NAVIGATION_OPEN: + MLNavItem_Select(hLibrary, hItem); + break; + + case ID_NAVIGATION_OPENNEWWINDOW: + { + HWND hWindow; + if (SUCCEEDED(Navigation_CreatePopup(hItem, &hWindow))) + { + ShowWindow(hWindow, SW_SHOWNORMAL); + } + } + break; + case ID_NAVIGATION_HELP: + SENDWAIPC(Plugin_GetWinamp(), IPC_OPEN_URL, L"https://help.winamp.com/hc/articles/8105304048660-The-Winamp-Media-Library"); + break; + + case ID_PLUGIN_PREFERENCES: + Navigation_OpenPreferences(); + break; + } + } + + service->Release(); + + return hr; +} + +BOOL Navigation_Initialize(void) +{ + OmService *service; + HWND hLibrary = Plugin_GetLibrary(); + + MLNavCtrl_BeginUpdate(hLibrary, NUF_LOCK_TOP); + + if (SUCCEEDED(OmService::CreateInstance(&service))) + { + HNAVITEM hParent = NULL; + Navigation_CreateItem(hLibrary, hParent, service); + service->Release(); + } + + MLNavCtrl_EndUpdate(hLibrary); + + return TRUE; +} + +static void Navigation_OnDeleteItem(HNAVITEM hItem) +{ + if (NULL == hItem) return; + + HWND hLibrary = Plugin_GetLibrary(); + if (NULL == hLibrary) return; + + OmService *service; + if (SUCCEEDED(Navigation_GetService(hLibrary, hItem, &service))) + { + + NAVITEM itemInfo; + itemInfo.cbSize = sizeof(NAVITEM); + itemInfo.hItem = hItem; + itemInfo.mask = NIMF_PARAM; + itemInfo.lParam = 0L; + MLNavItem_SetInfo(hLibrary, &itemInfo); + + service->Release(); // create + service->Release(); // Navigation_GetService + } +} + +BOOL Navigation_ProcessMessage(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3, INT_PTR *result) +{ + if (msg == ML_MSG_NO_CONFIG) + { + if (!GetModuleHandle(L"ml_online.dll")) + { + *result = TRUE; + return TRUE; + } + } + else if (msg == ML_MSG_CONFIG) + { + Navigation_OpenPreferences(); + *result = TRUE; + return TRUE; + } + + if (msg < ML_MSG_TREE_BEGIN || msg > ML_MSG_TREE_END) + return FALSE; + + switch(msg) + { + case ML_MSG_TREE_ONCREATEVIEW: + { + HWND hView; + HNAVITEM hItem = Navigation_GetMessageItem(msg, param1); + HRESULT hr = Navigation_CreateView(hItem, (HWND)param2, &hView); + if (SUCCEEDED(hr)) + { + *result = (INT_PTR)hView; + return TRUE; + } + } + break; + + case ML_MSG_NAVIGATION_ONDESTROY: + Navigation_OnDestroy(); + break; + + case ML_MSG_NAVIGATION_CONTEXTMENU: + { + HNAVITEM hItem = Navigation_GetMessageItem(msg, param1); + HRESULT hr = Navigation_ShowContextMenu(hItem, (HWND)param2, MAKEPOINTS(param3)); + if (SUCCEEDED(hr)) + { + *result = TRUE; + return TRUE; + } + } + break; + + case ML_MSG_NAVIGATION_ONDELETE: + { + HNAVITEM hItem = Navigation_GetMessageItem(msg, param1); + Navigation_OnDeleteItem(hItem); + break; + } + } + + return FALSE; +} + +HNAVITEM Navigation_FindService(UINT serviceId, OmService **serviceOut) +{ + NAVENUMRESULT result; + result.hItem = NULL; + result.service = NULL; + + result.serviceId = serviceId; + result.pszPrefix = NAVITEM_PREFIX; + result.cchPrefix = lstrlen(result.pszPrefix); + + result.hLibrary = Plugin_GetLibrary(); + result.itemInfo.cbSize = sizeof(result.itemInfo); + result.itemInfo.mask = NIMF_TEXTINVARIANT | NIMF_PARAM; + result.itemInfo.cchInvariantMax = ARRAYSIZE(result.szBuffer); + result.itemInfo.pszInvariant = result.szBuffer; + + NAVCTRLENUMPARAMS param; + param.enumProc = Navigation_ItemEnumerator; + param.hItemStart = NULL; + param.lParam = (LPARAM)&result; + + if (NULL != result.hLibrary) + MLNavCtrl_EnumItems(result.hLibrary, ¶m); + + if (NULL != serviceOut) + *serviceOut = result.service; + else if (NULL != result.service) + result.service->Release(); + + return result.hItem; +} + +HRESULT Navigation_ShowService(UINT serviceId, LPCWSTR pszUrl, UINT navFlags) +{ + HWND hLibrary = Plugin_GetLibrary(); + if (NULL == hLibrary) + return E_FAIL; + + OmService *service; + HNAVITEM hItem = Navigation_FindService(serviceId, &service); + if (NULL == hItem) + return E_FAIL; + + OmService *activeService; + HWND hView = Navigation_GetActiveView(&activeService); + if (NULL == hView || activeService->GetId() != service->GetId()) + { + hView = NULL; + activeService = NULL; + } + + HRESULT hr = S_OK; + + if (NULL != hView) + { + if (NULL == pszUrl && 0 != (NAVFLAG_FORCEACTIVE & navFlags)) + pszUrl = NAVIGATE_HOME; + + if (NULL != pszUrl && FALSE == BrowserView_Navigate(hView, pszUrl, TRUE)) + hr = E_FAIL; + } + else + { + if (NULL != pszUrl) + hr = Navigation_SetForceUrl(serviceId, pszUrl); + else + Navigation_RemoveForceUrl(); + + if (SUCCEEDED(hr) && FALSE == MLNavItem_Select(hLibrary, hItem)) + { + Navigation_RemoveForceUrl(); + hr = E_FAIL; + } + } + + if (SUCCEEDED(hr)) + { + if (0 != (NAVFLAG_ENSUREITEMVISIBLE & navFlags)) + MLNavItem_EnsureVisible(hLibrary, hItem); + + if (0 != (NAVFLAG_ENSUREMLVISIBLE & navFlags)) + SENDMLIPC(hLibrary, ML_IPC_ENSURE_VISIBLE, 0L); + } + + service->Release(); + if (NULL != activeService) + activeService->Release(); + + return hr; + +} +HNAVITEM Navigation_GetActive(OmService **serviceOut) +{ + HWND hLibrary = Plugin_GetLibrary(); + + OmService *service; + HNAVITEM hActive = (NULL != hLibrary) ? MLNavCtrl_GetSelection(hLibrary) : NULL; + if (NULL == hActive || FAILED(Navigation_GetService(hLibrary, hActive, &service))) + { + hActive = NULL; + service = NULL; + } + + if (NULL != serviceOut) + *serviceOut = service; + + return hActive; +} + +HWND Navigation_GetActiveView(OmService **serviceOut) +{ + HWND hLibrary = Plugin_GetLibrary(); + if (NULL == hLibrary) + { + if (NULL != serviceOut) *serviceOut = NULL; + return NULL; + } + + + HWND hView =((HWND)SENDMLIPC(hLibrary, ML_IPC_GETCURRENTVIEW, 0)); + if (NULL != hView) + { + WCHAR szBuffer[128] = {0}; + if (!GetClassName(hView, szBuffer, ARRAYSIZE(szBuffer)) || CSTR_EQUAL != CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, szBuffer, -1, L"Nullsoft_omBrowserView", -1)) + hView = NULL; + } + + OmService *service; + HNAVITEM hActive = (NULL != hLibrary) ? MLNavCtrl_GetSelection(hLibrary) : NULL; + if (NULL == hView || FALSE == BrowserView_GetService(hView, &service)) + { + hView = NULL; + service = NULL; + } + + if (NULL != serviceOut) + *serviceOut = service; + else if (NULL != service) + service->Release(); + + return hView; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_nowplaying/navigation.h b/Src/Plugins/Library/ml_nowplaying/navigation.h new file mode 100644 index 00000000..d37264db --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/navigation.h @@ -0,0 +1,27 @@ +#ifndef NULLSOFT_NOWPLAYING_PLUGIN_NAVIGATION_HEADER +#define NULLSOFT_NOWPLAYING_PLUGIN_NAVIGATION_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +typedef LPVOID HNAVITEM; +class OmService; + + +BOOL Navigation_Initialize(void); +BOOL Navigation_ProcessMessage(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3, INT_PTR *result); + +#define NAVFLAG_NORMAL 0x0000 +#define NAVFLAG_ENSUREITEMVISIBLE 0x0001 +#define NAVFLAG_ENSUREMLVISIBLE 0x0002 +#define NAVFLAG_FORCEACTIVE 0x0004 + +HRESULT Navigation_ShowService(UINT serviceId, LPCWSTR pszUrl, UINT navFlags); +HNAVITEM Navigation_FindService(UINT serviceId, OmService **serviceOut); +HNAVITEM Navigation_GetActive(OmService **serviceOut); +HWND Navigation_GetActiveView(OmService **serviceOut); + +#endif //NULLSOFT_NOWPLAYING_PLUGIN_NAVIGATION_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_nowplaying/png.rc b/Src/Plugins/Library/ml_nowplaying/png.rc new file mode 100644 index 00000000..ac25fbdd --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/png.rc @@ -0,0 +1,7 @@ +#include "resource.h" +///////////////////////////////////////////////////////////////////////////// +// +// Data +// +IDR_SERVICE_ICON RCDATA +".\\resources\\serviceIcon.png"
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_nowplaying/resource.h b/Src/Plugins/Library/ml_nowplaying/resource.h new file mode 100644 index 00000000..74da0171 --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/resource.h @@ -0,0 +1,23 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by ml_nowplaying.rc +// +#define IDS_SERVICE_NAME 1 +#define IDR_CONTEXTMENU 101 +#define IDR_SERVICE_ICON 20000 +#define ID_NAVIGATION_OPENNEWWINDOW 40000 +#define ID_NAVIGATION_OPEN 40001 +#define ID_NAVIGATION_HELP 40005 +#define ID_PLUGIN_PREFERENCES 40006 +#define IDS_PLUGIN_NAME 65534 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 105 +#define _APS_NEXT_COMMAND_VALUE 40007 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/Plugins/Library/ml_nowplaying/resources/serviceIcon.png b/Src/Plugins/Library/ml_nowplaying/resources/serviceIcon.png Binary files differnew file mode 100644 index 00000000..13d47f3b --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/resources/serviceIcon.png diff --git a/Src/Plugins/Library/ml_nowplaying/service.cpp b/Src/Plugins/Library/ml_nowplaying/service.cpp new file mode 100644 index 00000000..5d37ec21 --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/service.cpp @@ -0,0 +1,171 @@ +#include "main.h" +#include "./service.h" +#include "./wasabi.h" +#include "./resource.h" +#include "../replicant/nu/Autowide.h" +#include "../winamp/wa_ipc.h" +#include <strsafe.h> + +#define IS_INVALIDISPATCH(__disp) (((IDispatch *)1) == (__disp) || NULL == (__disp)) + +OmService::OmService(UINT nId) + : ref(1), id(nId), name(NULL), url(NULL), icon(NULL) +{ +} + +OmService::~OmService() +{ + Plugin_FreeResString(name); + Plugin_FreeString(url); + Plugin_FreeResString(icon); +} + +HRESULT OmService::CreateInstance(OmService **instance) +{ + if (NULL == instance) return E_POINTER; + *instance = NULL; + + OmService *service = new OmService(SERVICE_ID); + if (NULL == service) return E_OUTOFMEMORY; + + wchar_t nowplayingurl[1024] = {0}; + lstrcpynW(nowplayingurl, AutoWide(g_config->ReadString("nowplayingurl","")), ARRAYSIZE(nowplayingurl)); + + service->SetName(MAKEINTRESOURCE(IDS_SERVICE_NAME)); + service->SetUrl((nowplayingurl[0] ? nowplayingurl : SERVICE_HOMEURL)); + service->SetIcon(MAKEINTRESOURCE(IDR_SERVICE_ICON)); + + *instance = service; + return S_OK; +} + +size_t OmService::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +size_t OmService::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +int OmService::QueryInterface(GUID interface_guid, void **object) +{ + if (NULL == object) return E_POINTER; + + if (IsEqualIID(interface_guid, IFC_OmService)) + *object = static_cast<ifc_omservice*>(this); + else + { + *object = NULL; + return E_NOINTERFACE; + } + + if (NULL == *object) + return E_UNEXPECTED; + + AddRef(); + return S_OK; +} + +unsigned int OmService::GetId() +{ + return id; +} + +HRESULT OmService::GetName(wchar_t *pszBuffer, int cchBufferMax) +{ + return Plugin_CopyResString(pszBuffer, cchBufferMax, name); +} + +HRESULT OmService::GetUrl(wchar_t *pszBuffer, int cchBufferMax) +{ + return StringCchCopyEx(pszBuffer, cchBufferMax, url, NULL, NULL, STRSAFE_IGNORE_NULLS); +} + +HRESULT OmService::GetIcon(wchar_t *pszBuffer, int cchBufferMax) +{ + if (NULL != icon && IS_INTRESOURCE(icon)) + { + WCHAR szPath[2*MAX_PATH] = {0}; + if (0 == GetModuleFileName(Plugin_GetInstance(), szPath, ARRAYSIZE(szPath))) + return E_FAIL; + + return StringCchPrintf(pszBuffer, cchBufferMax, L"res://%s/#%d/#%d", szPath, RT_RCDATA, icon); + } + + return StringCchCopyEx(pszBuffer, cchBufferMax, icon, NULL, NULL, STRSAFE_IGNORE_NULLS); +} + +HRESULT OmService::GetExternal(IDispatch **ppDispatch) +{ + if (NULL == ppDispatch) + return E_POINTER; + + *ppDispatch = NULL; + + HWND hWinamp = Plugin_GetWinamp(); + if (NULL == hWinamp) + return E_UNEXPECTED; + + // So far we do not use JSAPI2 in nowplaying + // // try JSAPI2 first + // WCHAR szBuffer[64] = {0}; + // if (SUCCEEDED(StringCchPrintfW(szBuffer, ARRAYSIZE(szBuffer), L"%u", id))) + // *ppDispatch = (IDispatch*)SENDWAIPC(hWinamp, IPC_JSAPI2_GET_DISPATCH_OBJECT, (WPARAM)szBuffer); + + if (IS_INVALIDISPATCH(*ppDispatch)) + { // try JSAPI1 + *ppDispatch = (IDispatch*)SENDWAIPC(hWinamp, IPC_GET_DISPATCH_OBJECT, 0); + if (IS_INVALIDISPATCH(*ppDispatch)) + { // Fail + *ppDispatch = NULL; + return E_FAIL; + } + } + + return S_OK; +} + +HRESULT OmService::SetName(LPCWSTR pszName) +{ + Plugin_FreeResString(name); + name = Plugin_DuplicateResString(pszName); + return S_OK; +} + +HRESULT OmService::SetUrl(LPCWSTR pszUrl) +{ + Plugin_FreeString(url); + url = Plugin_CopyString(pszUrl); + return S_OK; +} + +HRESULT OmService::SetIcon(LPCWSTR pszIcon) +{ + Plugin_FreeResString(icon); + icon = Plugin_DuplicateResString(pszIcon); + return S_OK; +} + +#define CBCLASS OmService +START_DISPATCH; +CB(ADDREF, AddRef) +CB(RELEASE, Release) +CB(QUERYINTERFACE, QueryInterface) +CB(API_GETID, GetId) +CB(API_GETNAME, GetName) +CB(API_GETURL, GetUrl) +CB(API_GETICON, GetIcon) +CB(API_GETEXTERNAL, GetExternal) +END_DISPATCH; +#undef CBCLASS + + diff --git a/Src/Plugins/Library/ml_nowplaying/service.h b/Src/Plugins/Library/ml_nowplaying/service.h new file mode 100644 index 00000000..292a4628 --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/service.h @@ -0,0 +1,53 @@ +#ifndef NULLSOFT_NOWPLAYING_PLUGIN_SERVICE_HEADER +#define NULLSOFT_NOWPLAYING_PLUGIN_SERVICE_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include <ifc_omservice.h> + +#define SERVICE_ID 101 +#define SERVICE_HOMEURL L"http://client.winamp.com/nowplaying?v=5.9&icid=navigationtree" + +class OmService : public ifc_omservice +{ + +protected: + OmService(UINT nId); + ~OmService(); + +public: + static HRESULT CreateInstance(OmService **instance); + +public: + /* Dispatchable */ + size_t AddRef(); + size_t Release(); + int QueryInterface(GUID interface_guid, void **object); + + /* ifc_omservice */ + unsigned int GetId(); + HRESULT GetName(wchar_t *pszBuffer, int cchBufferMax); + HRESULT GetUrl(wchar_t *pszBuffer, int cchBufferMax); + HRESULT GetExternal(IDispatch **ppDispatch); + HRESULT GetIcon(wchar_t *pszBuffer, int cchBufferMax); + +public: + HRESULT SetName(LPCWSTR pszName); + HRESULT SetUrl(LPCWSTR pszUrl); + HRESULT SetIcon(LPCWSTR pszIcon); + +protected: + RECVS_DISPATCH; + +protected: + ULONG ref; + UINT id; + LPWSTR name; + LPWSTR url; + LPWSTR icon; +}; + +#endif //NULLSOFT_NOWPLAYING_PLUGIN_SERVICE_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_nowplaying/version.rc2 b/Src/Plugins/Library/ml_nowplaying/version.rc2 new file mode 100644 index 00000000..47a11f64 --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "../../../Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 4,0,1,0 + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp Media Library Plug-in" + VALUE "FileVersion", "4,0,1,0" + VALUE "InternalName", "Nullsoft Nowplaying" + VALUE "LegalCopyright", "Copyright © 2003-2023 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "ml_nowplaying.dll" + VALUE "ProductName", "Winamp" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/Src/Plugins/Library/ml_nowplaying/wasabi.cpp b/Src/Plugins/Library/ml_nowplaying/wasabi.cpp new file mode 100644 index 00000000..7a071143 --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/wasabi.cpp @@ -0,0 +1,82 @@ +#include "main.h" +#include "./wasabi.h" +#include <api/service/waservicefactory.h> + +static ULONG wasabiRef = 0; + +api_application *WASABI_API_APP = NULL; +api_language *WASABI_API_LNG = NULL; +JSAPI2::api_security *AGAVE_API_JSAPI2_SECURITY = NULL; +obj_ombrowser *browserManager = NULL; +api_syscb *WASABI_API_SYSCB = NULL; +ifc_omutility *omUtility = NULL; + +HINSTANCE WASABI_API_LNG_HINST = NULL; +HINSTANCE WASABI_API_ORIG_HINST = NULL; +EXTERN_C winampMediaLibraryPlugin plugin; + +void *Wasabi_QueryInterface(REFGUID interfaceGuid) +{ + waServiceFactory *serviceFactory = plugin.service->service_getServiceByGuid(interfaceGuid); + return (NULL != serviceFactory) ? serviceFactory->getInterface() : NULL; +} + +void Wasabi_ReleaseInterface(REFGUID interfaceGuid, void *pInstance) +{ + waServiceFactory *serviceFactory = plugin.service->service_getServiceByGuid(interfaceGuid); + if (NULL != serviceFactory) serviceFactory->releaseInterface(pInstance); +} + +void Wasabi_SafeRelease(Dispatchable *pDisp) +{ + if (NULL != pDisp) + pDisp->Release(); +} + +BOOL WasabiApi_Initialize(HINSTANCE hInstance) +{ + WASABI_API_APP = QueryWasabiInterface(api_application, applicationApiServiceGuid); + WASABI_API_SYSCB = QueryWasabiInterface(api_syscb, syscbApiServiceGuid); + WASABI_API_LNG = QueryWasabiInterface(api_language, languageApiGUID); + AGAVE_API_JSAPI2_SECURITY = QueryWasabiInterface(JSAPI2::api_security, JSAPI2::api_securityGUID); + OMBROWSERMNGR = QueryWasabiInterface(obj_ombrowser, OBJ_OmBrowser); + OMUTILITY = QueryWasabiInterface(ifc_omutility, IFC_OmUtility); + + if (NULL != WASABI_API_LNG) + WASABI_API_START_LANG(hInstance, MlNowPlayingLangGUID); + + WasabiApi_AddRef(); + return TRUE; +} + +static void WasabiApi_Uninitialize() +{ + ReleaseWasabiInterface(applicationApiServiceGuid, WASABI_API_APP); + ReleaseWasabiInterface(syscbApiServiceGuid, WASABI_API_SYSCB); + ReleaseWasabiInterface(languageApiGUID, WASABI_API_LNG); + ReleaseWasabiInterface(JSAPI2::api_securityGUID, AGAVE_API_JSAPI2_SECURITY); + ReleaseWasabiInterface(OBJ_OmBrowser, OMBROWSERMNGR); + ReleaseWasabiInterface(IFC_OmUtility, OMUTILITY); + + WASABI_API_APP = NULL; + WASABI_API_LNG = NULL; + AGAVE_API_JSAPI2_SECURITY = NULL; +} + +ULONG WasabiApi_AddRef() +{ + return InterlockedIncrement((LONG*)&wasabiRef); +} + +ULONG WasabiApi_Release() +{ + if (0 == wasabiRef) + return wasabiRef; + + LONG r = InterlockedDecrement((LONG*)&wasabiRef); + if (0 == r) + { + WasabiApi_Uninitialize(); + } + return r; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_nowplaying/wasabi.h b/Src/Plugins/Library/ml_nowplaying/wasabi.h new file mode 100644 index 00000000..72d17983 --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/wasabi.h @@ -0,0 +1,42 @@ +#ifndef NULLSOFT_NOWPLAYING_PLUGIN_WASABI_HEADER +#define NULLSOFT_NOWPLAYING_PLUGIN_WASABI_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +#include <api/application/api_application.h> +#define WASABI_API_APP applicationApi + +#include "../Agave/Language/api_language.h" + +#include "../Winamp/JSAPI2_api_security.h" +extern JSAPI2::api_security *jsapi2_securityApi; +#define AGAVE_API_JSAPI2_SECURITY jsapi2_securityApi + +#include <obj_ombrowser.h> +extern obj_ombrowser *browserManager; +#define OMBROWSERMNGR browserManager + +#include <api/syscb/api_syscb.h> +#define WASABI_API_SYSCB sysCallbackApi + +#include <ifc_omutility.h> +extern ifc_omutility *omUtility; +#define OMUTILITY omUtility + +BOOL WasabiApi_Initialize(HINSTANCE hInstance); +ULONG WasabiApi_AddRef(void); +ULONG WasabiApi_Release(void); + +void *Wasabi_QueryInterface(REFGUID interfaceGuid); +void Wasabi_ReleaseInterface(REFGUID interfaceGuid, void *pInstance); + +#define QueryWasabiInterface(__interfaceType, __interfaceGuid) ((##__interfaceType*)Wasabi_QueryInterface(__interfaceGuid)) +#define ReleaseWasabiInterface(__interfaceGuid, __interfaceInstance) (Wasabi_ReleaseInterface((__interfaceGuid), (__interfaceInstance))) + +void Wasabi_SafeRelease(Dispatchable *pDisp); + +#endif // NULLSOFT_NOWPLAYING_PLUGIN_WASABI_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_nowplaying/wasabiCallback.cpp b/Src/Plugins/Library/ml_nowplaying/wasabiCallback.cpp new file mode 100644 index 00000000..54e9d3a0 --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/wasabiCallback.cpp @@ -0,0 +1,90 @@ +#include "main.h" +#include "./wasabiCallback.h" +#include "./navigation.h" +#include "./service.h" +#include "../replicant/nu/Autowide.h" + +WasabiCallback::WasabiCallback() + : ref(1) +{ +} + +WasabiCallback::~WasabiCallback() +{ +} + +HRESULT WasabiCallback::CreateInstance(WasabiCallback **instance) +{ + if (NULL == instance) return E_POINTER; + + *instance = new WasabiCallback(); + if (NULL == *instance) return E_OUTOFMEMORY; + + return S_OK; +} + +size_t WasabiCallback::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +size_t WasabiCallback::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +int WasabiCallback::QueryInterface(GUID interface_guid, void **object) +{ + return 0; +} + +FOURCC WasabiCallback::GetEventType() +{ + return SysCallback::BROWSER; +} + +int WasabiCallback::Notify(int msg, intptr_t param1, intptr_t param2) +{ + switch (msg) + { + case BrowserCallback::ONOPENURL: + return OpenURL(reinterpret_cast<const wchar_t*>(param1), reinterpret_cast<bool *>(param2)); + } + return 0; +} + +int WasabiCallback::OpenURL(const wchar_t *url, bool *override) +{ + WCHAR szTemplate[1024] = L"http://client.winamp.com/nowplaying"; + INT cchTemplate = ARRAYSIZE(szTemplate) - 1; + lstrcpynW(szTemplate, AutoWide(g_config->ReadString("nowplayingurl", "http://client.winamp.com/nowplaying")), ARRAYSIZE(szTemplate)); + + if (NULL != url && + CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, url, cchTemplate, szTemplate, cchTemplate)) + { + if (SUCCEEDED(Navigation_ShowService(SERVICE_ID, url, + NAVFLAG_FORCEACTIVE | NAVFLAG_ENSUREMLVISIBLE | NAVFLAG_ENSUREITEMVISIBLE))) + { + *override = true; + return 1; + } + } + return 0; +} + +#define CBCLASS WasabiCallback +START_DISPATCH; + CB(ADDREF, AddRef); + CB(RELEASE, Release); + CB(QUERYINTERFACE, QueryInterface); + CB(SYSCALLBACK_GETEVENTTYPE, GetEventType); + CB(SYSCALLBACK_NOTIFY, Notify); +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_nowplaying/wasabiCallback.h b/Src/Plugins/Library/ml_nowplaying/wasabiCallback.h new file mode 100644 index 00000000..3add0b9b --- /dev/null +++ b/Src/Plugins/Library/ml_nowplaying/wasabiCallback.h @@ -0,0 +1,42 @@ +#ifndef NULLSOFT_NOWPLAYING_PLUGIN_WASABI_CALLBACK_HEADER +#define NULLSOFT_NOWPLAYING_PLUGIN_WASABI_CALLBACK_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <api/syscb/callbacks/syscb.h> +#include <api/syscb/callbacks/browsercb.h> + +class WasabiCallback : public SysCallback +{ +protected: + WasabiCallback(); + ~WasabiCallback(); + +public: + static HRESULT CreateInstance(WasabiCallback **instance); + +public: + /*** Dispatchable ***/ + size_t AddRef(); + size_t Release(); + int QueryInterface(GUID interface_guid, void **object); + + /*** SysCallback ***/ + FOURCC GetEventType(); + int Notify(int msg, intptr_t param1 = 0, intptr_t param2 = 0); + +protected: + // set *override = true to prevent the URL from being opened + // leave it alone otherwise (in case someone else wanted to override it) + int OpenURL(const wchar_t *url, bool *override); + +protected: + RECVS_DISPATCH; + +protected: + ULONG ref; +}; + +#endif //NULLSOFT_NOWPLAYING_PLUGIN_WASABI_CALLBACK_HEADER
\ No newline at end of file |