/*
* Copyright (C) 2005-2013 Team XBMC
* http://xbmc.org
*
* 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, 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 XBMC; see the file COPYING. If not, see
* .
*
*/
#include "system.h"
#include "interfaces/Builtins.h"
#include "ApplicationMessenger.h"
#include "Application.h"
#ifdef _XBOX
#include "xbox/XKUtils.h"
#include "xbox/XKHDD.h"
#endif
#include "TextureManager.h"
#include "PlayListPlayer.h"
#include "Util.h"
#include "utils/URIUtils.h"
#include "lib/libPython/XBPython.h"
#include "pictures/GUIWindowSlideShow.h"
#include "lib/libGoAhead/XBMChttp.h"
#include "xbox/network.h"
#include "GUIWindowManager.h"
#include "settings/Settings.h"
#include "FileItem.h"
#include "GUIDialog.h"
#include "SectionLoader.h"
#include "lib/libPython/xbmcmodule/GUIPythonWindowDialog.h"
#include "lib/libPython/xbmcmodule/GUIPythonWindowXMLDialog.h"
using namespace std;
extern HWND g_hWnd;
CApplicationMessenger::~CApplicationMessenger()
{
Cleanup();
}
void CApplicationMessenger::Cleanup()
{
CSingleLock lock (m_critSection);
while (m_vecMessages.size() > 0)
{
ThreadMessage* pMsg = m_vecMessages.front();
if (pMsg->hWaitEvent)
SetEvent(pMsg->hWaitEvent);
delete pMsg;
m_vecMessages.pop();
}
while (m_vecWindowMessages.size() > 0)
{
ThreadMessage* pMsg = m_vecWindowMessages.front();
if (pMsg->hWaitEvent)
SetEvent(pMsg->hWaitEvent);
delete pMsg;
m_vecWindowMessages.pop();
}
}
void CApplicationMessenger::SendMessage(ThreadMessage& message, bool wait)
{
message.hWaitEvent = NULL;
if (wait)
{ // check that we're not being called from our application thread, else we'll be waiting
// forever!
if (GetCurrentThreadId() != g_application.GetThreadId())
message.hWaitEvent = CreateEvent(NULL, true, false, NULL);
else
{
//OutputDebugString("Attempting to wait on a SendMessage() from our application thread will cause lockup!\n");
//OutputDebugString("Sending immediately\n");
ProcessMessage(&message);
return;
}
}
CSingleLock lock (m_critSection);
if (g_application.m_bStop)
{
if (message.hWaitEvent)
{
CloseHandle(message.hWaitEvent);
message.hWaitEvent = NULL;
}
return;
}
ThreadMessage* msg = new ThreadMessage();
msg->dwMessage = message.dwMessage;
msg->dwParam1 = message.dwParam1;
msg->dwParam2 = message.dwParam2;
msg->hWaitEvent = message.hWaitEvent;
msg->lpVoid = message.lpVoid;
msg->strParam = message.strParam;
msg->params = message.params;
if (msg->dwMessage == TMSG_DIALOG_DOMODAL ||
msg->dwMessage == TMSG_WRITE_SCRIPT_OUTPUT)
m_vecWindowMessages.push(msg);
else
m_vecMessages.push(msg);
lock.Leave();
if (message.hWaitEvent)
{
WaitForSingleObject(message.hWaitEvent, INFINITE);
CloseHandle(message.hWaitEvent);
message.hWaitEvent = NULL;
}
}
void CApplicationMessenger::ProcessMessages()
{
// process threadmessages
CSingleLock lock (m_critSection);
while (m_vecMessages.size() > 0)
{
ThreadMessage* pMsg = m_vecMessages.front();
//first remove the message from the queue, else the message could be processed more then once
m_vecMessages.pop();
//Leave here as the message might make another
//thread call processmessages or sendmessage
lock.Leave();
ProcessMessage(pMsg);
if (pMsg->hWaitEvent)
SetEvent(pMsg->hWaitEvent);
delete pMsg;
//Reenter here again, to not ruin message vector
lock.Enter();
}
}
void CApplicationMessenger::ProcessMessage(ThreadMessage *pMsg)
{
switch (pMsg->dwMessage)
{
case TMSG_SHUTDOWN:
#ifdef _XBOX
case TMSG_POWERDOWN:
#endif
{
g_application.Stop();
Sleep(200);
#ifdef _XBOX
#ifndef _DEBUG // don't actually shut off if debug build, it hangs VS for a long time
XKHDD::SpindownHarddisk(); // Spindown the Harddisk
XKUtils::XBOXPowerOff();
while(1){Sleep(0);}
#endif
#else
// send the WM_CLOSE window message
::SendMessage( g_hWnd, WM_CLOSE, 0, 0 );
#endif
}
break;
#ifndef _XBOX
case TMSG_POWERDOWN:
{
#if !defined(_LINUX)
#ifdef _WIN32PC
if (CWIN32Util::PowerManagement(POWERSTATE_SHUTDOWN))
#endif
#endif
#ifdef HAS_HAL
if (CHalManager::PowerManagement(POWERSTATE_SHUTDOWN))
#endif
{
g_application.Stop();
exit(0);
}
}
break;
#endif
#ifdef _XBOX
case TMSG_QUIT:
{
CBuiltins::Execute("XBMC.Dashboard()");
}
break;
#else
case TMSG_QUIT:
{
g_application.Stop();
Sleep(200);
exit(0);
}
break;
#endif
case TMSG_HIBERNATE:
{
#ifdef HAS_HAL
CHalManager::PowerManagement(POWERSTATE_HIBERNATE);
#elif defined(_WIN32PC)
CWIN32Util::PowerManagement(POWERSTATE_HIBERNATE);
#endif
}
break;
case TMSG_SUSPEND:
{
#ifdef HAS_HAL
CHalManager::PowerManagement(POWERSTATE_SUSPEND);
#elif defined(_WIN32PC)
CWIN32Util::PowerManagement(POWERSTATE_SUSPEND);
#endif
}
break;
case TMSG_RESTART:
{
g_application.Stop();
Sleep(200);
#ifdef _XBOX
#ifndef _DEBUG // don't actually shut off if debug build, it hangs VS for a long time
XKUtils::XBOXPowerCycle();
while(1){Sleep(0);}
#endif
#else
// send the WM_CLOSE window message
::SendMessage( g_hWnd, WM_CLOSE, 0, 0 );
#endif
}
break;
case TMSG_RESET:
{
g_application.Stop();
Sleep(200);
#ifdef _XBOX
#ifndef _DEBUG // don't actually shut off if debug build, it hangs VS for a long time
XKUtils::XBOXReset();
while(1){Sleep(0);}
#endif
#else
// send the WM_CLOSE window message
::SendMessage( g_hWnd, WM_CLOSE, 0, 0 );
#endif
}
break;
case TMSG_RESTARTAPP:
{
char szXBEFileName[1024];
CIoSupport::GetXbePath(szXBEFileName);
CUtil::RunXBE(szXBEFileName);
}
break;
case TMSG_MEDIA_PLAY:
{
// first check if we were called from the PlayFile() function
if (pMsg->lpVoid && pMsg->dwParam2 == 0)
{
CFileItem *item = (CFileItem *)pMsg->lpVoid;
g_application.PlayFile(*item, pMsg->dwParam1 != 0);
delete item;
return;
}
// restore to previous window if needed
if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW ||
g_windowManager.GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO ||
g_windowManager.GetActiveWindow() == WINDOW_VISUALISATION)
g_windowManager.PreviousWindow();
g_application.ResetScreenSaver();
g_application.ResetScreenSaverWindow();
//g_application.StopPlaying();
// play file
CFileItem item;
if(pMsg->lpVoid)
{
item = *(CFileItem *)pMsg->lpVoid;
delete (CFileItem *)pMsg->lpVoid;
}
else
{
item.SetPath(pMsg->strParam);
item.m_bIsFolder = false;
if (item.IsAudio())
item.SetMusicThumb();
else
item.SetVideoThumb();
item.FillInDefaultIcon();
}
g_application.PlayMedia(item, item.IsAudio() ? PLAYLIST_MUSIC : PLAYLIST_VIDEO); //Note: this will play playlists always in the temp music playlist (default 2nd parameter), maybe needs some tweaking.
}
break;
case TMSG_MEDIA_RESTART:
g_application.Restart(true);
break;
case TMSG_PICTURE_SHOW:
{
CGUIWindowSlideShow *pSlideShow = (CGUIWindowSlideShow *)g_windowManager.GetWindow(WINDOW_SLIDESHOW);
if (!pSlideShow) return ;
// stop playing file
if (g_application.IsPlayingVideo()) g_application.StopPlaying();
if (g_windowManager.GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO)
g_windowManager.PreviousWindow();
g_application.ResetScreenSaver();
g_application.ResetScreenSaverWindow();
g_graphicsContext.Lock();
pSlideShow->Reset();
if (g_windowManager.GetActiveWindow() != WINDOW_SLIDESHOW)
g_windowManager.ActivateWindow(WINDOW_SLIDESHOW);
if (URIUtils::IsZIP(pMsg->strParam) || URIUtils::IsRAR(pMsg->strParam)) // actually a cbz/cbr
{
CFileItemList items;
CStdString strPath;
if (URIUtils::IsZIP(pMsg->strParam))
URIUtils::CreateArchivePath(strPath, "zip", pMsg->strParam.c_str(), "");
else
URIUtils::CreateArchivePath(strPath, "rar", pMsg->strParam.c_str(), "");
CUtil::GetRecursiveListing(strPath, items, g_settings.m_pictureExtensions);
if (items.Size() > 0)
{
for (int i=0;iAdd(items[i].get());
}
pSlideShow->Select(items[0]->GetPath());
}
}
else
{
CFileItem item(pMsg->strParam, false);
pSlideShow->Add(&item);
pSlideShow->Select(pMsg->strParam);
}
g_graphicsContext.Unlock();
}
break;
case TMSG_SLIDESHOW_SCREENSAVER:
case TMSG_PICTURE_SLIDESHOW:
{
CGUIWindowSlideShow *pSlideShow = (CGUIWindowSlideShow *)g_windowManager.GetWindow(WINDOW_SLIDESHOW);
if (!pSlideShow) return ;
g_graphicsContext.Lock();
pSlideShow->Reset();
CFileItemList items;
CStdString strPath = pMsg->strParam;
if (pMsg->dwMessage == TMSG_SLIDESHOW_SCREENSAVER &&
g_guiSettings.GetString("screensaver.mode").Equals("Fanart Slideshow"))
{
CUtil::GetRecursiveListing(g_settings.GetVideoFanartFolder(), items, ".tbn");
CUtil::GetRecursiveListing(g_settings.GetMusicFanartFolder(), items, ".tbn");
}
else
CUtil::GetRecursiveListing(strPath, items, g_settings.m_pictureExtensions);
if (items.Size() > 0)
{
for (int i=0;iAdd(items[i].get());
pSlideShow->StartSlideShow(pMsg->dwMessage == TMSG_SLIDESHOW_SCREENSAVER); //Start the slideshow!
}
if (pMsg->dwMessage == TMSG_SLIDESHOW_SCREENSAVER)
pSlideShow->Shuffle();
if (g_windowManager.GetActiveWindow() != WINDOW_SLIDESHOW)
{
if(items.Size() == 0)
{
g_guiSettings.SetString("screensaver.mode", "Dim");
g_application.ActivateScreenSaver();
}
else
g_windowManager.ActivateWindow(WINDOW_SLIDESHOW);
}
g_graphicsContext.Unlock();
}
break;
case TMSG_MEDIA_STOP:
{
// restore to previous window if needed
if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW ||
g_windowManager.GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO ||
g_windowManager.GetActiveWindow() == WINDOW_VISUALISATION)
g_windowManager.PreviousWindow();
g_application.ResetScreenSaver();
g_application.ResetScreenSaverWindow();
// stop playing file
if (g_application.IsPlaying()) g_application.StopPlaying();
}
break;
case TMSG_MEDIA_PAUSE:
if (g_application.m_pPlayer)
{
g_application.ResetScreenSaver();
g_application.ResetScreenSaverWindow();
g_application.m_pPlayer->Pause();
}
break;
case TMSG_SWITCHTOFULLSCREEN:
if( g_windowManager.GetActiveWindow() != WINDOW_FULLSCREEN_VIDEO )
g_application.SwitchToFullScreen();
break;
case TMSG_HTTPAPI:
{
if (!m_pXbmcHttp)
{
CSectionLoader::Load("LIBHTTP");
m_pXbmcHttp = new CXbmcHttp();
}
switch (m_pXbmcHttp->xbmcCommand(pMsg->strParam))
{
case 1:
g_application.getApplicationMessenger().Restart();
break;
case 2:
g_application.getApplicationMessenger().Shutdown();
break;
case 3:
g_application.getApplicationMessenger().RebootToDashBoard();
break;
case 4:
g_application.getApplicationMessenger().Reset();
break;
case 5:
g_application.getApplicationMessenger().RestartApp();
break;
}
}
break;
case TMSG_EXECUTE_SCRIPT:
{
unsigned int argc = 1;
char ** argv = new char*[argc];
argv[0] = (char*)pMsg->strParam.c_str();
g_pythonParser.evalFile(argv[0], argc, (const char**)argv);
delete [] argv;
}
break;
case TMSG_EXECUTE_BUILT_IN:
CBuiltins::Execute(pMsg->strParam.c_str());
break;
case TMSG_PLAYLISTPLAYER_PLAY:
if (pMsg->dwParam1 != (DWORD) -1)
g_playlistPlayer.Play(pMsg->dwParam1);
else
g_playlistPlayer.Play();
break;
case TMSG_PLAYLISTPLAYER_NEXT:
g_playlistPlayer.PlayNext();
break;
case TMSG_PLAYLISTPLAYER_PREV:
g_playlistPlayer.PlayPrevious();
break;
// Window messages below here...
case TMSG_DIALOG_DOMODAL: //doModel of window
{
CGUIDialog* pDialog = (CGUIDialog*)g_windowManager.GetWindow(pMsg->dwParam1);
if (!pDialog) return ;
pDialog->DoModal();
}
break;
case TMSG_WRITE_SCRIPT_OUTPUT:
{
//send message to window 2004 (CGUIWindowScriptsInfo)
CGUIMessage msg(GUI_MSG_USER, 0, 0);
msg.SetLabel(pMsg->strParam);
CGUIWindow* pWindowScripts = g_windowManager.GetWindow(WINDOW_SCRIPTS_INFO);
if (pWindowScripts) pWindowScripts->OnMessage(msg);
}
break;
case TMSG_NETWORKMESSAGE:
{
g_application.getNetwork().NetworkMessage((CNetwork::EMESSAGE)pMsg->dwParam1, pMsg->dwParam2);
}
break;
case TMSG_GUI_PYTHON_DIALOG:
{
if (pMsg->lpVoid)
{ // TODO: This is ugly - really these python dialogs should just be normal XBMC dialogs
if (pMsg->dwParam1)
((CGUIPythonWindowXMLDialog *)pMsg->lpVoid)->Show_Internal(pMsg->dwParam2 > 0);
else
((CGUIPythonWindowDialog *)pMsg->lpVoid)->Show_Internal(pMsg->dwParam2 > 0);
}
}
break;
}
}
void CApplicationMessenger::ProcessWindowMessages()
{
CSingleLock lock (m_critSection);
//message type is window, process window messages
while (m_vecWindowMessages.size() > 0)
{
ThreadMessage* pMsg = m_vecWindowMessages.front();
//first remove the message from the queue, else the message could be processed more then once
m_vecWindowMessages.pop();
// leave here in case we make more thread messages from this one
lock.Leave();
ProcessMessage(pMsg);
if (pMsg->hWaitEvent)
SetEvent(pMsg->hWaitEvent);
delete pMsg;
lock.Enter();
}
}
int CApplicationMessenger::SetResponse(CStdString response)
{
CSingleLock lock (m_critBuffer);
bufferResponse=response;
lock.Leave();
return 0;
}
CStdString CApplicationMessenger::GetResponse()
{
CStdString tmp;
CSingleLock lock (m_critBuffer);
tmp=bufferResponse;
lock.Leave();
return tmp;
}
void CApplicationMessenger::HttpApi(string cmd, bool wait)
{
SetResponse("");
ThreadMessage tMsg = {TMSG_HTTPAPI};
tMsg.strParam = cmd;
SendMessage(tMsg, wait);
}
void CApplicationMessenger::ExecBuiltIn(const CStdString &command)
{
ThreadMessage tMsg = {TMSG_EXECUTE_BUILT_IN};
tMsg.strParam = command;
SendMessage(tMsg);
}
void CApplicationMessenger::MediaPlay(string filename)
{
ThreadMessage tMsg = {TMSG_MEDIA_PLAY};
tMsg.strParam = filename;
SendMessage(tMsg, true);
}
void CApplicationMessenger::MediaPlay(const CFileItem &item)
{
ThreadMessage tMsg = {TMSG_MEDIA_PLAY};
CFileItem *pItem = new CFileItem(item);
tMsg.lpVoid = (void *)pItem;
tMsg.dwParam1 = 0;
tMsg.dwParam2 = 1;
SendMessage(tMsg, true);
}
void CApplicationMessenger::PlayFile(const CFileItem &item, bool bRestart /*= false*/)
{
ThreadMessage tMsg = {TMSG_MEDIA_PLAY};
CFileItem *pItem = new CFileItem(item);
tMsg.lpVoid = (void *)pItem;
tMsg.dwParam1 = bRestart ? 1 : 0;
tMsg.dwParam2 = 0;
SendMessage(tMsg, false);
}
void CApplicationMessenger::MediaStop()
{
ThreadMessage tMsg = {TMSG_MEDIA_STOP};
SendMessage(tMsg, true);
}
void CApplicationMessenger::MediaPause()
{
ThreadMessage tMsg = {TMSG_MEDIA_PAUSE};
SendMessage(tMsg, true);
}
void CApplicationMessenger::MediaRestart(bool bWait)
{
ThreadMessage tMsg = {TMSG_MEDIA_RESTART};
SendMessage(tMsg, bWait);
}
void CApplicationMessenger::PlayListPlayerPlay()
{
ThreadMessage tMsg = {TMSG_PLAYLISTPLAYER_PLAY, (DWORD) -1};
SendMessage(tMsg, true);
}
void CApplicationMessenger::PlayListPlayerPlay(int iSong)
{
ThreadMessage tMsg = {TMSG_PLAYLISTPLAYER_PLAY, iSong};
SendMessage(tMsg, true);
}
void CApplicationMessenger::PlayListPlayerNext()
{
ThreadMessage tMsg = {TMSG_PLAYLISTPLAYER_NEXT};
SendMessage(tMsg, true);
}
void CApplicationMessenger::PlayListPlayerPrevious()
{
ThreadMessage tMsg = {TMSG_PLAYLISTPLAYER_PREV};
SendMessage(tMsg, true);
}
void CApplicationMessenger::PictureShow(string filename)
{
ThreadMessage tMsg = {TMSG_PICTURE_SHOW};
tMsg.strParam = filename;
SendMessage(tMsg);
}
void CApplicationMessenger::PictureSlideShow(string pathname, bool bScreensaver /* = false */)
{
DWORD dwMessage = TMSG_PICTURE_SLIDESHOW;
if (bScreensaver)
dwMessage = TMSG_SLIDESHOW_SCREENSAVER;
ThreadMessage tMsg = {dwMessage};
tMsg.strParam = pathname;
SendMessage(tMsg);
}
void CApplicationMessenger::Shutdown()
{
ThreadMessage tMsg = {TMSG_SHUTDOWN};
SendMessage(tMsg);
}
void CApplicationMessenger::Powerdown()
{
ThreadMessage tMsg = {TMSG_POWERDOWN};
SendMessage(tMsg);
}
void CApplicationMessenger::Quit()
{
ThreadMessage tMsg = {TMSG_QUIT};
SendMessage(tMsg);
}
void CApplicationMessenger::Hibernate()
{
ThreadMessage tMsg = {TMSG_HIBERNATE};
SendMessage(tMsg);
}
void CApplicationMessenger::Suspend()
{
ThreadMessage tMsg = {TMSG_SUSPEND};
SendMessage(tMsg);
}
void CApplicationMessenger::Restart()
{
ThreadMessage tMsg = {TMSG_RESTART};
SendMessage(tMsg);
}
void CApplicationMessenger::Reset()
{
ThreadMessage tMsg = {TMSG_RESET};
SendMessage(tMsg);
}
void CApplicationMessenger::RestartApp()
{
ThreadMessage tMsg = {TMSG_RESTARTAPP};
SendMessage(tMsg);
}
void CApplicationMessenger::RebootToDashBoard()
{
ThreadMessage tMsg = {TMSG_DASHBOARD};
SendMessage(tMsg);
}
void CApplicationMessenger::NetworkMessage(DWORD dwMessage, DWORD dwParam)
{
ThreadMessage tMsg = {TMSG_NETWORKMESSAGE, dwMessage, dwParam};
SendMessage(tMsg);
}
void CApplicationMessenger::SwitchToFullscreen()
{
/* FIXME: ideally this call should return upon a successfull switch but currently
is causing deadlocks between the dvdplayer destructor and the rendermanager
*/
ThreadMessage tMsg = {TMSG_SWITCHTOFULLSCREEN};
SendMessage(tMsg, false);
}