NowPlayingPlugin:

- Added interface to receive MSN/Windows Live Messenger 'Listening to' notifications sent by various players (e.g Media Player Classic)
- Changes to CAD interface to allow proper supports for players other than MusicBee (e.g. VLC)
- Fixed crash when using the WinAmp interface in multiple skins
This commit is contained in:
Birunthan Mohanathas 2011-06-12 10:05:37 +00:00
parent 7163823971
commit 67cc4c7c6c
11 changed files with 397 additions and 45 deletions

View File

@ -25,6 +25,7 @@
#include "PlayerITunes.h"
#include "PlayerSpotify.h"
#include "PlayerWinamp.h"
#include "PlayerWLM.h"
#include "PlayerWMP.h"
CPlayer* g_AIMP = NULL;
@ -33,11 +34,13 @@ CPlayer* g_Foobar = NULL;
CPlayer* g_iTunes = NULL;
CPlayer* g_Spotify = NULL;
CPlayer* g_Winamp = NULL;
CPlayer* g_WLM = NULL;
CPlayer* g_WMP = NULL;
static MeasureMap g_Values;
static bool g_DisableLeazingZero = false;
std::wstring g_CachePath;
std::wstring g_SettingsFile;
void SecondsToTime(UINT seconds, WCHAR* buffer)
{
@ -69,11 +72,21 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
{
if (g_Values.empty())
{
// Get path to temporary folder (for cover art cache)
WCHAR buffer[MAX_PATH];
GetTempPath(MAX_PATH, buffer);
wcscat(buffer, L"Rainmeter-Cache\\");
CreateDirectory(buffer, NULL);
g_CachePath = buffer;
// Get path to Plugins.ini (usually %APPDATA%\Rainmeter\Plugins.ini)
std::wstring str = PluginBridge(L"getconfig", iniFile);
if (!str.empty())
{
str += L" \"SETTINGSPATH\"";
g_SettingsFile = PluginBridge(L"getvariable", str.c_str());
g_SettingsFile += L"Plugins.ini";
}
}
UINT maxValue = 0;
@ -124,6 +137,14 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
}
data->player = g_AIMP;
}
else if (_wcsicmp(L"CAD", str) == 0)
{
if (!g_CAD)
{
g_CAD = new CPlayerCAD();
}
data->player = g_CAD;
}
else if (_wcsicmp(L"foobar2000", str) == 0)
{
if (!g_Foobar)
@ -148,14 +169,6 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
}
data->player = g_Winamp;
}
else if (_wcsicmp(L"MusicBee", str) == 0)
{
if (!g_CAD)
{
g_CAD = new CPlayerCAD();
}
data->player = g_CAD;
}
else if (_wcsicmp(L"Spotify", str) == 0)
{
if (!g_Spotify)
@ -172,6 +185,14 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
}
data->player = g_Winamp;
}
else if (_wcsicmp(L"WLM", str) == 0)
{
if (!g_WLM)
{
g_WLM = new CPlayerWLM();
}
data->player = g_WLM;
}
else if (_wcsicmp(L"WMP", str) == 0)
{
if (!g_WMP)
@ -182,6 +203,12 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
}
else
{
if (_wcsicmp(L"MusicBee", str) == 0)
{
// TODO: Remove this in a few weeks (left here for MusicBee backwards compatibility)
MessageBox(NULL, L"Due to some internal changes in the NowPlaying plugin, PlayerName=MusicBee is not valid any longer.\n\nPlease edit the skin and change to PlayerName=CAD to continue use with MusicBee.", L"NowPlaying", MB_OK | MB_ICONINFORMATION | MB_TOPMOST);
}
std::wstring error = L"NowPlayingPlugin: PlayerName=";
error += str;
error += L" in section [";
@ -463,7 +490,6 @@ void ExecuteBang(LPCTSTR bang, UINT id)
{
if (wcsnicmp(bang, L"SetPosition", 11) == 0)
{
//(player->GetPosition() * 100) / player->GetDuration()
int position = (_wtoi(arg) * player->GetDuration()) / 100;
if (arg[0] == L'+' || arg[0] == L'-')
{
@ -505,7 +531,7 @@ void ExecuteBang(LPCTSTR bang, UINT id)
UINT GetPluginVersion()
{
// Major * 1000 + Minor
return 1000;
return 1001;
}
/*

View File

@ -20,10 +20,11 @@
#include "PlayerCAD.h"
#include "CAD/cad_sdk.h"
// This player emulates the CD Art Display IPC API. For now, only MusicBee
// is supported, but this can easily be extended.
extern CPlayer* g_CAD;
extern std::wstring g_SettingsFile;
// This player emulates the CD Art Display IPC interface, which is supported by
// MusicBee, VLC (with libcad plugin), and possibly others.
/*
** CPlayerCAD
@ -111,22 +112,44 @@ void CPlayerCAD::Initialize()
hInstance,
this);
HWND wnd = FindWindow(L"WindowsForms10.Window.8.app.0.378734a", NULL);
if (wnd)
WCHAR buffer[MAX_PATH];
LPCTSTR file = g_SettingsFile.c_str();
// Read saved settings
GetPrivateProfileString(L"NowPlaying.dll", L"ClassName", NULL, buffer, MAX_PATH, file);
std::wstring className = buffer;
GetPrivateProfileString(L"NowPlaying.dll", L"WindowName", NULL, buffer, MAX_PATH, file);
std::wstring windowName = buffer;
GetPrivateProfileString(L"NowPlaying.dll", L"PlayerPath", NULL, buffer, MAX_PATH, file);
m_PlayerPath = buffer;
LPCTSTR classSz = className.empty() ? NULL : className.c_str();
LPCTSTR windowSz = windowName.empty() ? NULL : windowName.c_str();
if (classSz || windowSz)
{
// Let's check if it's MusicBee
WCHAR buffer[256];
GetWindowText(wnd, buffer, 256);
if (wcsstr(buffer, L"MusicBee"))
m_PlayerWindow = FindWindow(classSz, windowSz);
}
else
{
// TODO: Remove this in a few weeks (left here for MusicBee backwards compatibility)
m_PlayerWindow = FindWindow(L"WindowsForms10.Window.8.app.0.378734a", NULL);
if (m_PlayerWindow)
{
m_PlayerWindow = wnd;
SendMessage(m_PlayerWindow, WM_USER, (WPARAM)m_Window, IPC_SET_CALLBACK_HWND);
SendMessage(m_PlayerWindow, WM_USER, 0, IPC_GET_CURRENT_TRACK);
m_State = (PLAYERSTATE)SendMessage(m_PlayerWindow, WM_USER, 0, IPC_GET_PLAYER_STATE);
WritePrivateProfileString(L"NowPlaying.dll", L"ClassName", L"WindowsForms10.Window.8.app.0.378734a", file);
}
else
}
if (m_PlayerWindow)
{
SendMessage(m_PlayerWindow, WM_USER, (WPARAM)m_Window, IPC_SET_CALLBACK_HWND);
m_State = (PLAYERSTATE)SendMessage(m_PlayerWindow, WM_USER, 0, IPC_GET_PLAYER_STATE);
if (m_State != PLAYER_STOPPED)
{
LSLog(LOG_DEBUG, L"Rainmeter", L"NowPlayingPlugin: MusicBee class found with invalid title.");
SendMessage(m_PlayerWindow, WM_USER, 0, IPC_GET_CURRENT_TRACK);
}
}
}
@ -220,8 +243,7 @@ LRESULT CALLBACK CPlayerCAD::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM
data.erase(0, ++len);
len = data.find_first_of(L'\t');
UINT rating = _wtoi(data.substr(0, len).c_str()) + 1;
rating /= 2; // From 0 - 10 to 0 - 5
UINT rating = (_wtoi(data.substr(0, len).c_str()) + 1) / 2; // From 0 - 10 to 0 - 5
p->m_Rating = rating;
data.erase(0, ++len);
@ -248,8 +270,25 @@ LRESULT CALLBACK CPlayerCAD::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM
p->m_PlayerPath.assign(data, 0, len);
data.erase(0, ++len);
p->m_PlayerWindow = FindWindow(className.empty() ? NULL : className.c_str(),
windowName.empty() ? NULL : windowName.c_str());
LPCTSTR classSz = className.empty() ? NULL : className.c_str();
LPCTSTR windowSz = windowName.empty() ? NULL : windowName.c_str();
LPCTSTR file = g_SettingsFile.c_str();
WritePrivateProfileString(L"NowPlaying.dll", L"ClassName", classSz, file);
WritePrivateProfileString(L"NowPlaying.dll", L"WindowName", windowSz, file);
WritePrivateProfileString(L"NowPlaying.dll", L"PlayerPath", p->m_PlayerPath.c_str(), file);
p->m_PlayerWindow = FindWindow(classSz, windowSz);
if (p->m_PlayerWindow)
{
p->m_State = (PLAYERSTATE)SendMessage(p->m_PlayerWindow, WM_USER, 0, IPC_GET_PLAYER_STATE);
if (p->m_State != PLAYER_STOPPED)
{
SendMessage(p->m_PlayerWindow, WM_USER, 0, IPC_GET_CURRENT_TRACK);
}
}
}
}
}
@ -436,6 +475,10 @@ void CPlayerCAD::ClosePlayer()
*/
void CPlayerCAD::OpenPlayer()
{
if (!m_PlayerPath.empty())
{
ShellExecute(NULL, L"open", m_PlayerPath.c_str(), NULL, NULL, SW_SHOW);
}
}
/*

View File

@ -16,8 +16,8 @@
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __PLAYERIPC_H__
#define __PLAYERIPC_H__
#ifndef __PLAYERCAD_H__
#define __PLAYERCAD_H__
#include "Player.h"
@ -50,8 +50,8 @@ private:
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
bool m_HasCoverMeasure;
HWND m_Window; // Our reciever window
HWND m_PlayerWindow; // CAD receiver window
HWND m_Window;
HWND m_PlayerWindow;
};
#endif

View File

@ -94,7 +94,7 @@ void CPlayerFoobar::Initialize()
wc.lpszClassName = L"NowPlayingFoobarClass";
RegisterClass(&wc);
// Create dummy window
// Create window
m_Window = CreateWindow(L"NowPlayingFoobarClass",
L"ReceiverWindow",
WS_DISABLED,

View File

@ -0,0 +1,224 @@
/*
Copyright (C) 2011 Birunthan Mohanathas (www.poiru.net)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "StdAfx.h"
#include "PlayerWLM.h"
extern CPlayer* g_WLM;
// This player emulates the MSN/WLM Messenger 'Listening to' interface, which is
// supported by OpenPandora, Last.fm, Media Player Classic, TTPlayer, etc.
/*
** CPlayerWLM
**
** Constructor.
**
*/
CPlayerWLM::CPlayerWLM() : CPlayer(),
m_Window()
{
HINSTANCE hInstance = GetModuleHandle(NULL);
// Create windows class
WNDCLASS wc = {0};
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = L"MsnMsgrUIManager";
RegisterClass(&wc);
// Create dummy window
m_Window = CreateWindow(L"MsnMsgrUIManager",
L"",
WS_DISABLED,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
this);
}
/*
** ~CPlayerWLM
**
** Destructor.
**
*/
CPlayerWLM::~CPlayerWLM()
{
DestroyWindow(m_Window);
UnregisterClass(L"MsnMsgrUIManager", GetModuleHandle(NULL));
}
/*
** AddInstance
**
** Called during initialization of each measure.
**
*/
void CPlayerWLM::AddInstance(MEASURETYPE type)
{
++m_InstanceCount;
}
/*
** RemoveInstance
**
** Called during destruction of each measure.
**
*/
void CPlayerWLM::RemoveInstance()
{
if (--m_InstanceCount == 0)
{
g_WLM = NULL;
delete this;
}
}
LRESULT CALLBACK CPlayerWLM::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static CPlayerWLM* player;
switch (msg)
{
case WM_CREATE:
{
// Get pointer to the CPlayerWLM class from the CreateWindow call
player = (CPlayerWLM*)((CREATESTRUCT*)lParam)->lpCreateParams;
return 0;
}
case WM_COPYDATA:
{
PCOPYDATASTRUCT cds = (PCOPYDATASTRUCT)lParam;
if (cds->dwData != 1351) return 0;
// lpData: \0Music\0<status>\0<format>\0<title>\0<artist>\0<album>\0
std::wstring data = (WCHAR*)cds->lpData;
// Some players include player name in the beginning. Skip that.
std::wstring::size_type len = data.find(L"\\0Music\\0");
len += 9;
data.erase(0, len); // Get rid of \0Music\0
bool playing = (data[0] == L'1');
if (playing)
{
player->m_State = PLAYER_PLAYING;
data.erase(0, 3); // Get rid of the status
len = data.find_first_of(L'\\');
len += 2;
data.erase(0, len); // Get rid of the format
len = data.find_first_of(L'\\');
player->m_Title = data.substr(0, len);
len += 2;
data.erase(0, len);
len = data.find_first_of(L'\\');
player->m_Artist = data.substr(0, len);
len += 2;
data.erase(0, len);
len = data.find_first_of(L'\\');
player->m_Album = data.substr(0, len);
}
else
{
player->ClearInfo();
}
return 0;
}
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
void CPlayerWLM::SendKeyInput(WORD key)
{
KEYBDINPUT kbi = {0};
kbi.wVk = key;
kbi.dwExtraInfo = (ULONG_PTR)GetMessageExtraInfo();
INPUT input = {0};
input.type = INPUT_KEYBOARD;
input.ki = kbi;
SendInput(1, &input, sizeof(INPUT));
}
/*
** UpdateData
**
** Called during each update of the main measure.
**
*/
void CPlayerWLM::UpdateData()
{
}
/*
** PlayPause
**
** Handles the PlayPause bang.
**
*/
void CPlayerWLM::PlayPause()
{
SendKeyInput(VK_MEDIA_PLAY_PAUSE);
}
/*
** Stop
**
** Handles the Stop bang.
**
*/
void CPlayerWLM::Stop()
{
SendKeyInput(VK_MEDIA_STOP);
}
/*
** Next
**
** Handles the Next bang.
**
*/
void CPlayerWLM::Next()
{
SendKeyInput(VK_MEDIA_NEXT_TRACK);
}
/*
** Previous
**
** Handles the Previous bang.
**
*/
void CPlayerWLM::Previous()
{
SendKeyInput(VK_MEDIA_PREV_TRACK);
}

View File

@ -0,0 +1,48 @@
/*
Copyright (C) 2011 Birunthan Mohanathas (www.poiru.net)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __PLAYERWLM_H__
#define __PLAYERWLM_H__
#include "Player.h"
class CPlayerWLM : public CPlayer
{
public:
CPlayerWLM();
~CPlayerWLM();
virtual void Pause() { return PlayPause(); }
virtual void Play() { return PlayPause(); }
virtual void PlayPause();
virtual void Stop();
virtual void Next();
virtual void Previous();
virtual void AddInstance(MEASURETYPE type);
virtual void RemoveInstance();
virtual void UpdateData();
private:
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void SendKeyInput(WORD key);
HWND m_Window; // Spotify window
};
#endif

View File

@ -52,7 +52,7 @@ HRESULT CPlayerWMP::CRemoteHost::GetServiceType(BSTR* pbstrType)
HRESULT hr = E_POINTER;
if (pbstrType)
{
*pbstrType = SysAllocString(L"Remote");
*pbstrType = SysAllocString(L"RemoteNoDialogs");
hr = *pbstrType? S_OK : E_POINTER;
}
return hr;
@ -222,9 +222,9 @@ void CPlayerWMP::Initialize()
wc.lpszClassName = L"NowPlayingWMPClass";
RegisterClass(&wc);
// Create dummy window
// Create the host window
m_Window = CreateWindow(L"NowPlayingWMPClass",
L"DummyWindow",
L"HostWindow",
WS_DISABLED,
CW_USEDEFAULT,
CW_USEDEFAULT,
@ -253,7 +253,7 @@ void CPlayerWMP::Initialize()
m_AxWindow->Create(m_Window, NULL, NULL, WS_CHILD | WS_DISABLED);
if(IsWindow(m_AxWindow->m_hWnd))
{
hr = m_AxWindow->QueryHost(IID_IObjectWithSite, (void **)&spHostObject);
hr = m_AxWindow->QueryHost(IID_IObjectWithSite, (void**)&spHostObject);
if(!spHostObject.p)
{
hr = E_POINTER;
@ -337,7 +337,6 @@ void CPlayerWMP::Initialize()
pRemoteHost->Release();
}
hr = m_IPlayer->get_controls(&m_IControls);
if (FAILED(hr)) return;
@ -356,7 +355,11 @@ void CPlayerWMP::Initialize()
m_State = PLAYER_PAUSED;
}
m_TrackChanged = true;
if (m_State != PLAYER_STOPPED)
{
m_TrackChanged = true;
}
m_Initialized = true;
}

View File

@ -21,10 +21,10 @@
#include "Winamp/wa_ipc.h"
#include "Winamp/wa_cmd.h"
// This player retrieves data through the Winamp IPC API.
extern CPlayer* g_Winamp;
// This player retrieves data through the Winamp IPC interface.
/*
** CPlayerWinamp
**
@ -63,7 +63,6 @@ void CPlayerWinamp::AddInstance(MEASURETYPE type)
if (type == MEASURE_COVER)
{
g_Winamp = NULL;
m_HasCoverMeasure = true;
}
}
@ -78,6 +77,7 @@ void CPlayerWinamp::RemoveInstance()
{
if (--m_InstanceCount == 0)
{
g_Winamp = NULL;
delete this;
}
}

View File

@ -12,7 +12,7 @@
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,0
FILEVERSION 1,1,0,0
PRODUCTVERSION PRODUCTVER
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
@ -29,7 +29,7 @@ BEGIN
BLOCK "040904E4"
BEGIN
VALUE "FileDescription", "NowPlaying Plugin for Rainmeter"
VALUE "FileVersion", "1.0.0.0"
VALUE "FileVersion", "1.1.0.0"
VALUE "InternalName", "NowPlaying"
VALUE "LegalCopyright", "Copyright (C) 2011 - Birunthan Mohanathas"
VALUE "OriginalFilename", "NowPlaying.dll"

View File

@ -263,6 +263,7 @@
<ClCompile Include="PlayerITunes.cpp" />
<ClCompile Include="PlayerSpotify.cpp" />
<ClCompile Include="PlayerWinamp.cpp" />
<ClCompile Include="PlayerWLM.cpp" />
<ClCompile Include="PlayerWMP.cpp" />
<ClCompile Include="SDKs\iTunes\iTunesCOMInterface_i.c" />
<ClCompile Include="StdAfx.cpp" />
@ -349,6 +350,7 @@
<ClInclude Include="PlayerITunes.h" />
<ClInclude Include="PlayerSpotify.h" />
<ClInclude Include="PlayerWinamp.h" />
<ClInclude Include="PlayerWLM.h" />
<ClInclude Include="PlayerWMP.h" />
<ClInclude Include="SDKs\AIMP\aimp2_sdk.h" />
<ClInclude Include="SDKs\CAD\cad_sdk.h" />

View File

@ -261,6 +261,9 @@
<ClCompile Include="StdAfx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PlayerWLM.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="SDKs\AIMP\aimp2_sdk.h">
@ -314,6 +317,9 @@
<ClInclude Include="StdAfx.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="PlayerWLM.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="PluginNowPlaying.rc">