cosmetics: m_ prefix, whitespace, removed commented code, painted python bikeshed...
[xbmc:xbmc.git] / xbmc / lib / libPython / XBPython.cpp
1 /*
2  *      Copyright (C) 2005-2008 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 // python.h should always be included first before any other includes
23 #if (defined HAVE_CONFIG_H) && (!defined WIN32)
24   #include "config.h"
25 #endif
26 #if (defined USE_EXTERNAL_PYTHON)
27   #if (defined HAVE_LIBPYTHON2_6)
28     #include <python2.6/Python.h>
29   #elif (defined HAVE_LIBPYTHON2_5)
30     #include <python2.5/Python.h>
31   #elif (defined HAVE_LIBPYTHON2_4)
32     #include <python2.4/Python.h>
33   #else
34     #error "Could not determine version of Python to use."
35   #endif
36 #else
37   #include "Python/Include/Python.h"
38 #endif
39 #include "cores/DllLoader/DllLoaderContainer.h"
40 #include "GUIPassword.h"
41
42 #include "XBPython.h"
43 #include "XBPythonDll.h"
44 #include "Settings.h"
45 #include "Profile.h"
46 #include "FileSystem/File.h"
47 #include "FileSystem/SpecialProtocol.h"
48 #include "utils/log.h"
49 #include "utils/SingleLock.h"
50
51 XBPython g_pythonParser;
52
53 #ifndef _LINUX
54 #define PYTHON_DLL "special://xbmc/system/python/python24.dll"
55 #else
56 #if defined(__APPLE__)
57 #if defined(__POWERPC__)
58 #define PYTHON_DLL "special://xbmc/system/python/python24-powerpc-osx.so"
59 #else
60 #define PYTHON_DLL "special://xbmc/system/python/python24-x86-osx.so"
61 #endif
62 #elif defined(__x86_64__)
63 #if (defined HAVE_LIBPYTHON2_6)
64 #define PYTHON_DLL "special://xbmc/system/python/python26-x86_64-linux.so"
65 #elif (defined HAVE_LIBPYTHON2_5)
66 #define PYTHON_DLL "special://xbmc/system/python/python25-x86_64-linux.so"
67 #else
68 #define PYTHON_DLL "special://xbmc/system/python/python24-x86_64-linux.so"
69 #endif
70 #elif defined(__powerpc__)
71 #if (defined HAVE_LIBPYTHON2_6)
72 #define PYTHON_DLL "special://xbmc/system/python/python26-powerpc-linux.so"
73 #elif (defined HAVE_LIBPYTHON2_5)
74 #define PYTHON_DLL "special://xbmc/system/python/python25-powerpc-linux.so"
75 #else
76 #define PYTHON_DLL "special://xbmc/system/python/python24-powerpc-linux.so"
77 #endif
78 #else /* !__x86_64__ && !__powerpc__ */
79 #if (defined HAVE_LIBPYTHON2_6)
80 #define PYTHON_DLL "special://xbmc/system/python/python26-i486-linux.so"
81 #elif (defined HAVE_LIBPYTHON2_5)
82 #define PYTHON_DLL "special://xbmc/system/python/python25-i486-linux.so"
83 #else
84 #define PYTHON_DLL "special://xbmc/system/python/python24-i486-linux.so"
85 #endif
86 #endif /* __x86_64__ */
87 #endif /* _LINUX */
88
89 extern "C" HMODULE __stdcall dllLoadLibraryA(LPCSTR file);
90 extern "C" BOOL __stdcall dllFreeLibrary(HINSTANCE hLibModule);
91
92 extern "C" {
93   void InitXBMCModule(void);
94   void InitXBMCTypes(void);
95   void DeinitXBMCModule(void);
96   void InitPluginModule(void);
97   void InitPluginTypes(void);
98   void DeinitPluginModule(void);
99   void InitGUIModule(void);
100   void InitGUITypes(void);
101   void DeinitGUIModule(void);
102 }
103
104 XBPython::XBPython()
105 {
106   m_bInitialized      = false;
107   m_bStartup          = false;
108   m_bLogin            = false;
109   m_nextid            = 0;
110   m_mainThreadState   = NULL;
111   m_hEvent            = CreateEvent(NULL, false, false, (char*)"pythonEvent");
112   m_globalEvent       = CreateEvent(NULL, false, false, (char*)"pythonGlobalEvent");
113   m_ThreadId          = CThread::GetCurrentThreadId();
114   m_iDllScriptCounter = 0;
115   m_vecPlayerCallbackList.clear();
116 }
117
118 XBPython::~XBPython()
119 {
120   CloseHandle(m_globalEvent);
121 }
122
123 bool XBPython::SendMessage(CGUIMessage& message)
124 {
125   return (evalFile(message.GetStringParam().c_str()) != -1);
126 }
127
128 // message all registered callbacks that xbmc stopped playing
129 void XBPython::OnPlayBackEnded()
130 {
131   if (m_bInitialized)
132   {
133     PlayerCallbackList::iterator it = m_vecPlayerCallbackList.begin();
134     while (it != m_vecPlayerCallbackList.end())
135     {
136       ((IPlayerCallback*)(*it))->OnPlayBackEnded();
137       it++;
138     }
139   }
140 }
141
142 // message all registered callbacks that we started playing
143 void XBPython::OnPlayBackStarted()
144 {
145   if (m_bInitialized)
146   {
147     PlayerCallbackList::iterator it = m_vecPlayerCallbackList.begin();
148     while (it != m_vecPlayerCallbackList.end())
149     {
150       ((IPlayerCallback*)(*it))->OnPlayBackStarted();
151       it++;
152     }
153   }
154 }
155
156 // message all registered callbacks that user stopped playing
157 void XBPython::OnPlayBackStopped()
158 {
159   if (m_bInitialized)
160   {
161     PlayerCallbackList::iterator it = m_vecPlayerCallbackList.begin();
162     while (it != m_vecPlayerCallbackList.end())
163     {
164       ((IPlayerCallback*)(*it))->OnPlayBackStopped();
165       it++;
166     }
167   }
168 }
169
170 void XBPython::RegisterPythonPlayerCallBack(IPlayerCallback* pCallback)
171 {
172   m_vecPlayerCallbackList.push_back(pCallback);
173 }
174
175 void XBPython::UnregisterPythonPlayerCallBack(IPlayerCallback* pCallback)
176 {
177   PlayerCallbackList::iterator it = m_vecPlayerCallbackList.begin();
178   while (it != m_vecPlayerCallbackList.end())
179   {
180     if (*it == pCallback)
181       it = m_vecPlayerCallbackList.erase(it);
182     else
183       it++;
184   }
185 }
186
187 /**
188 * Check for file and print an error if needed
189 */
190 bool XBPython::FileExist(const char* strFile)
191 {
192   if (!strFile)
193     return false;
194
195   if (!XFILE::CFile::Exists(strFile))
196   {
197     CLog::Log(LOGERROR, "Python: Cannot find '%s'", strFile);
198     return false;
199   }
200   return true;
201 }
202
203 void XBPython::RegisterExtensionLib(LibraryLoader *pLib)
204 {
205   if (!pLib) 
206     return;
207
208   CSingleLock lock(m_critSection);
209
210   CLog::Log(LOGDEBUG,"%s, adding %s (%p)", __FUNCTION__, pLib->GetName(), (void*)pLib);
211   m_extensions.push_back(pLib);
212 }
213
214 void XBPython::UnregisterExtensionLib(LibraryLoader *pLib)
215 {
216   if (!pLib) 
217     return;
218
219   CSingleLock lock(m_critSection);
220   CLog::Log(LOGDEBUG,"%s, removing %s (0x%p)", __FUNCTION__, pLib->GetName(), (void *)pLib);
221   PythonExtensionLibraries::iterator iter = m_extensions.begin();
222   while (iter != m_extensions.end())
223   {
224     if (*iter == pLib)
225     {
226       m_extensions.erase(iter);
227       break;
228     }
229     iter++;
230   }
231 }
232
233 void XBPython::UnloadExtensionLibs()
234 {
235   CLog::Log(LOGDEBUG,"%s, clearing python extension libraries", __FUNCTION__);
236   CSingleLock lock(m_critSection);
237   PythonExtensionLibraries::iterator iter = m_extensions.begin();
238   while (iter != m_extensions.end())
239   {
240       DllLoaderContainer::ReleaseModule(*iter);
241       iter++;
242   }
243   m_extensions.clear();
244 }
245
246 void XBPython::InitializeInterpreter()
247 {
248   InitXBMCModule(); // init xbmc modules
249   InitPluginModule(); // init plugin modules
250   InitGUIModule(); // init xbmcgui modules
251
252   // redirecting default output to debug console
253   if (PyRun_SimpleString(""
254         "import xbmc\n"
255         "class xbmcout:\n"
256         "       def write(self, data):\n"
257         "               xbmc.output(data)\n"
258         "       def close(self):\n"
259         "               xbmc.output('.')\n"
260         "       def flush(self):\n"
261         "               xbmc.output('.')\n"
262         "\n"
263         "import sys\n"
264         "sys.stdout = xbmcout()\n"
265         "sys.stderr = xbmcout()\n"
266         "print '-->Python Interpreter Initialized<--'\n"
267         "") == -1)
268   {
269     CLog::Log(LOGFATAL, "Python Initialize Error");
270   }
271 }
272
273 void XBPython::DeInitializeInterpreter()
274 {
275   DeinitXBMCModule(); 
276   DeinitPluginModule(); 
277   DeinitGUIModule(); 
278 }
279
280 /**
281 * Should be called before executing a script
282 */
283 void XBPython::Initialize()
284 {
285   CLog::Log(LOGINFO, "initializing python engine. ");
286   CSingleLock lock(m_critSection);
287   m_iDllScriptCounter++;
288   if (!m_bInitialized)
289   {
290     if (CThread::IsCurrentThread(m_ThreadId))
291     {
292       m_pDll = DllLoaderContainer::LoadModule(PYTHON_DLL, NULL, true);
293
294       if (!m_pDll || !python_load_dll(*m_pDll))
295       {
296         CLog::Log(LOGFATAL, "Python: error loading python24.dll");
297         Finalize();
298         return;
299       }
300
301       // first we check if all necessary files are installed
302 #ifndef _LINUX      
303       if (!FileExist("special://xbmc/system/python/python24.zlib") ||
304         !FileExist("special://xbmc/system/python/DLLs/_socket.pyd") ||
305         !FileExist("special://xbmc/system/python/DLLs/_ssl.pyd") ||
306         !FileExist("special://xbmc/system/python/DLLs/bz2.pyd") ||
307         !FileExist("special://xbmc/system/python/DLLs/pyexpat.pyd") ||
308         !FileExist("special://xbmc/system/python/DLLs/select.pyd") ||
309         !FileExist("special://xbmc/system/python/DLLs/unicodedata.pyd") ||
310         !FileExist("special://xbmc/system/python/DLLs/zlib.pyd"))
311       {
312         CLog::Log(LOGERROR, "Python: Missing files, unable to execute script");
313         Finalize();
314         return;
315       }
316 #endif        
317
318
319       // Info about interesting python envvars available 
320       // at http://docs.python.org/using/cmdline.html#environment-variables
321
322 #if (!defined USE_EXTERNAL_PYTHON)
323 #ifdef _LINUX
324       // Required for python to find optimized code (pyo) files
325       setenv("PYTHONOPTIMIZE", "1", 1);
326       setenv("PYTHONHOME", _P("special://xbmc/system/python").c_str(), 1);
327 #ifdef __APPLE__
328       // OSX uses contents from extracted zip, 3X to 4X times faster during Py_Initialize
329       setenv("PYTHONPATH", _P("special://xbmc/system/python/Lib").c_str(), 1);
330 #else
331       setenv("PYTHONPATH", _P("special://xbmc/system/python/python24.zip").c_str(), 1);
332 #endif /* __APPLE__ */
333       setenv("PYTHONCASEOK", "1", 1);
334       CLog::Log(LOGDEBUG, "Python wrapper library linked with internal Python library");
335 #endif /* _LINUX */
336 #else
337       /* PYTHONOPTIMIZE is set off intentionally when using external Python.
338          Reason for this is because we cannot be sure what version of Python
339          was used to compile the various Python object files (i.e. .pyo,
340          .pyc, etc.). */
341       setenv("PYTHONCASEOK", "1", 1); //This line should really be removed
342       CLog::Log(LOGDEBUG, "Python wrapper library linked with system Python library");
343 #endif /* USE_EXTERNAL_PYTHON */
344
345       Py_Initialize();
346       PyEval_InitThreads();
347
348       char* python_argv[1] = { (char*)"" } ;
349       PySys_SetArgv(1, python_argv);
350
351       InitXBMCTypes();
352       InitGUITypes();
353       InitPluginTypes();
354
355       m_mainThreadState = PyThreadState_Get();
356
357       // release the lock
358       PyEval_ReleaseLock();
359
360       m_bInitialized = true;
361       PulseEvent(m_hEvent);
362     }
363     else
364     {
365       // only the main thread should initialize python.
366       m_iDllScriptCounter--;
367
368       lock.Leave();
369       WaitForSingleObject(m_hEvent, INFINITE);
370       lock.Enter();
371     }
372   }
373 }
374
375 /**
376 * Should be called when a script is finished
377 */
378 void XBPython::Finalize()
379 {
380   CSingleLock lock(m_critSection);
381   // for linux - we never release the library. its loaded and stays in memory.
382   m_iDllScriptCounter--;
383   if (m_iDllScriptCounter == 0 && m_bInitialized)
384   {
385     CLog::Log(LOGINFO, "Python, unloading python24.dll because no scripts are running anymore");
386     PyEval_AcquireLock();
387     PyThreadState_Swap(m_mainThreadState);
388     Py_Finalize();
389
390     UnloadExtensionLibs();
391
392     // first free all dlls loaded by python, after that python24.dll (this is done by UnloadPythonDlls
393     DllLoaderContainer::UnloadPythonDlls();
394 #ifdef _LINUX
395     // we can't release it on windows, as this is done in UnloadPythonDlls() for win32 (see above).
396     // The implementation for linux and os x needs looking at - UnloadPythonDlls() currently only searches for "python24.dll"
397     DllLoaderContainer::ReleaseModule(m_pDll);
398 #endif
399     m_hModule         = NULL;
400     m_mainThreadState = NULL;
401     m_bInitialized    = false;
402   }
403 }
404
405 void XBPython::FreeResources()
406 {
407   CSingleLock lock(m_critSection);
408   if (m_bInitialized)
409   {
410     // cleanup threads that are still running
411     PyList::iterator it = m_vecPyList.begin();
412     while (it != m_vecPyList.end())
413     { 
414       lock.Leave(); //unlock here because the python thread might lock when it exits
415       delete it->pyThread;
416       lock.Enter();
417       it = m_vecPyList.erase(it);
418       Finalize();
419     }
420   }
421
422   if (m_hEvent)
423     CloseHandle(m_hEvent);
424 }
425
426 void XBPython::Process()
427 {
428   if (m_bStartup)
429   {
430     m_bStartup = false;
431     if (evalFile("special://home/scripts/autoexec.py") < 0)
432       evalFile("special://xbmc/scripts/autoexec.py");
433   }
434
435   if (m_bLogin)
436   {
437     m_bLogin = false;
438     evalFile("special://profile/scripts/autoexec.py");
439   }
440
441   CSingleLock lock(m_critSection);
442
443   if (m_bInitialized)
444   {
445     PyList::iterator it = m_vecPyList.begin();
446     while (it != m_vecPyList.end())
447     {
448       //delete scripts which are done
449       if (it->bDone)
450       {
451         delete it->pyThread;
452         it = m_vecPyList.erase(it);
453         Finalize();
454       }
455       else ++it;
456     }
457   }
458 }
459
460 int XBPython::evalFile(const char *src) { return evalFile(src, 0, NULL); }
461 // execute script, returns -1 if script doesn't exist
462 int XBPython::evalFile(const char *src, const unsigned int argc, const char ** argv)
463 {
464   // return if file doesn't exist
465   if (!XFILE::CFile::Exists(src))
466   {
467     CLog::Log(LOGERROR, "Python script \"%s\" does not exist", src);
468     return -1;
469   }
470
471   // check if locked
472   int profile = g_settings.m_iLastLoadedProfileIndex;
473   if (g_settings.m_vecProfiles[profile].programsLocked() &&
474       g_settings.m_vecProfiles[0].getLockMode() != LOCK_MODE_EVERYONE)
475     if (!g_passwordManager.IsMasterLockUnlocked(true))
476       return -1;
477
478   Initialize();
479
480   if (!m_bInitialized) return -1;
481
482   m_nextid++;
483   XBPyThread *pyThread = new XBPyThread(this, m_nextid);
484   if (argv != NULL)
485     pyThread->setArgv(argc, argv);
486   pyThread->evalFile(src);
487   PyElem inf;
488   inf.id        = m_nextid;
489   inf.bDone     = false;
490   inf.strFile   = src;
491   inf.pyThread  = pyThread;
492
493   CSingleLock lock(m_critSection);
494   m_vecPyList.push_back(inf);
495
496   return m_nextid;
497 }
498
499 void XBPython::setDone(int id)
500 {
501   CSingleLock lock(m_critSection);
502   PyList::iterator it = m_vecPyList.begin();
503   while (it != m_vecPyList.end())
504   {
505     if (it->id == id)
506     {
507       if (it->pyThread->isStopping())
508         CLog::Log(LOGINFO, "Python script interrupted by user");
509       else
510         CLog::Log(LOGINFO, "Python script stopped");
511       it->bDone = true;
512     }
513     ++it;
514   }
515 }
516
517 void XBPython::stopScript(int id)
518 {
519   CSingleLock lock(m_critSection);
520   PyList::iterator it = m_vecPyList.begin();
521   while (it != m_vecPyList.end())
522   {
523     if (it->id == id) {
524       CLog::Log(LOGINFO, "Stopping script with id: %i", id);
525       it->pyThread->stop();
526       return;
527     }
528     ++it;
529   }
530 }
531
532 PyThreadState *XBPython::getMainThreadState()
533 {
534   return m_mainThreadState;
535 }
536
537 int XBPython::ScriptsSize()
538 {
539   CSingleLock lock(m_critSection);
540   return m_vecPyList.size();
541 }
542
543 const char* XBPython::getFileName(int scriptId)
544 {
545   const char* cFileName = NULL;
546  
547   CSingleLock lock(m_critSection);
548   PyList::iterator it = m_vecPyList.begin();
549   while (it != m_vecPyList.end())
550   {
551     if (it->id == scriptId)
552       cFileName = it->strFile.c_str();
553     ++it;
554   }
555
556   return cFileName;
557 }
558
559 int XBPython::getScriptId(const char* strFile)
560 {
561   int iId = -1;
562   
563   CSingleLock lock(m_critSection);
564
565   PyList::iterator it = m_vecPyList.begin();
566   while (it != m_vecPyList.end())
567   {
568     if (!stricmp(it->strFile.c_str(), strFile))
569       iId = it->id;
570     ++it;
571   }
572   
573   return iId;
574 }
575
576 bool XBPython::isRunning(int scriptId)
577 {
578   bool bRunning = false;
579   CSingleLock lock(m_critSection); 
580
581   PyList::iterator it = m_vecPyList.begin();
582   while (it != m_vecPyList.end())
583   {
584     if (it->id == scriptId)
585       bRunning = true;
586     ++it;
587   }
588   
589   return bRunning;
590 }
591
592 bool XBPython::isStopping(int scriptId)
593 {
594   bool bStopping = false;
595   
596   CSingleLock lock(m_critSection);
597   PyList::iterator it = m_vecPyList.begin();
598   while (it != m_vecPyList.end())
599   {
600     if (it->id == scriptId)
601       bStopping = it->pyThread->isStopping();
602     ++it;
603   }
604   
605   return bStopping;
606 }
607
608 int XBPython::GetPythonScriptId(int scriptPosition)
609 {
610   CSingleLock lock(m_critSection);
611   return (int)m_vecPyList[scriptPosition].id;
612 }
613
614 void XBPython::PulseGlobalEvent()
615 {
616   SetEvent(m_globalEvent);
617 }
618
619 void XBPython::WaitForEvent(HANDLE hEvent, DWORD timeout)
620 {
621   // wait for either this event our our global event
622   HANDLE handles[2] = { hEvent, m_globalEvent };
623   WaitForMultipleObjects(2, handles, FALSE, timeout);
624   ResetEvent(m_globalEvent);
625 }
626
627 // execute script, returns -1 if script doesn't exist
628 int XBPython::evalString(const char *src, const unsigned int argc, const char ** argv)
629 {
630   CLog::Log(LOGDEBUG, "XBPython::evalString (python)");
631   
632   Initialize();
633
634   if (!m_bInitialized) 
635   {
636     CLog::Log(LOGERROR, "XBPython::evalString, python not initialized (python)");
637     return -1;
638   }
639
640   // Previous implementation would create a new thread for every script
641   m_nextid++;
642   XBPyThread *pyThread = new XBPyThread(this, m_nextid);
643   if (argv != NULL)
644     pyThread->setArgv(argc, argv);
645   pyThread->evalString(src);
646   
647   PyElem inf;
648   inf.id        = m_nextid;
649   inf.bDone     = false;
650   inf.strFile   = "<string>";
651   inf.pyThread  = pyThread;
652
653   CSingleLock lock(m_critSection);
654   m_vecPyList.push_back(inf);
655
656   return m_nextid;
657 }