mirror of
https://github.com/Team-Resurgent/XBMC4Xbox.git
synced 2026-08-15 11:18:13 -07:00
If ever adding the XBMC4Xbox source to GitHub use "git add -f -A" or it wont add all the files.
576 lines
14 KiB
C++
576 lines
14 KiB
C++
/*
|
|
* 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
|
|
* <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#ifdef _XBOX
|
|
#pragma comment(linker, "/merge:PY_TEXT=PYTHON")
|
|
#pragma comment(linker, "/merge:PY_DATA=PY_RW")
|
|
#pragma comment(linker, "/merge:PY_BSS=PY_RW")
|
|
#pragma comment(linker, "/merge:PY_RDATA=PYTHON")
|
|
#endif
|
|
|
|
// python.h should always be included first before any other includes
|
|
#include "system.h"
|
|
#include "python/Include/Python.h"
|
|
#include "cores/DllLoader/DllLoaderContainer.h"
|
|
#include "GUIPassword.h"
|
|
|
|
#include "XBPython.h"
|
|
#include "XBPythonDll.h"
|
|
#include "Application.h"
|
|
#include "settings/Settings.h"
|
|
#include "settings/Profile.h"
|
|
#include "FileSystem/File.h"
|
|
#include "FileSystem/SpecialProtocol.h"
|
|
#include "xbox/network.h"
|
|
#include "settings/Settings.h"
|
|
#include "utils/log.h"
|
|
|
|
XBPython g_pythonParser;
|
|
|
|
#define PYTHON_DLL "Q:\\system\\python\\python27.dll"
|
|
#define PYTHON_LIBDIR "Q:\\system\\python\\lib\\"
|
|
#define PYTHON_EXT "Q:\\system\\python\\lib\\*.pyd"
|
|
|
|
extern "C" HMODULE __stdcall dllLoadLibraryA(LPCSTR file);
|
|
extern "C" BOOL __stdcall dllFreeLibrary(HINSTANCE hLibModule);
|
|
|
|
|
|
XBPython::XBPython()
|
|
{
|
|
m_bInitialized = false;
|
|
bThreadInitialize = false;
|
|
bStartup = false;
|
|
bLogin = false;
|
|
nextid = 0;
|
|
mainThreadState = NULL;
|
|
m_hEvent = CreateEvent(NULL, false, false, (char*)"pythonEvent");
|
|
m_globalEvent = CreateEvent(NULL, false, false, (char*)"pythonGlobalEvent");
|
|
dThreadId = CThread::GetCurrentThreadId();
|
|
m_iDllScriptCounter = 0;
|
|
vecPlayerCallbackList.clear();
|
|
}
|
|
|
|
XBPython::~XBPython()
|
|
{
|
|
CloseHandle(m_globalEvent);
|
|
}
|
|
|
|
// message all registered callbacks that xbmc stopped playing
|
|
void XBPython::OnPlayBackEnded()
|
|
{
|
|
CSingleLock lock (m_critSection);
|
|
if (m_bInitialized)
|
|
{
|
|
PlayerCallbackList::iterator it = vecPlayerCallbackList.begin();
|
|
while (it != vecPlayerCallbackList.end())
|
|
{
|
|
((IPlayerCallback*)(*it))->OnPlayBackEnded();
|
|
it++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// message all registered callbacks that we started playing
|
|
void XBPython::OnPlayBackStarted()
|
|
{
|
|
CSingleLock lock (m_critSection);
|
|
if (m_bInitialized)
|
|
{
|
|
PlayerCallbackList::iterator it = vecPlayerCallbackList.begin();
|
|
while (it != vecPlayerCallbackList.end())
|
|
{
|
|
((IPlayerCallback*)(*it))->OnPlayBackStarted();
|
|
it++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// message all registered callbacks that we paused playing
|
|
void XBPython::OnPlayBackPaused()
|
|
{
|
|
if (m_bInitialized)
|
|
{
|
|
PlayerCallbackList::iterator it = vecPlayerCallbackList.begin();
|
|
while (it != vecPlayerCallbackList.end())
|
|
{
|
|
((IPlayerCallback*)(*it))->OnPlayBackPaused();
|
|
it++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// message all registered callbacks that we resumed playing
|
|
void XBPython::OnPlayBackResumed()
|
|
{
|
|
if (m_bInitialized)
|
|
{
|
|
PlayerCallbackList::iterator it = vecPlayerCallbackList.begin();
|
|
while (it != vecPlayerCallbackList.end())
|
|
{
|
|
((IPlayerCallback*)(*it))->OnPlayBackResumed();
|
|
it++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// message all registered callbacks that user stopped playing
|
|
void XBPython::OnPlayBackStopped()
|
|
{
|
|
CSingleLock lock (m_critSection);
|
|
if (m_bInitialized)
|
|
{
|
|
PlayerCallbackList::iterator it = vecPlayerCallbackList.begin();
|
|
while (it != vecPlayerCallbackList.end())
|
|
{
|
|
((IPlayerCallback*)(*it))->OnPlayBackStopped();
|
|
it++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void XBPython::RegisterPythonPlayerCallBack(IPlayerCallback* pCallback)
|
|
{
|
|
CSingleLock lock (m_critSection);
|
|
vecPlayerCallbackList.push_back(pCallback);
|
|
}
|
|
|
|
void XBPython::UnregisterPythonPlayerCallBack(IPlayerCallback* pCallback)
|
|
{
|
|
CSingleLock lock (m_critSection);
|
|
PlayerCallbackList::iterator it = vecPlayerCallbackList.begin();
|
|
while (it != vecPlayerCallbackList.end())
|
|
{
|
|
if (*it == pCallback)
|
|
it = vecPlayerCallbackList.erase(it);
|
|
else
|
|
it++;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check for file and print an error if needed
|
|
*/
|
|
bool XBPython::FileExist(const char* strFile)
|
|
{
|
|
if (!strFile)
|
|
return false;
|
|
|
|
if (!XFILE::CFile::Exists(strFile))
|
|
{
|
|
CLog::Log(LOGERROR, "Python: Cannot find '%s'", strFile);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Should be called before executing a script
|
|
*/
|
|
void XBPython::Initialize()
|
|
{
|
|
CLog::Log(LOGINFO, "initializing python engine. ");
|
|
CSingleLock lock(m_critSection);
|
|
m_iDllScriptCounter++;
|
|
if (!m_bInitialized)
|
|
{
|
|
if (dThreadId == GetCurrentThreadId())
|
|
{
|
|
m_hModule = dllLoadLibraryA(PYTHON_DLL);
|
|
LibraryLoader* pDll = DllLoaderContainer::GetModule(m_hModule);
|
|
if (!pDll || !python_load_dll(*pDll))
|
|
{
|
|
CLog::Log(LOGFATAL, "Python: error loading python27.dll");
|
|
Finalize();
|
|
return;
|
|
}
|
|
|
|
// first we check if all necessary files are installed
|
|
if (!FileExist("Q:\\system\\python\\python27.zlib") ||
|
|
!FileExist("Q:\\system\\python\\DLLs\\_socket.pyd") ||
|
|
!FileExist("Q:\\system\\python\\DLLs\\_sqlite3.pyd") ||
|
|
!FileExist("Q:\\system\\python\\DLLs\\_ssl.pyd") ||
|
|
!FileExist("Q:\\system\\python\\DLLs\\bz2.pyd") ||
|
|
!FileExist("Q:\\system\\python\\DLLs\\pyexpat.pyd") ||
|
|
!FileExist("Q:\\system\\python\\DLLs\\select.pyd") ||
|
|
!FileExist("Q:\\system\\python\\DLLs\\unicodedata.pyd"))
|
|
{
|
|
CLog::Log(LOGERROR, "Python: Missing files, unable to execute script");
|
|
Finalize();
|
|
return;
|
|
}
|
|
|
|
Py_Initialize();
|
|
PyEval_InitThreads();
|
|
|
|
if (g_advancedSettings.m_bPythonVerbose)
|
|
{
|
|
CLog::Log(LOGDEBUG, "%s - Running Python in verbose(--verbose) mode", __FUNCTION__);
|
|
char* python_argv[1] = { (char*)"--verbose" };
|
|
PySys_SetArgv(1, python_argv);
|
|
}
|
|
else
|
|
{
|
|
char* python_argv[1] = { (char*)"" };
|
|
PySys_SetArgv(1, python_argv);
|
|
}
|
|
|
|
initxbmc(); // init xbmc modules
|
|
initxbmcplugin(); // init plugin modules
|
|
initxbmcgui(); // init xbmcgui modules
|
|
// redirecting default output to debug console
|
|
if (PyRun_SimpleString(""
|
|
"import xbmc\n"
|
|
"class xbmcout:\n"
|
|
"\tdef write(self, data):\n"
|
|
"\t\txbmc.log(data)\n"
|
|
"\tdef close(self):\n"
|
|
"\t\txbmc.log('.')\n"
|
|
"\tdef flush(self):\n"
|
|
"\t\txbmc.log('.')\n"
|
|
"\n"
|
|
"import sys\n"
|
|
"sys.stdout = xbmcout()\n"
|
|
"sys.stderr = xbmcout()\n"
|
|
"print '-->Python Initialized<--'\n"
|
|
"") == -1)
|
|
{
|
|
CLog::Log(LOGFATAL, "Python Initialize Error");
|
|
}
|
|
|
|
if (!(mainThreadState = PyThreadState_Get()))
|
|
CLog::Log(LOGERROR, "Python threadstate is NULL.");
|
|
PyEval_ReleaseLock();
|
|
|
|
m_bInitialized = true;
|
|
bThreadInitialize = false;
|
|
PulseEvent(m_hEvent);
|
|
}
|
|
else
|
|
{
|
|
// only the main thread should initialize python.
|
|
m_iDllScriptCounter--;
|
|
bThreadInitialize = true;
|
|
|
|
lock.Leave();
|
|
WaitForSingleObject(m_hEvent, INFINITE);
|
|
lock.Enter();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Should be called when a script is finished
|
|
*/
|
|
void XBPython::Finalize()
|
|
{
|
|
CSingleLock lock(m_critSection);
|
|
m_iDllScriptCounter--;
|
|
if (m_iDllScriptCounter == 0 && m_bInitialized)
|
|
{
|
|
m_bInitialized = false;
|
|
CLog::Log(LOGINFO, "Python, unloading python27.dll because no scripts are running anymore");
|
|
PyEval_AcquireLock();
|
|
PyThreadState_Swap(mainThreadState);
|
|
Py_Finalize();
|
|
PyEval_ReleaseLock();
|
|
// first free all dlls loaded by python, after that python27.dll (this is done by UnloadPythonDlls
|
|
DllLoaderContainer::UnloadPythonDlls();
|
|
m_hModule = NULL;
|
|
mainThreadState = NULL;
|
|
}
|
|
}
|
|
|
|
void XBPython::FreeResources()
|
|
{
|
|
CSingleLock lock(m_critSection);
|
|
if (m_bInitialized)
|
|
{
|
|
// cleanup threads that are still running
|
|
PyList::iterator it = vecPyList.begin();
|
|
while (it != vecPyList.end())
|
|
{
|
|
lock.Leave(); //unlock here because the python thread might lock when it exits
|
|
delete it->pyThread;
|
|
lock.Enter();
|
|
it = vecPyList.erase(it);
|
|
Finalize();
|
|
}
|
|
}
|
|
|
|
if (m_hEvent)
|
|
CloseHandle(m_hEvent);
|
|
}
|
|
|
|
void XBPython::Process()
|
|
{
|
|
CStdString strAutoExecPy;
|
|
|
|
// initialize if init was called from another thread
|
|
if (bThreadInitialize) Initialize();
|
|
|
|
if (bStartup)
|
|
{
|
|
bStartup = false;
|
|
|
|
// autoexec.py - system
|
|
strAutoExecPy = "special://xbmc/scripts/autoexec.py";
|
|
|
|
if (XFILE::CFile::Exists(strAutoExecPy))
|
|
{
|
|
// We need to make sure the network is up in case the start scripts require network
|
|
g_application.getNetwork().WaitForSetup(5000);
|
|
|
|
evalFile(strAutoExecPy);
|
|
}
|
|
else
|
|
CLog::Log(LOGDEBUG, "%s - no system autoexec.py (%s) found, skipping", __FUNCTION__, CSpecialProtocol::TranslatePath(strAutoExecPy).c_str());
|
|
}
|
|
|
|
if (bLogin)
|
|
{
|
|
bLogin = false;
|
|
|
|
// autoexec.py - profile
|
|
strAutoExecPy = "special://profile/scripts/autoexec.py";
|
|
|
|
if (XFILE::CFile::Exists(strAutoExecPy))
|
|
evalFile(strAutoExecPy);
|
|
else
|
|
CLog::Log(LOGDEBUG, "%s - no profile autoexec.py (%s) found, skipping", __FUNCTION__, CSpecialProtocol::TranslatePath(strAutoExecPy).c_str());
|
|
}
|
|
|
|
CSingleLock lock(m_critSection);
|
|
|
|
if (m_bInitialized)
|
|
{
|
|
PyList::iterator it = vecPyList.begin();
|
|
while (it != vecPyList.end())
|
|
{
|
|
//delete scripts which are done
|
|
if (it->bDone)
|
|
{
|
|
delete it->pyThread;
|
|
it = vecPyList.erase(it);
|
|
Finalize();
|
|
}
|
|
else ++it;
|
|
}
|
|
}
|
|
}
|
|
|
|
int XBPython::evalFile(const char *src) { return evalFile(src, 0, NULL); }
|
|
// execute script, returns -1 if script doesn't exist
|
|
int XBPython::evalFile(const char *src, const unsigned int argc, const char ** argv)
|
|
{
|
|
CSingleExit ex(g_graphicsContext);
|
|
CSingleLock lock(m_critSection);
|
|
// return if file doesn't exist
|
|
if (!XFILE::CFile::Exists(src))
|
|
{
|
|
CLog::Log(LOGERROR, "Python script \"%s\" does not exist", CSpecialProtocol::TranslatePath(src).c_str());
|
|
return -1;
|
|
}
|
|
|
|
// check if locked
|
|
if (g_settings.GetCurrentProfile().programsLocked() && !g_passwordManager.IsMasterLockUnlocked(true))
|
|
return -1;
|
|
|
|
Initialize();
|
|
|
|
if (!m_bInitialized) return -1;
|
|
|
|
nextid++;
|
|
XBPyThread *pyThread = new XBPyThread(this, mainThreadState, nextid);
|
|
if (argv != NULL)
|
|
pyThread->setArgv(argc, argv);
|
|
pyThread->evalFile(src);
|
|
PyElem inf;
|
|
inf.id = nextid;
|
|
inf.bDone = false;
|
|
inf.strFile = src;
|
|
inf.pyThread = pyThread;
|
|
|
|
vecPyList.push_back(inf);
|
|
|
|
return nextid;
|
|
}
|
|
|
|
void XBPython::setDone(int id)
|
|
{
|
|
CSingleLock lock(m_critSection);
|
|
PyList::iterator it = vecPyList.begin();
|
|
while (it != vecPyList.end())
|
|
{
|
|
if (it->id == id)
|
|
{
|
|
if (it->pyThread->isStopping())
|
|
CLog::Log(LOGINFO, "Python script interrupted by user");
|
|
else
|
|
CLog::Log(LOGINFO, "Python script stopped");
|
|
it->bDone = true;
|
|
}
|
|
++it;
|
|
}
|
|
}
|
|
|
|
void XBPython::stopScript(int id)
|
|
{
|
|
CSingleExit ex(g_graphicsContext);
|
|
CSingleLock lock(m_critSection);
|
|
PyList::iterator it = vecPyList.begin();
|
|
while (it != vecPyList.end())
|
|
{
|
|
if (it->id == id) {
|
|
CLog::Log(LOGINFO, "Stopping script with id: %i", id);
|
|
it->pyThread->stop();
|
|
return;
|
|
}
|
|
++it;
|
|
}
|
|
}
|
|
|
|
PyThreadState *XBPython::getMainThreadState()
|
|
{
|
|
CSingleLock lock(m_critSection);
|
|
return mainThreadState;
|
|
}
|
|
|
|
int XBPython::ScriptsSize()
|
|
{
|
|
CSingleLock lock(m_critSection);
|
|
return vecPyList.size();
|
|
}
|
|
|
|
const char* XBPython::getFileName(int scriptId)
|
|
{
|
|
const char* cFileName = NULL;
|
|
|
|
CSingleLock lock(m_critSection);
|
|
PyList::iterator it = vecPyList.begin();
|
|
while (it != vecPyList.end())
|
|
{
|
|
if (it->id == scriptId)
|
|
cFileName = it->strFile.c_str();
|
|
++it;
|
|
}
|
|
|
|
return cFileName;
|
|
}
|
|
|
|
int XBPython::getScriptId(const char* strFile)
|
|
{
|
|
int iId = -1;
|
|
|
|
CSingleLock lock(m_critSection);
|
|
|
|
PyList::iterator it = vecPyList.begin();
|
|
while (it != vecPyList.end())
|
|
{
|
|
if (!stricmp(it->strFile.c_str(), strFile))
|
|
iId = it->id;
|
|
++it;
|
|
}
|
|
|
|
return iId;
|
|
}
|
|
|
|
bool XBPython::isRunning(int scriptId)
|
|
{
|
|
CSingleLock lock(m_critSection);
|
|
|
|
for(PyList::iterator it = vecPyList.begin(); it != vecPyList.end(); it++)
|
|
{
|
|
if (it->id == scriptId)
|
|
{
|
|
if(it->bDone)
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool XBPython::isStopping(int scriptId)
|
|
{
|
|
bool bStopping = false;
|
|
|
|
CSingleLock lock(m_critSection);
|
|
PyList::iterator it = vecPyList.begin();
|
|
while (it != vecPyList.end())
|
|
{
|
|
if (it->id == scriptId)
|
|
bStopping = it->pyThread->isStopping();
|
|
++it;
|
|
}
|
|
|
|
return bStopping;
|
|
}
|
|
|
|
int XBPython::GetPythonScriptId(int scriptPosition)
|
|
{
|
|
CSingleLock lock(m_critSection);
|
|
return (int)vecPyList[scriptPosition].id;
|
|
}
|
|
|
|
void XBPython::PulseGlobalEvent()
|
|
{
|
|
SetEvent(m_globalEvent);
|
|
}
|
|
|
|
void XBPython::WaitForEvent(HANDLE hEvent, DWORD timeout)
|
|
{
|
|
// wait for either this event our our global event
|
|
HANDLE handles[2] = { hEvent, m_globalEvent };
|
|
WaitForMultipleObjects(2, handles, FALSE, timeout);
|
|
ResetEvent(m_globalEvent);
|
|
}
|
|
|
|
// execute script, returns -1 if script doesn't exist
|
|
int XBPython::evalString(const char *src, const unsigned int argc, const char ** argv)
|
|
{
|
|
CLog::Log(LOGDEBUG, "XBPython::evalString (python)");
|
|
CSingleLock lock(m_critSection);
|
|
|
|
Initialize();
|
|
|
|
if (!m_bInitialized)
|
|
{
|
|
CLog::Log(LOGERROR, "XBPython::evalString, python not initialized (python)");
|
|
return -1;
|
|
}
|
|
|
|
// Previous implementation would create a new thread for every script
|
|
nextid++;
|
|
XBPyThread *pyThread = new XBPyThread(this, mainThreadState, nextid);
|
|
if (argv != NULL)
|
|
pyThread->setArgv(argc, argv);
|
|
pyThread->evalString(src);
|
|
|
|
PyElem inf;
|
|
inf.id = nextid;
|
|
inf.bDone = false;
|
|
inf.strFile = "<string>";
|
|
inf.pyThread = pyThread;
|
|
|
|
vecPyList.push_back(inf);
|
|
|
|
return nextid;
|
|
} |