[bluray] Fix stream info/language retrieval for blurays in non-nav mode.
[vuplus_xbmc] / xbmc / interfaces / python / XBPython.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #if (defined HAVE_CONFIG_H) && (!defined TARGET_WINDOWS)
23   #include "config.h"
24 #endif
25
26 // python.h should always be included first before any other includes
27 #include <Python.h>
28
29 #include <algorithm>
30
31 #include "system.h"
32 #include "cores/DllLoader/DllLoaderContainer.h"
33 #include "GUIPassword.h"
34 #include "XBPython.h"
35 #include "filesystem/File.h"
36 #include "filesystem/SpecialProtocol.h"
37 #include "guilib/GraphicContext.h"
38 #include "profiles/ProfilesManager.h"
39 #include "utils/log.h"
40 #include "pythreadstate.h"
41 #include "utils/TimeUtils.h"
42 #include "Util.h"
43 #include "guilib/GraphicContext.h"
44 #ifdef TARGET_WINDOWS
45 #include "utils/Environment.h"
46 #endif
47
48 #include "threads/SystemClock.h"
49 #include "addons/Addon.h"
50 #include "interfaces/AnnouncementManager.h"
51
52 #include "interfaces/legacy/Monitor.h"
53 #include "interfaces/legacy/AddonUtils.h"
54
55 using namespace ANNOUNCEMENT;
56
57 namespace PythonBindings {
58   void initModule_xbmcgui(void);
59   void initModule_xbmc(void);
60   void initModule_xbmcplugin(void);
61   void initModule_xbmcaddon(void);
62   void initModule_xbmcvfs(void);
63 }
64
65 using namespace PythonBindings;
66
67 XBPython::XBPython()
68 {
69   m_bInitialized      = false;
70   m_bLogin            = false;
71   m_nextid            = 0;
72   m_mainThreadState   = NULL;
73   m_ThreadId          = CThread::GetCurrentThreadId();
74   m_iDllScriptCounter = 0;
75   m_endtime           = 0;
76   m_pDll              = NULL;
77   m_vecPlayerCallbackList.clear();
78   m_vecMonitorCallbackList.clear();
79
80   CAnnouncementManager::AddAnnouncer(this);
81 }
82
83 XBPython::~XBPython()
84 {
85   TRACE;
86   CAnnouncementManager::RemoveAnnouncer(this);
87 }
88
89 #define LOCK_AND_COPY(type, dest, src) \
90   if (!m_bInitialized) return; \
91   CSingleLock lock(src); \
92   src.hadSomethingRemoved = false; \
93   type dest; \
94   dest = src
95
96 #define CHECK_FOR_ENTRY(l,v) \
97   (l.hadSomethingRemoved ? (std::find(l.begin(),l.end(),v) != l.end()) : true)
98
99 // message all registered callbacks that xbmc stopped playing
100 void XBPython::OnPlayBackEnded()
101 {
102   TRACE;
103   LOCK_AND_COPY(std::vector<PVOID>,tmp,m_vecPlayerCallbackList);
104   for (PlayerCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
105     if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList,(*it)))
106       ((IPlayerCallback*)(*it))->OnPlayBackEnded();
107 }
108
109 void XBPython::Announce(AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data)
110 {
111   if (flag & VideoLibrary)
112   {
113    if (strcmp(message, "OnScanFinished") == 0)
114      OnDatabaseUpdated("video");
115    else if (strcmp(message, "OnScanStarted") == 0)
116      OnDatabaseScanStarted("video");
117   }
118   else if (flag & AudioLibrary)
119   {
120    if (strcmp(message, "OnScanFinished") == 0)
121      OnDatabaseUpdated("music");
122    else if (strcmp(message, "OnScanStarted") == 0)
123      OnDatabaseScanStarted("music");
124   }
125   else if (flag & GUI)
126   {
127    if (strcmp(message, "OnScreensaverDeactivated") == 0)
128      OnScreensaverDeactivated();   
129    else if (strcmp(message, "OnScreensaverActivated") == 0)
130      OnScreensaverActivated();
131   }
132 }
133
134 // message all registered callbacks that we started playing
135 void XBPython::OnPlayBackStarted()
136 {
137   TRACE;
138   LOCK_AND_COPY(std::vector<PVOID>,tmp,m_vecPlayerCallbackList);
139   for (PlayerCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
140     if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList,(*it)))
141       ((IPlayerCallback*)(*it))->OnPlayBackStarted();
142 }
143
144 // message all registered callbacks that we paused playing
145 void XBPython::OnPlayBackPaused()
146 {
147   TRACE;
148   LOCK_AND_COPY(std::vector<PVOID>,tmp,m_vecPlayerCallbackList);
149   for (PlayerCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
150     if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList,(*it)))
151       ((IPlayerCallback*)(*it))->OnPlayBackPaused();
152 }
153
154 // message all registered callbacks that we resumed playing
155 void XBPython::OnPlayBackResumed()
156 {
157   TRACE;
158   LOCK_AND_COPY(std::vector<PVOID>,tmp,m_vecPlayerCallbackList);
159   for (PlayerCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
160     if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList,(*it)))
161       ((IPlayerCallback*)(*it))->OnPlayBackResumed();
162 }
163
164 // message all registered callbacks that user stopped playing
165 void XBPython::OnPlayBackStopped()
166 {
167   TRACE;
168   LOCK_AND_COPY(std::vector<PVOID>,tmp,m_vecPlayerCallbackList);
169   for (PlayerCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
170     if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList,(*it)))
171       ((IPlayerCallback*)(*it))->OnPlayBackStopped();
172 }
173
174 // message all registered callbacks that playback speed changed (FF/RW)
175 void XBPython::OnPlayBackSpeedChanged(int iSpeed)
176 {
177   TRACE;
178   LOCK_AND_COPY(std::vector<PVOID>,tmp,m_vecPlayerCallbackList);
179   for (PlayerCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
180     if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList,(*it)))
181       ((IPlayerCallback*)(*it))->OnPlayBackSpeedChanged(iSpeed);
182 }
183
184 // message all registered callbacks that player is seeking
185 void XBPython::OnPlayBackSeek(int iTime, int seekOffset)
186 {
187   TRACE;
188   LOCK_AND_COPY(std::vector<PVOID>,tmp,m_vecPlayerCallbackList);
189   for (PlayerCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
190     if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList,(*it)))
191       ((IPlayerCallback*)(*it))->OnPlayBackSeek(iTime, seekOffset);
192 }
193
194 // message all registered callbacks that player chapter seeked
195 void XBPython::OnPlayBackSeekChapter(int iChapter)
196 {
197   TRACE;
198   LOCK_AND_COPY(std::vector<PVOID>,tmp,m_vecPlayerCallbackList);
199   for (PlayerCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
200     if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList,(*it)))
201       ((IPlayerCallback*)(*it))->OnPlayBackSeekChapter(iChapter);
202 }
203
204 // message all registered callbacks that next item has been queued
205 void XBPython::OnQueueNextItem()
206 {
207   TRACE;
208   LOCK_AND_COPY(std::vector<PVOID>,tmp,m_vecPlayerCallbackList);
209   for (PlayerCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
210     if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList,(*it)))
211       ((IPlayerCallback*)(*it))->OnQueueNextItem();
212 }
213
214 void XBPython::RegisterPythonPlayerCallBack(IPlayerCallback* pCallback)
215 {
216   TRACE;
217   CSingleLock lock(m_vecPlayerCallbackList);
218   m_vecPlayerCallbackList.push_back(pCallback);
219 }
220
221 void XBPython::UnregisterPythonPlayerCallBack(IPlayerCallback* pCallback)
222 {
223   TRACE;
224   CSingleLock lock(m_vecPlayerCallbackList);
225   PlayerCallbackList::iterator it = m_vecPlayerCallbackList.begin();
226   while (it != m_vecPlayerCallbackList.end())
227   {
228     if (*it == pCallback)
229     {
230       it = m_vecPlayerCallbackList.erase(it);
231       m_vecPlayerCallbackList.hadSomethingRemoved = true;
232     }
233     else
234       ++it;
235   }
236 }
237
238 void XBPython::RegisterPythonMonitorCallBack(XBMCAddon::xbmc::Monitor* pCallback)
239 {
240   TRACE;
241   CSingleLock lock(m_vecMonitorCallbackList);
242   m_vecMonitorCallbackList.push_back(pCallback);
243 }
244
245 void XBPython::UnregisterPythonMonitorCallBack(XBMCAddon::xbmc::Monitor* pCallback)
246 {
247   TRACE;
248   CSingleLock lock(m_vecMonitorCallbackList);
249   MonitorCallbackList::iterator it = m_vecMonitorCallbackList.begin();
250   while (it != m_vecMonitorCallbackList.end())
251   {
252     if (*it == pCallback)
253     {
254       it = m_vecMonitorCallbackList.erase(it);
255       m_vecMonitorCallbackList.hadSomethingRemoved = true;
256     }
257     else
258       ++it;
259   }
260 }
261
262 void XBPython::OnSettingsChanged(const CStdString &ID)
263 {
264   TRACE;
265   LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>,tmp,m_vecMonitorCallbackList);
266   for (MonitorCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
267     if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList,(*it)) && ((*it)->GetId() == ID))
268       (*it)->OnSettingsChanged();
269 }  
270
271 void XBPython::OnScreensaverActivated()
272 {
273   TRACE;
274   LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>,tmp,m_vecMonitorCallbackList);
275   for (MonitorCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
276     if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList,(*it)))
277       (*it)->OnScreensaverActivated();
278
279
280 void XBPython::OnScreensaverDeactivated()
281 {
282   TRACE;
283   LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>,tmp,m_vecMonitorCallbackList);
284   for (MonitorCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
285     if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList,(*it)))
286       (*it)->OnScreensaverDeactivated();
287
288
289 void XBPython::OnDatabaseUpdated(const std::string &database)
290 {
291   TRACE;
292   LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>,tmp,m_vecMonitorCallbackList);
293   for (MonitorCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
294     if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList,(*it)))
295       (*it)->OnDatabaseUpdated(database);
296
297
298 void XBPython::OnDatabaseScanStarted(const std::string &database)
299 {
300   TRACE;
301   LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>,tmp,m_vecMonitorCallbackList);
302   for (MonitorCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
303     if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList,(*it)))
304       (*it)->OnDatabaseScanStarted(database);
305 }
306
307 void XBPython::OnAbortRequested(const CStdString &ID)
308 {
309   TRACE;
310   LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>,tmp,m_vecMonitorCallbackList);
311   for (MonitorCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
312   {
313     if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList,(*it)))
314     {
315       if (ID.IsEmpty())
316         (*it)->OnAbortRequested();
317       else if ((*it)->GetId() == ID)
318         (*it)->OnAbortRequested();
319     }
320   }  
321
322
323 /**
324 * Check for file and print an error if needed
325 */
326 bool XBPython::FileExist(const char* strFile)
327 {
328   if (!strFile)
329     return false;
330
331   if (!XFILE::CFile::Exists(strFile))
332   {
333     CLog::Log(LOGERROR, "Python: Cannot find '%s'", strFile);
334     return false;
335   }
336   return true;
337 }
338
339 void XBPython::RegisterExtensionLib(LibraryLoader *pLib)
340 {
341   if (!pLib)
342     return;
343
344   CSingleLock lock(m_critSection);
345
346   CLog::Log(LOGDEBUG,"%s, adding %s (%p)", __FUNCTION__, pLib->GetName(), (void*)pLib);
347   m_extensions.push_back(pLib);
348 }
349
350 void XBPython::UnregisterExtensionLib(LibraryLoader *pLib)
351 {
352   if (!pLib)
353     return;
354
355   CSingleLock lock(m_critSection);
356   CLog::Log(LOGDEBUG,"%s, removing %s (0x%p)", __FUNCTION__, pLib->GetName(), (void *)pLib);
357   PythonExtensionLibraries::iterator iter = m_extensions.begin();
358   while (iter != m_extensions.end())
359   {
360     if (*iter == pLib)
361     {
362       m_extensions.erase(iter);
363       break;
364     }
365     ++iter;
366   }
367 }
368
369 void XBPython::UnloadExtensionLibs()
370 {
371   CLog::Log(LOGDEBUG,"%s, clearing python extension libraries", __FUNCTION__);
372   CSingleLock lock(m_critSection);
373   PythonExtensionLibraries::iterator iter = m_extensions.begin();
374   while (iter != m_extensions.end())
375   {
376       DllLoaderContainer::ReleaseModule(*iter);
377       ++iter;
378   }
379   m_extensions.clear();
380 }
381
382 #define MODULE "xbmc"
383
384 #define RUNSCRIPT_PRAMBLE \
385         "" \
386         "import " MODULE "\n" \
387         "xbmc.abortRequested = False\n" \
388         "class xbmcout:\n" \
389         "\tdef __init__(self, loglevel=" MODULE ".LOGNOTICE):\n" \
390         "\t\tself.ll=loglevel\n" \
391         "\tdef write(self, data):\n" \
392         "\t\t" MODULE ".log(data,self.ll)\n" \
393         "\tdef close(self):\n" \
394         "\t\t" MODULE ".log('.')\n" \
395         "\tdef flush(self):\n" \
396         "\t\t" MODULE ".log('.')\n" \
397         "import sys\n" \
398         "sys.stdout = xbmcout()\n" \
399         "sys.stderr = xbmcout(" MODULE ".LOGERROR)\n"
400
401 #define RUNSCRIPT_OVERRIDE_HACK \
402         "" \
403         "import os\n" \
404         "def getcwd_xbmc():\n" \
405         "  import __main__\n" \
406         "  import warnings\n" \
407         "  if hasattr(__main__, \"__file__\"):\n" \
408         "    warnings.warn(\"os.getcwd() currently lies to you so please use addon.getAddonInfo('path') to find the script's root directory and DO NOT make relative path accesses based on the results of 'os.getcwd.' \", DeprecationWarning, stacklevel=2)\n" \
409         "    return os.path.dirname(__main__.__file__)\n" \
410         "  else:\n" \
411         "    return os.getcwd_original()\n" \
412         "" \
413         "def chdir_xbmc(dir):\n" \
414         "  raise RuntimeError(\"os.chdir not supported in xbmc\")\n" \
415         "" \
416         "os_getcwd_original = os.getcwd\n" \
417         "os.getcwd          = getcwd_xbmc\n" \
418         "os.chdir_orignal   = os.chdir\n" \
419         "os.chdir           = chdir_xbmc\n" \
420         ""
421  
422 #define RUNSCRIPT_POSTSCRIPT \
423         "print '-->Python Interpreter Initialized<--'\n" \
424         ""
425
426 #define RUNSCRIPT_BWCOMPATIBLE \
427   RUNSCRIPT_PRAMBLE RUNSCRIPT_OVERRIDE_HACK RUNSCRIPT_POSTSCRIPT
428
429 #define RUNSCRIPT_COMPLIANT \
430   RUNSCRIPT_PRAMBLE RUNSCRIPT_POSTSCRIPT
431
432 void XBPython::InitializeInterpreter(ADDON::AddonPtr addon)
433 {
434   TRACE;
435   {
436     GilSafeSingleLock lock(m_critSection);
437     initModule_xbmcgui();
438     initModule_xbmc();
439     initModule_xbmcplugin();
440     initModule_xbmcaddon();
441     initModule_xbmcvfs();
442   }
443
444   CStdString addonVer = ADDON::GetXbmcApiVersionDependency(addon);
445   bool bwcompatMode = (addon.get() == NULL || (ADDON::AddonVersion(addonVer) <= ADDON::AddonVersion("1.0")));
446   const char* runscript = bwcompatMode ? RUNSCRIPT_BWCOMPATIBLE : RUNSCRIPT_COMPLIANT;
447
448   // redirecting default output to debug console
449   if (PyRun_SimpleString(runscript) == -1)
450   {
451     CLog::Log(LOGFATAL, "Python Initialize Error");
452   }
453 }
454
455 void XBPython::DeInitializeInterpreter()
456 {
457   TRACE;
458 }
459
460 /**
461 * Should be called before executing a script
462 */
463 void XBPython::Initialize()
464 {
465   TRACE;
466   CLog::Log(LOGINFO, "initializing python engine. ");
467   CSingleLock lock(m_critSection);
468   m_iDllScriptCounter++;
469   if (!m_bInitialized)
470   {
471       // first we check if all necessary files are installed
472 #ifndef TARGET_POSIX
473       if(!FileExist("special://xbmc/system/python/DLLs/_socket.pyd") ||
474         !FileExist("special://xbmc/system/python/DLLs/_ssl.pyd") ||
475         !FileExist("special://xbmc/system/python/DLLs/bz2.pyd") ||
476         !FileExist("special://xbmc/system/python/DLLs/pyexpat.pyd") ||
477         !FileExist("special://xbmc/system/python/DLLs/select.pyd") ||
478         !FileExist("special://xbmc/system/python/DLLs/unicodedata.pyd"))
479       {
480         CLog::Log(LOGERROR, "Python: Missing files, unable to execute script");
481         Finalize();
482         return;
483       }
484 #endif
485
486
487 // Darwin packs .pyo files, we need PYTHONOPTIMIZE on in order to load them.
488 #if defined(TARGET_DARWIN)
489    setenv("PYTHONOPTIMIZE", "1", 1);
490 #endif
491       // Info about interesting python envvars available
492       // at http://docs.python.org/using/cmdline.html#environment-variables
493
494 #if !defined(TARGET_WINDOWS) && !defined(TARGET_ANDROID)
495       /* PYTHONOPTIMIZE is set off intentionally when using external Python.
496          Reason for this is because we cannot be sure what version of Python
497          was used to compile the various Python object files (i.e. .pyo,
498          .pyc, etc.). */
499         // check if we are running as real xbmc.app or just binary
500       if (!CUtil::GetFrameworksPath(true).IsEmpty())
501       {
502         // using external python, it's build looking for xxx/lib/python2.6
503         // so point it to frameworks which is where python2.6 is located
504         setenv("PYTHONHOME", CSpecialProtocol::TranslatePath("special://frameworks").c_str(), 1);
505         setenv("PYTHONPATH", CSpecialProtocol::TranslatePath("special://frameworks").c_str(), 1);
506         CLog::Log(LOGDEBUG, "PYTHONHOME -> %s", CSpecialProtocol::TranslatePath("special://frameworks").c_str());
507         CLog::Log(LOGDEBUG, "PYTHONPATH -> %s", CSpecialProtocol::TranslatePath("special://frameworks").c_str());
508       }
509       setenv("PYTHONCASEOK", "1", 1); //This line should really be removed
510 #elif defined(TARGET_WINDOWS)
511       // because the third party build of python is compiled with vs2008 we need
512       // a hack to set the PYTHONPATH
513       CStdString buf;
514       buf = "PYTHONPATH=" + CSpecialProtocol::TranslatePath("special://xbmc/system/python/DLLs") + ";" + CSpecialProtocol::TranslatePath("special://xbmc/system/python/Lib");
515       CEnvironment::putenv(buf);
516       buf = "PYTHONOPTIMIZE=1";
517       CEnvironment::putenv(buf);
518       buf = "PYTHONHOME=" + CSpecialProtocol::TranslatePath("special://xbmc/system/python");
519       CEnvironment::putenv(buf);
520       buf = "OS=win32";
521       CEnvironment::putenv(buf);
522
523 #elif defined(TARGET_ANDROID)
524       CStdString apkPath = getenv("XBMC_ANDROID_APK");
525       apkPath += "/assets/python2.6";
526       setenv("PYTHONHOME",apkPath.c_str(), 1);
527       setenv("PYTHONPATH", "", 1);
528       setenv("PYTHONOPTIMIZE","",1);
529       setenv("PYTHONNOUSERSITE","1",1);
530 #endif
531
532       if (PyEval_ThreadsInitialized())
533         PyEval_AcquireLock();
534       else
535         PyEval_InitThreads();
536
537       Py_Initialize();
538       PyEval_ReleaseLock();
539
540       // If this is not the first time we initialize Python, the interpreter
541       // lock already exists and we need to lock it as PyEval_InitThreads
542       // would not do that in that case.
543       PyEval_AcquireLock();
544       char* python_argv[1] = { (char*)"" } ;
545       PySys_SetArgv(1, python_argv);
546
547       if (!(m_mainThreadState = PyThreadState_Get()))
548         CLog::Log(LOGERROR, "Python threadstate is NULL.");
549       PyEval_ReleaseLock();
550
551       m_bInitialized = true;
552   }
553 }
554
555 /**
556 * Should be called when a script is finished
557 */
558 void XBPython::FinalizeScript()
559 {
560   TRACE;
561   CSingleLock lock(m_critSection);
562   // for linux - we never release the library. its loaded and stays in memory.
563   if (m_iDllScriptCounter)
564     m_iDllScriptCounter--;
565   else
566     CLog::Log(LOGERROR, "Python script counter attempted to become negative");
567   m_endtime = XbmcThreads::SystemClockMillis();
568 }
569
570
571 // Always called with the lock held on m_critSection
572 void XBPython::Finalize()
573 {
574   TRACE;
575   if (m_bInitialized)
576   {
577     CLog::Log(LOGINFO, "Python, unloading python shared library because no scripts are running anymore");
578
579     // set the m_bInitialized flag before releasing the lock. This will prevent
580     // Other methods that rely on this flag from an incorrect interpretation.
581     m_bInitialized    = false;
582     PyThreadState* curTs = (PyThreadState*)m_mainThreadState;
583     m_mainThreadState = NULL; // clear the main thread state before releasing the lock
584     {
585       CSingleExit exit(m_critSection);
586       PyEval_AcquireLock();
587       PyThreadState_Swap(curTs);
588
589       Py_Finalize();
590       PyEval_ReleaseLock();
591     }
592
593 #if !(defined(TARGET_DARWIN) || defined(TARGET_WINDOWS))
594     UnloadExtensionLibs();
595 #endif
596
597     // first free all dlls loaded by python, after that python24.dll (this is done by UnloadPythonDlls
598 #if !(defined(TARGET_DARWIN) || defined(TARGET_WINDOWS))
599     DllLoaderContainer::UnloadPythonDlls();
600 #endif
601 #if defined(TARGET_POSIX) && !defined(TARGET_DARWIN) && !defined(TARGET_FREEBSD)
602     // we can't release it on windows, as this is done in UnloadPythonDlls() for win32 (see above).
603     // The implementation for linux needs looking at - UnloadPythonDlls() currently only searches for "python24.dll"
604     // The implementation for osx can never unload the python dylib.
605     DllLoaderContainer::ReleaseModule(m_pDll);
606 #endif
607   }
608 }
609
610 void XBPython::FreeResources()
611 {
612   LOCK_AND_COPY(std::vector<PyElem>,tmpvec,m_vecPyList);
613   m_vecPyList.clear();
614   m_vecPyList.hadSomethingRemoved = true;
615
616   lock.Leave(); //unlock here because the python thread might lock when it exits
617
618   // cleanup threads that are still running
619   tmpvec.clear(); // boost releases the XBPyThreads which, if deleted, calls FinalizeScript
620 }
621
622 void XBPython::Process()
623 {
624   if (m_bLogin)
625   {
626     m_bLogin = false;
627
628     // autoexec.py - profile
629     CStdString strAutoExecPy = CSpecialProtocol::TranslatePath("special://profile/autoexec.py");
630
631     if ( XFILE::CFile::Exists(strAutoExecPy) )
632       evalFile(strAutoExecPy,ADDON::AddonPtr());
633     else
634       CLog::Log(LOGDEBUG, "%s - no profile autoexec.py (%s) found, skipping", __FUNCTION__, strAutoExecPy.c_str());
635   }
636
637   CSingleLock lock(m_vecPyList);
638
639   if (m_bInitialized)
640   {
641     PyList tmpvec;
642     for (PyList::iterator it = m_vecPyList.begin(); it != m_vecPyList.end();)
643     {
644       if (it->bDone)
645       {
646         tmpvec.push_back(*it);
647         it = m_vecPyList.erase(it);
648         m_vecPyList.hadSomethingRemoved = true;
649       }
650       else
651         ++it;
652     }
653     lock.Leave();
654
655     //delete scripts which are done
656     tmpvec.clear(); // boost releases the XBPyThreads which, if deleted, calls FinalizeScript
657
658     CSingleLock l2(m_critSection);
659     if(m_iDllScriptCounter == 0 && (XbmcThreads::SystemClockMillis() - m_endtime) > 10000 )
660     {
661       Finalize();
662     }
663   }
664 }
665
666 bool XBPython::StopScript(const CStdString &path)
667 {
668   TRACE;
669   int id = getScriptId(path);
670   if (id != -1)
671   {
672     /* if we are here we already know that this script is running.
673      * But we will check it again to be sure :)
674      */
675     if (isRunning(id))
676     {
677       stopScript(id);
678       return true;
679     }
680   }
681   return false;
682 }
683
684 int XBPython::evalFile(const CStdString &src, ADDON::AddonPtr addon)
685 {
686   std::vector<CStdString> argv;
687   return evalFile(src, argv, addon);
688 }
689 // execute script, returns -1 if script doesn't exist
690 int XBPython::evalFile(const CStdString &src, const std::vector<CStdString> &argv, ADDON::AddonPtr addon)
691 {
692   CSingleExit ex(g_graphicsContext);
693   // return if file doesn't exist
694   if (!XFILE::CFile::Exists(src))
695   {
696     CLog::Log(LOGERROR, "Python script \"%s\" does not exist", CSpecialProtocol::TranslatePath(src).c_str());
697     return -1;
698   }
699
700   // check if locked
701   if (CProfilesManager::Get().GetCurrentProfile().programsLocked() && !g_passwordManager.IsMasterLockUnlocked(true))
702     return -1;
703
704   CSingleLock lock(m_vecPyList);
705   Initialize();
706
707   if (!m_bInitialized) return -1;
708
709   m_nextid++;
710   boost::shared_ptr<XBPyThread> pyThread = boost::shared_ptr<XBPyThread>(new XBPyThread(this, m_nextid));
711   pyThread->setArgv(argv);
712   pyThread->setAddon(addon);
713   pyThread->evalFile(src);
714   PyElem inf;
715   inf.id        = m_nextid;
716   inf.bDone     = false;
717   inf.strFile   = src;
718   inf.pyThread  = pyThread;
719
720   m_vecPyList.push_back(inf);
721
722   return m_nextid;
723 }
724
725 void XBPython::setDone(int id)
726 {
727   CSingleLock lock(m_vecPyList);
728   PyList::iterator it = m_vecPyList.begin();
729   while (it != m_vecPyList.end())
730   {
731     if (it->id == id)
732     {
733       if (it->pyThread->isStopping())
734         CLog::Log(LOGINFO, "Python script interrupted by user");
735       else
736         CLog::Log(LOGINFO, "Python script stopped");
737       it->bDone = true;
738     }
739     ++it;
740   }
741 }
742
743 void XBPython::stopScript(int id)
744 {
745   CSingleExit ex(g_graphicsContext);
746   CSingleLock lock(m_vecPyList);
747   PyList::iterator it = m_vecPyList.begin();
748   while (it != m_vecPyList.end())
749   {
750     if (it->id == id) {
751       CLog::Log(LOGINFO, "Stopping script with id: %i", id);
752       it->pyThread->stop();
753       return;
754     }
755     ++it;
756   }
757 }
758
759 void* XBPython::getMainThreadState()
760 {
761   CSingleLock lock(m_critSection);
762   return m_mainThreadState;
763 }
764
765 int XBPython::ScriptsSize()
766 {
767   CSingleLock lock(m_vecPyList);
768   return m_vecPyList.size();
769 }
770
771 const char* XBPython::getFileName(int scriptId)
772 {
773   const char* cFileName = NULL;
774
775   CSingleLock lock(m_vecPyList);
776   PyList::iterator it = m_vecPyList.begin();
777   while (it != m_vecPyList.end())
778   {
779     if (it->id == scriptId)
780       cFileName = it->strFile.c_str();
781     ++it;
782   }
783
784   return cFileName;
785 }
786
787 int XBPython::getScriptId(const CStdString &strFile)
788 {
789   int iId = -1;
790
791   CSingleLock lock(m_vecPyList);
792
793   PyList::iterator it = m_vecPyList.begin();
794   while (it != m_vecPyList.end())
795   {
796     if (it->strFile == strFile)
797       iId = it->id;
798     ++it;
799   }
800
801   return iId;
802 }
803
804 bool XBPython::isRunning(int scriptId)
805 {
806   CSingleLock lock(m_vecPyList);
807
808   for(PyList::iterator it = m_vecPyList.begin(); it != m_vecPyList.end(); ++it)
809   {
810     if (it->id == scriptId)
811     {
812       if(it->bDone)
813         return false;
814       else
815         return true;
816     }
817   }
818   return false;
819 }
820
821 bool XBPython::isStopping(int scriptId)
822 {
823   bool bStopping = false;
824
825   CSingleLock lock(m_vecPyList);
826   PyList::iterator it = m_vecPyList.begin();
827   while (it != m_vecPyList.end())
828   {
829     if (it->id == scriptId)
830       bStopping = it->pyThread->isStopping();
831     ++it;
832   }
833
834   return bStopping;
835 }
836
837 int XBPython::GetPythonScriptId(int scriptPosition)
838 {
839   CSingleLock lock(m_vecPyList);
840   return (int)m_vecPyList[scriptPosition].id;
841 }
842
843 void XBPython::PulseGlobalEvent()
844 {
845   m_globalEvent.Set();
846 }
847
848 bool XBPython::WaitForEvent(CEvent& hEvent, unsigned int milliseconds)
849 {
850   // wait for either this event our our global event
851   XbmcThreads::CEventGroup eventGroup(&hEvent, &m_globalEvent, NULL);
852   CEvent* ret = eventGroup.wait(milliseconds);
853   if (ret)
854     m_globalEvent.Reset();
855   return ret == NULL ? false : true;
856 }
857
858 // execute script, returns -1 if script doesn't exist
859 int XBPython::evalString(const CStdString &src, const std::vector<CStdString> &argv)
860 {
861   CLog::Log(LOGDEBUG, "XBPython::evalString (python)");
862   CSingleLock lock(m_critSection);
863
864   Initialize();
865
866   if (!m_bInitialized)
867   {
868     CLog::Log(LOGERROR, "XBPython::evalString, python not initialized (python)");
869     return -1;
870   }
871
872   // Previous implementation would create a new thread for every script
873   m_nextid++;
874   boost::shared_ptr<XBPyThread> pyThread = boost::shared_ptr<XBPyThread>(new XBPyThread(this, m_nextid));
875   pyThread->setArgv(argv);
876   pyThread->evalString(src);
877
878   PyElem inf;
879   inf.id        = m_nextid;
880   inf.bDone     = false;
881   inf.strFile   = "<string>";
882   inf.pyThread  = pyThread;
883
884   lock.Leave();
885   CSingleLock l2(m_vecPyList);
886
887   m_vecPyList.push_back(inf);
888
889   return m_nextid;
890 }