Merge pull request #2846 from Montellese/multi_language_addons
authorSascha Montellese <sascha.montellese@gmail.com>
Mon, 8 Jul 2013 22:02:51 +0000 (15:02 -0700)
committerSascha Montellese <sascha.montellese@gmail.com>
Mon, 8 Jul 2013 22:02:51 +0000 (15:02 -0700)
language agnostic script invocation

29 files changed:
Makefile.in
XBMC.xcodeproj/project.pbxproj
project/VS2010Express/XBMC.vcxproj
project/VS2010Express/XBMC.vcxproj.filters
xbmc/Application.cpp
xbmc/Application.h
xbmc/ApplicationMessenger.cpp
xbmc/addons/ScreenSaver.cpp
xbmc/addons/Service.cpp
xbmc/filesystem/PluginDirectory.cpp
xbmc/interfaces/Builtins.cpp
xbmc/interfaces/generic/ILanguageInvocationHandler.h [new file with mode: 0644]
xbmc/interfaces/generic/ILanguageInvoker.h [new file with mode: 0644]
xbmc/interfaces/generic/LanguageInvokerThread.cpp [new file with mode: 0644]
xbmc/interfaces/generic/LanguageInvokerThread.h [new file with mode: 0644]
xbmc/interfaces/generic/Makefile [new file with mode: 0644]
xbmc/interfaces/generic/ScriptInvocationManager.cpp [new file with mode: 0644]
xbmc/interfaces/generic/ScriptInvocationManager.h [new file with mode: 0644]
xbmc/interfaces/python/Makefile.in
xbmc/interfaces/python/PythonInvoker.cpp [new file with mode: 0644]
xbmc/interfaces/python/PythonInvoker.h [new file with mode: 0644]
xbmc/interfaces/python/XBPyThread.cpp [deleted file]
xbmc/interfaces/python/XBPyThread.h [deleted file]
xbmc/interfaces/python/XBPython.cpp
xbmc/interfaces/python/XBPython.h
xbmc/utils/Weather.cpp
xbmc/windows/GUIMediaWindow.cpp
xbmc/windows/GUIWindowFileManager.cpp
xbmc/windows/GUIWindowLoginScreen.cpp

index 017af79..5dd2057 100644 (file)
@@ -48,6 +48,7 @@ DIRECTORY_ARCHIVES=$(DVDPLAYER_ARCHIVES) \
                    xbmc/filesystem/filesystem.a \
                    xbmc/guilib/guilib.a \
                    xbmc/input/input.a \
+                   xbmc/interfaces/generic/interfaces-generic.a \
                    xbmc/interfaces/info/info.a \
                    xbmc/interfaces/interfaces.a \
                    xbmc/interfaces/json-rpc/json-rpc.a \
index cdaef65..0a01515 100644 (file)
                DF402A64164461B9001C56B8 /* LanguageHook.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F502BFE4160F34DC00C96C76 /* LanguageHook.cpp */; };
                DF402A65164461B9001C56B8 /* swig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F502BFF0160F36AD00C96C76 /* swig.cpp */; };
                DF402A66164461B9001C56B8 /* XBPython.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F502BFE6160F34FE00C96C76 /* XBPython.cpp */; };
-               DF402A67164461B9001C56B8 /* XBPyThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F502BFE8160F34FE00C96C76 /* XBPyThread.cpp */; };
                DF404A3916B9896C00D8023E /* cximage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF404A3416B9896C00D8023E /* cximage.cpp */; };
                DF404A3A16B9896C00D8023E /* imagefactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF404A3716B9896C00D8023E /* imagefactory.cpp */; };
+               DF40BC1E178B4BEC009DB567 /* PythonInvoker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF40BC1C178B4BEC009DB567 /* PythonInvoker.cpp */; };
+               DF40BC1F178B4BEC009DB567 /* PythonInvoker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF40BC1C178B4BEC009DB567 /* PythonInvoker.cpp */; };
+               DF40BC20178B4BEC009DB567 /* PythonInvoker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF40BC1C178B4BEC009DB567 /* PythonInvoker.cpp */; };
+               DF40BC29178B4C07009DB567 /* LanguageInvokerThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF40BC24178B4C07009DB567 /* LanguageInvokerThread.cpp */; };
+               DF40BC2B178B4C07009DB567 /* ScriptInvocationManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF40BC27178B4C07009DB567 /* ScriptInvocationManager.cpp */; };
+               DF40BC2C178B4C07009DB567 /* LanguageInvokerThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF40BC24178B4C07009DB567 /* LanguageInvokerThread.cpp */; };
+               DF40BC2E178B4C07009DB567 /* ScriptInvocationManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF40BC27178B4C07009DB567 /* ScriptInvocationManager.cpp */; };
+               DF40BC2F178B4C07009DB567 /* LanguageInvokerThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF40BC24178B4C07009DB567 /* LanguageInvokerThread.cpp */; };
+               DF40BC31178B4C07009DB567 /* ScriptInvocationManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF40BC27178B4C07009DB567 /* ScriptInvocationManager.cpp */; };
                DF448457140048A60069344B /* AirTunesServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF448455140048A60069344B /* AirTunesServer.cpp */; };
                DF44845E140048C80069344B /* PipesManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF44845B140048C80069344B /* PipesManager.cpp */; };
                DF52566D1732C1890094A464 /* DVDDemuxCDDA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF52566B1732C1890094A464 /* DVDDemuxCDDA.cpp */; };
                DFF0F2E417528350002DA3A4 /* PyContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB02DE816629DBA00F37752 /* PyContext.cpp */; };
                DFF0F2E517528350002DA3A4 /* swig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F502BFF0160F36AD00C96C76 /* swig.cpp */; };
                DFF0F2E617528350002DA3A4 /* XBPython.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F502BFE6160F34FE00C96C76 /* XBPython.cpp */; };
-               DFF0F2E717528350002DA3A4 /* XBPyThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F502BFE8160F34FE00C96C76 /* XBPyThread.cpp */; };
                DFF0F2E817528350002DA3A4 /* AnnouncementManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5A7A700112893E50059D6AA /* AnnouncementManager.cpp */; };
                DFF0F2E917528350002DA3A4 /* Builtins.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CCF7F1B1069F3AE00992676 /* Builtins.cpp */; };
                DFF0F2EA17528350002DA3A4 /* ConvUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E1D6A0D25F9FD00618676 /* ConvUtils.cpp */; };
                E4991365174E5EEF00741B6D /* PyContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB02DE816629DBA00F37752 /* PyContext.cpp */; };
                E4991366174E5EEF00741B6D /* swig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F502BFF0160F36AD00C96C76 /* swig.cpp */; };
                E4991367174E5EEF00741B6D /* XBPython.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F502BFE6160F34FE00C96C76 /* XBPython.cpp */; };
-               E4991368174E5EEF00741B6D /* XBPyThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F502BFE8160F34FE00C96C76 /* XBPyThread.cpp */; };
                E4991369174E5EEF00741B6D /* AnnouncementManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5A7A700112893E50059D6AA /* AnnouncementManager.cpp */; };
                E499136A174E5EEF00741B6D /* Builtins.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CCF7F1B1069F3AE00992676 /* Builtins.cpp */; };
                E499136B174E5EF700741B6D /* ConvUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E1D6A0D25F9FD00618676 /* ConvUtils.cpp */; };
                DF404A3616B9896C00D8023E /* iimage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iimage.h; sourceTree = "<group>"; };
                DF404A3716B9896C00D8023E /* imagefactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = imagefactory.cpp; sourceTree = "<group>"; };
                DF404A3816B9896C00D8023E /* imagefactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = imagefactory.h; sourceTree = "<group>"; };
+               DF40BC1C178B4BEC009DB567 /* PythonInvoker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PythonInvoker.cpp; path = python/PythonInvoker.cpp; sourceTree = "<group>"; };
+               DF40BC1D178B4BEC009DB567 /* PythonInvoker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PythonInvoker.h; path = python/PythonInvoker.h; sourceTree = "<group>"; };
+               DF40BC22178B4C07009DB567 /* ILanguageInvocationHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ILanguageInvocationHandler.h; sourceTree = "<group>"; };
+               DF40BC23178B4C07009DB567 /* ILanguageInvoker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ILanguageInvoker.h; sourceTree = "<group>"; };
+               DF40BC24178B4C07009DB567 /* LanguageInvokerThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LanguageInvokerThread.cpp; sourceTree = "<group>"; };
+               DF40BC25178B4C07009DB567 /* LanguageInvokerThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LanguageInvokerThread.h; sourceTree = "<group>"; };
+               DF40BC27178B4C07009DB567 /* ScriptInvocationManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScriptInvocationManager.cpp; sourceTree = "<group>"; };
+               DF40BC28178B4C07009DB567 /* ScriptInvocationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScriptInvocationManager.h; sourceTree = "<group>"; };
                DF448455140048A60069344B /* AirTunesServer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AirTunesServer.cpp; sourceTree = "<group>"; };
                DF448456140048A60069344B /* AirTunesServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AirTunesServer.h; sourceTree = "<group>"; };
                DF44845B140048C80069344B /* PipesManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PipesManager.cpp; sourceTree = "<group>"; };
                F502BFE5160F34DC00C96C76 /* LanguageHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LanguageHook.h; path = python/LanguageHook.h; sourceTree = "<group>"; };
                F502BFE6160F34FE00C96C76 /* XBPython.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XBPython.cpp; path = python/XBPython.cpp; sourceTree = "<group>"; };
                F502BFE7160F34FE00C96C76 /* XBPython.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XBPython.h; path = python/XBPython.h; sourceTree = "<group>"; };
-               F502BFE8160F34FE00C96C76 /* XBPyThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XBPyThread.cpp; path = python/XBPyThread.cpp; sourceTree = "<group>"; };
-               F502BFE9160F34FE00C96C76 /* XBPyThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XBPyThread.h; path = python/XBPyThread.h; sourceTree = "<group>"; };
                F502BFF0160F36AD00C96C76 /* swig.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = swig.cpp; path = python/swig.cpp; sourceTree = "<group>"; };
                F502BFF1160F36AD00C96C76 /* swig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = swig.h; path = python/swig.h; sourceTree = "<group>"; };
                F50629780E57B9680066625A /* MultiPathFile.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = MultiPathFile.cpp; sourceTree = "<group>"; };
                4367217312D6640E002508E6 /* interfaces */ = {
                        isa = PBXGroup;
                        children = (
+                               DF40BC21178B4C07009DB567 /* generic */,
                                7C89674213C03B21003631FE /* info */,
                                F5AE407F13415D9E0004BD79 /* json-rpc */,
                                DF1AD17B15FCE77900E10810 /* legacy */,
                                F502BFE5160F34DC00C96C76 /* LanguageHook.h */,
                                DFB02DE816629DBA00F37752 /* PyContext.cpp */,
                                DFB02DE916629DBA00F37752 /* PyContext.h */,
+                               DF40BC1C178B4BEC009DB567 /* PythonInvoker.cpp */,
+                               DF40BC1D178B4BEC009DB567 /* PythonInvoker.h */,
                                F502BFF0160F36AD00C96C76 /* swig.cpp */,
                                F502BFF1160F36AD00C96C76 /* swig.h */,
                                F502BFE6160F34FE00C96C76 /* XBPython.cpp */,
                                F502BFE7160F34FE00C96C76 /* XBPython.h */,
-                               F502BFE8160F34FE00C96C76 /* XBPyThread.cpp */,
-                               F502BFE9160F34FE00C96C76 /* XBPyThread.h */,
                        );
                        name = python;
                        sourceTree = "<group>";
                        path = windows;
                        sourceTree = "<group>";
                };
+               DF40BC21178B4C07009DB567 /* generic */ = {
+                       isa = PBXGroup;
+                       children = (
+                               DF40BC22178B4C07009DB567 /* ILanguageInvocationHandler.h */,
+                               DF40BC23178B4C07009DB567 /* ILanguageInvoker.h */,
+                               DF40BC24178B4C07009DB567 /* LanguageInvokerThread.cpp */,
+                               DF40BC25178B4C07009DB567 /* LanguageInvokerThread.h */,
+                               DF40BC27178B4C07009DB567 /* ScriptInvocationManager.cpp */,
+                               DF40BC28178B4C07009DB567 /* ScriptInvocationManager.h */,
+                       );
+                       path = generic;
+                       sourceTree = "<group>";
+               };
                DF527729151BAF4C00B5B63B /* websocket */ = {
                        isa = PBXGroup;
                        children = (
                                DF402A64164461B9001C56B8 /* LanguageHook.cpp in Sources */,
                                DF402A65164461B9001C56B8 /* swig.cpp in Sources */,
                                DF402A66164461B9001C56B8 /* XBPython.cpp in Sources */,
-                               DF402A67164461B9001C56B8 /* XBPyThread.cpp in Sources */,
                                F5EDC48C1651A6F900B852D8 /* GroupUtils.cpp in Sources */,
                                7C7CEAF1165629530059C9EB /* AELimiter.cpp in Sources */,
                                DFB02DEA16629DBA00F37752 /* PyContext.cpp in Sources */,
                                DFE4095B17417FDF00473BD9 /* LegacyPathTranslation.cpp in Sources */,
                                0E3036EC1760F68A00D93596 /* FavouritesDirectory.cpp in Sources */,
                                551C3A45175A12010051AAAD /* VDA.cpp in Sources */,
+                               DF40BC20178B4BEC009DB567 /* PythonInvoker.cpp in Sources */,
+                               DF40BC2F178B4C07009DB567 /* LanguageInvokerThread.cpp in Sources */,
+                               DF40BC31178B4C07009DB567 /* ScriptInvocationManager.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                DFF0F2E417528350002DA3A4 /* PyContext.cpp in Sources */,
                                DFF0F2E517528350002DA3A4 /* swig.cpp in Sources */,
                                DFF0F2E617528350002DA3A4 /* XBPython.cpp in Sources */,
-                               DFF0F2E717528350002DA3A4 /* XBPyThread.cpp in Sources */,
                                DFF0F2E817528350002DA3A4 /* AnnouncementManager.cpp in Sources */,
                                DFF0F2E917528350002DA3A4 /* Builtins.cpp in Sources */,
                                DFF0F2EA17528350002DA3A4 /* ConvUtils.cpp in Sources */,
                                DF3C3C0F1752A7EE000989C3 /* IOSExternalTouchController.mm in Sources */,
                                DF3C3C101752A7EE000989C3 /* IOSScreenManager.mm in Sources */,
                                0E3036EE1760F68A00D93596 /* FavouritesDirectory.cpp in Sources */,
+                               DF40BC1F178B4BEC009DB567 /* PythonInvoker.cpp in Sources */,
+                               DF40BC2C178B4C07009DB567 /* LanguageInvokerThread.cpp in Sources */,
+                               DF40BC2E178B4C07009DB567 /* ScriptInvocationManager.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                E4991365174E5EEF00741B6D /* PyContext.cpp in Sources */,
                                E4991366174E5EEF00741B6D /* swig.cpp in Sources */,
                                E4991367174E5EEF00741B6D /* XBPython.cpp in Sources */,
-                               E4991368174E5EEF00741B6D /* XBPyThread.cpp in Sources */,
                                E4991369174E5EEF00741B6D /* AnnouncementManager.cpp in Sources */,
                                E499136A174E5EEF00741B6D /* Builtins.cpp in Sources */,
                                E499136B174E5EF700741B6D /* ConvUtils.cpp in Sources */,
                                E4991593174E707400741B6D /* cc_decoder.c in Sources */,
                                E4991596174E70BF00741B6D /* yuv2rgb.neon.S in Sources */,
                                0E3036ED1760F68A00D93596 /* FavouritesDirectory.cpp in Sources */,
+                               DF40BC1E178B4BEC009DB567 /* PythonInvoker.cpp in Sources */,
+                               DF40BC29178B4C07009DB567 /* LanguageInvokerThread.cpp in Sources */,
+                               DF40BC2B178B4C07009DB567 /* ScriptInvocationManager.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
index f2d19d1..0df9a6c 100644 (file)
     <ClCompile Include="..\..\xbmc\input\XBMC_keytable.cpp" />
     <ClCompile Include="..\..\xbmc\interfaces\AnnouncementManager.cpp" />
     <ClCompile Include="..\..\xbmc\interfaces\Builtins.cpp" />
+    <ClCompile Include="..\..\xbmc\interfaces\generic\LanguageInvokerThread.cpp" />
+    <ClCompile Include="..\..\xbmc\interfaces\generic\ScriptInvocationManager.cpp" />
     <ClCompile Include="..\..\xbmc\interfaces\info\InfoBool.cpp" />
     <ClCompile Include="..\..\xbmc\interfaces\info\SkinVariable.cpp" />
     <ClCompile Include="..\..\xbmc\interfaces\json-rpc\AddonsOperations.cpp" />
     <ClCompile Include="..\..\xbmc\interfaces\python\generated\AddonModuleXbmcvfs.cpp" />
     <ClCompile Include="..\..\xbmc\interfaces\python\LanguageHook.cpp" />
     <ClCompile Include="..\..\xbmc\interfaces\python\PyContext.cpp" />
+    <ClCompile Include="..\..\xbmc\interfaces\python\PythonInvoker.cpp" />
     <ClCompile Include="..\..\xbmc\interfaces\python\swig.cpp" />
     <ClCompile Include="..\..\xbmc\interfaces\python\test\TestSwig.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug (DirectX)|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Template|Win32'">true</ExcludedFromBuild>
     </ClCompile>
     <ClCompile Include="..\..\xbmc\interfaces\python\XBPython.cpp" />
-    <ClCompile Include="..\..\xbmc\interfaces\python\XBPyThread.cpp" />
     <ClCompile Include="..\..\xbmc\LangInfo.cpp" />
     <ClCompile Include="..\..\xbmc\MediaSource.cpp" />
     <ClCompile Include="..\..\xbmc\music\Album.cpp" />
     <ClInclude Include="..\..\xbmc\input\touch\ITouchInputHandling.h" />
     <ClInclude Include="..\..\xbmc\input\touch\TouchTypes.h" />
     <ClInclude Include="..\..\xbmc\input\windows\WINJoystick.h" />
+    <ClInclude Include="..\..\xbmc\interfaces\generic\ILanguageInvocationHandler.h" />
+    <ClInclude Include="..\..\xbmc\interfaces\generic\ILanguageInvoker.h" />
+    <ClInclude Include="..\..\xbmc\interfaces\generic\LanguageInvokerThread.h" />
+    <ClInclude Include="..\..\xbmc\interfaces\generic\ScriptInvocationManager.h" />
     <ClInclude Include="..\..\xbmc\interfaces\json-rpc\FavouritesOperations.h" />
     <ClInclude Include="..\..\xbmc\interfaces\json-rpc\PVROperations.h" />
     <ClInclude Include="..\..\xbmc\interfaces\legacy\Addon.h" />
     <ClInclude Include="..\..\xbmc\interfaces\python\LanguageHook.h" />
     <ClInclude Include="..\..\xbmc\interfaces\python\preamble.h" />
     <ClInclude Include="..\..\xbmc\interfaces\python\PyContext.h" />
+    <ClInclude Include="..\..\xbmc\interfaces\python\PythonInvoker.h" />
     <ClInclude Include="..\..\xbmc\interfaces\python\pythreadstate.h" />
     <ClInclude Include="..\..\xbmc\music\karaoke\karaokevideobackground.h" />
     <ClInclude Include="..\..\xbmc\network\NetworkServices.h" />
     <ClInclude Include="..\..\xbmc\video\FFmpegVideoDecoder.h" />
     <ClInclude Include="..\..\xbmc\interfaces\python\swig.h" />
     <ClInclude Include="..\..\xbmc\interfaces\python\XBPython.h" />
-    <ClInclude Include="..\..\xbmc\interfaces\python\XBPyThread.h" />
     <ClInclude Include="..\..\xbmc\music\MusicDbUrl.h" />
     <ClInclude Include="..\..\xbmc\music\tags\MusicInfoTagLoaderWav.h" />
     <ClInclude Include="..\..\xbmc\music\tags\TagLibVFSStream.h" />
index da8e592..581e74d 100644 (file)
     <Filter Include="network\mdns">
       <UniqueIdentifier>{4deb3d70-7772-42be-8884-4a550c1ff666}</UniqueIdentifier>
     </Filter>
+    <Filter Include="interfaces\generic">
+      <UniqueIdentifier>{4286258a-45d7-45e8-9e56-ebf18fea53ec}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\xbmc\win32\pch.cpp">
     <ClCompile Include="..\..\xbmc\interfaces\python\XBPython.cpp">
       <Filter>interfaces\python</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\xbmc\interfaces\python\XBPyThread.cpp">
-      <Filter>interfaces\python</Filter>
-    </ClCompile>
     <ClCompile Include="..\..\xbmc\interfaces\python\generated\AddonModuleXbmc.cpp">
       <Filter>interfaces\python\generated</Filter>
     </ClCompile>
     <ClCompile Include="..\..\xbmc\utils\LegacyPathTranslation.cpp">
       <Filter>utils</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\xbmc\interfaces\generic\LanguageInvokerThread.cpp">
+      <Filter>interfaces\generic</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\interfaces\generic\ScriptInvocationManager.cpp">
+      <Filter>interfaces\generic</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\interfaces\python\PythonInvoker.cpp">
+      <Filter>interfaces\python</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\xbmc\win32\pch.h">
     <ClInclude Include="..\..\xbmc\interfaces\python\XBPython.h">
       <Filter>interfaces\python</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\xbmc\interfaces\python\XBPyThread.h">
-      <Filter>interfaces\python</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\xbmc\music\tags\TagLibVFSStream.h">
       <Filter>music\tags</Filter>
     </ClInclude>
     <ClInclude Include="..\..\xbmc\utils\LegacyPathTranslation.h">
       <Filter>utils</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\xbmc\interfaces\generic\ILanguageInvoker.h">
+      <Filter>interfaces\generic</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\interfaces\generic\LanguageInvokerThread.h">
+      <Filter>interfaces\generic</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\interfaces\generic\ScriptInvocationManager.h">
+      <Filter>interfaces\generic</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\interfaces\python\PythonInvoker.h">
+      <Filter>interfaces\python</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\interfaces\generic\ILanguageInvocationHandler.h">
+      <Filter>interfaces\generic</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\..\xbmc\win32\XBMC_PC.rc">
index e185fa4..d560360 100644 (file)
@@ -46,6 +46,7 @@
 #include "guilib/GUIColorManager.h"
 #include "guilib/GUITextLayout.h"
 #include "addons/Skin.h"
+#include "interfaces/generic/ScriptInvocationManager.h"
 #ifdef HAS_PYTHON
 #include "interfaces/python/XBPython.h"
 #endif
@@ -402,6 +403,7 @@ CApplication::CApplication(void)
   m_bPlaybackStarting = false;
   m_ePlayState = PLAY_STATE_NONE;
   m_skinReloading = false;
+  m_loggingIn = false;
 
 #ifdef HAS_GLX
   XInitThreads();
@@ -458,7 +460,6 @@ CApplication::~CApplication(void)
   delete m_seekHandler;
   delete m_playerController;
   delete m_pInertialScrollingHandler;
-
 }
 
 bool CApplication::OnEvent(XBMC_Event& newEvent)
@@ -777,6 +778,10 @@ bool CApplication::Create()
   // initialize the addon database (must be before the addon manager is init'd)
   CDatabaseManager::Get().Initialize(true);
 
+#ifdef HAS_PYTHON
+  CScriptInvocationManager::Get().RegisterLanguageInvocationHandler(&g_pythonParser, ".py");
+#endif // HAS_PYTHON
+
   // start-up Addons Framework
   // currently bails out if either cpluff Dll is unavailable or system dir can not be scanned
   if (!CAddonMgr::Get().Init())
@@ -1417,9 +1422,7 @@ bool CApplication::Initialize()
   if (!CProfilesManager::Get().UsingLoginScreen())
   {
     UpdateLibraries();
-#ifdef HAS_PYTHON
-    g_pythonParser.m_bLogin = true;
-#endif
+    SetLoggingIn(true);
   }
 
   m_slowTimer.StartZero();
@@ -3375,17 +3378,16 @@ void CApplication::Stop(int exitCode)
     CCrystalHD::RemoveInstance();
 #endif
 
-  g_mediaManager.Stop();
+    g_mediaManager.Stop();
 
-  // Stop services before unloading Python
-  CAddonMgr::Get().StopServices(false);
+    // Stop services before unloading Python
+    CAddonMgr::Get().StopServices(false);
+
+    // stop all remaining scripts; must be done after skin has been unloaded,
+    // not before some windows still need it when deinitializing during skin
+    // unloading
+    CScriptInvocationManager::Get().Uninitialize();
 
-/* Python resource freeing must be done after skin has been unloaded, not before
-   some windows still need it when deinitializing during skin unloading. */
-#ifdef HAS_PYTHON
-  CLog::Log(LOGNOTICE, "stop python");
-  g_pythonParser.FreeResources();
-#endif
     g_Windowing.DestroyRenderSystem();
     g_Windowing.DestroyWindow();
     g_Windowing.DestroyWindowSystem();
@@ -4890,7 +4892,7 @@ bool CApplication::ExecuteXBMCAction(std::string actionStr)
 #ifdef HAS_PYTHON
     if (item.IsPythonScript())
     { // a python script
-      g_pythonParser.evalFile(item.GetPath().c_str(),ADDON::AddonPtr());
+      CScriptInvocationManager::Get().Execute(item.GetPath());
     }
     else
 #endif
@@ -4915,10 +4917,21 @@ void CApplication::Process()
   // (this can only be done after g_windowManager.Render())
   CApplicationMessenger::Get().ProcessWindowMessages();
 
-#ifdef HAS_PYTHON
-  // process any Python scripts
-  g_pythonParser.Process();
-#endif
+  if (m_loggingIn)
+  {
+    m_loggingIn = false;
+
+    // autoexec.py - profile
+    CStdString strAutoExecPy = CSpecialProtocol::TranslatePath("special://profile/autoexec.py");
+
+    if (XFILE::CFile::Exists(strAutoExecPy))
+      CScriptInvocationManager::Get().Execute(strAutoExecPy);
+    else
+      CLog::Log(LOGDEBUG, "no profile autoexec.py (%s) found, skipping", strAutoExecPy.c_str());
+  }
+
+  // handle any active scripts
+  CScriptInvocationManager::Get().Process();
 
   // process messages, even if a movie is playing
   CApplicationMessenger::Get().ProcessMessages();
index 00e20ac..52a11f3 100644 (file)
@@ -359,6 +359,9 @@ public:
   bool SetLanguage(const CStdString &strLanguage);
 
   ReplayGainSettings& GetReplayGainSettings() { return m_replayGainSettings; }
+
+  void SetLoggingIn(bool loggingIn) { m_loggingIn = loggingIn; }
+
 protected:
   virtual bool OnSettingsSaving() const;
 
@@ -374,6 +377,8 @@ protected:
 
   bool m_skinReloading; // if true we disallow LoadSkin until ReloadSkin is called
 
+  bool m_loggingIn;
+
 #if defined(TARGET_DARWIN_IOS)
   friend class CWinEventsIOS;
 #endif
index c5788fa..bcc7f6e 100644 (file)
 #include "LangInfo.h"
 #include "PlayListPlayer.h"
 #include "Util.h"
-#ifdef HAS_PYTHON
-#include "interfaces/python/XBPython.h"
-#endif
 #include "pictures/GUIWindowSlideShow.h"
 #include "interfaces/Builtins.h"
+#include "interfaces/generic/ScriptInvocationManager.h"
 #include "network/Network.h"
 #include "utils/log.h"
 #include "utils/URIUtils.h"
@@ -565,9 +563,7 @@ void CApplicationMessenger::ProcessMessage(ThreadMessage *pMsg)
       break;
 
     case TMSG_EXECUTE_SCRIPT:
-#ifdef HAS_PYTHON
-      g_pythonParser.evalFile(pMsg->strParam.c_str(),ADDON::AddonPtr());
-#endif
+      CScriptInvocationManager::Get().Execute(pMsg->strParam);
       break;
 
     case TMSG_EXECUTE_BUILT_IN:
index 5f143bc..7a0eaba 100644 (file)
  *
  */
 #include "ScreenSaver.h"
+#include "interfaces/generic/ScriptInvocationManager.h"
 #include "settings/DisplaySettings.h"
-#include "windowing/WindowingFactory.h"
-
-#ifdef HAS_PYTHON
-#include "interfaces/python/XBPython.h"
 #include "utils/AlarmClock.h"
+#include "windowing/WindowingFactory.h"
 
 // What sound does a python screensaver make?
-#define PYTHON_ALARM "sssssscreensaver"
+#define SCRIPT_ALARM "sssssscreensaver"
 
-#define PYTHON_SCRIPT_TIMEOUT 5 // seconds
-#endif
+#define SCRIPT_TIMEOUT 5 // seconds
 
 namespace ADDON
 {
@@ -41,17 +38,15 @@ namespace ADDON
 
 bool CScreenSaver::CreateScreenSaver()
 {
-#ifdef HAS_PYTHON
-  if (URIUtils::HasExtension(LibPath(), ".py"))
+  if (CScriptInvocationManager::Get().HasLanguageInvoker(LibPath()))
   {
     // Don't allow a previously-scheduled alarm to kill our new screensaver
-    g_alarmClock.Stop(PYTHON_ALARM, true);
+    g_alarmClock.Stop(SCRIPT_ALARM, true);
 
-    if (!g_pythonParser.StopScript(LibPath()))
-      g_pythonParser.evalFile(LibPath(), AddonPtr(new CScreenSaver(Props())));
+    if (!CScriptInvocationManager::Get().Stop(LibPath()))
+      CScriptInvocationManager::Get().Execute(LibPath(), AddonPtr(new CScreenSaver(Props())));
     return true;
   }
-#endif
  // pass it the screen width,height
  // and the name of the screensaver
   int iWidth = g_graphicsContext.GetWidth();
@@ -101,7 +96,7 @@ void CScreenSaver::Destroy()
 #ifdef HAS_PYTHON
   if (URIUtils::HasExtension(LibPath(), ".py"))
   {
-    g_alarmClock.Start(PYTHON_ALARM, PYTHON_SCRIPT_TIMEOUT, "StopScript(" + LibPath() + ")", true, false);
+    g_alarmClock.Start(SCRIPT_ALARM, SCRIPT_TIMEOUT, "StopScript(" + LibPath() + ")", true, false);
     return;
   }
 #endif
index 22715d8..a075205 100644 (file)
  */
 #include "Service.h"
 #include "AddonManager.h"
+#include "interfaces/generic/ScriptInvocationManager.h"
 #include "utils/log.h"
-#ifdef HAS_PYTHON
-#include "interfaces/python/XBPython.h"
-#endif
 
 using namespace std;
 
@@ -53,7 +51,7 @@ bool CService::Start()
   {
 #ifdef HAS_PYTHON
   case PYTHON:
-    ret = (g_pythonParser.evalFile(LibPath(), this->shared_from_this()) != -1);
+    ret = (CScriptInvocationManager::Get().Execute(LibPath(), this->shared_from_this()) != -1);
     break;
 #endif
 
@@ -74,7 +72,7 @@ bool CService::Stop()
   {
 #ifdef HAS_PYTHON
   case PYTHON:
-    ret = g_pythonParser.StopScript(LibPath());
+    ret = CScriptInvocationManager::Get().Stop(LibPath());
     break;
 #endif
 
index 0442df0..c2614bc 100644 (file)
@@ -26,9 +26,7 @@
 #include "addons/AddonManager.h"
 #include "addons/AddonInstaller.h"
 #include "addons/IAddon.h"
-#ifdef HAS_PYTHON
-#include "interfaces/python/XBPython.h"
-#endif
+#include "interfaces/generic/ScriptInvocationManager.h"
 #include "threads/SingleLock.h"
 #include "guilib/GUIWindowManager.h"
 #include "dialogs/GUIDialogProgress.h"
@@ -122,7 +120,7 @@ bool CPluginDirectory::StartScript(const CStdString& strPath, bool retrievingDir
   // setup our parameters to send the script
   CStdString strHandle;
   strHandle.Format("%i", handle);
-  vector<CStdString> argv;
+  vector<string> argv;
   argv.push_back(basePath);
   argv.push_back(strHandle);
   argv.push_back(options);
@@ -130,16 +128,14 @@ bool CPluginDirectory::StartScript(const CStdString& strPath, bool retrievingDir
   // run the script
   CLog::Log(LOGDEBUG, "%s - calling plugin %s('%s','%s','%s')", __FUNCTION__, m_addon->Name().c_str(), argv[0].c_str(), argv[1].c_str(), argv[2].c_str());
   bool success = false;
-#ifdef HAS_PYTHON
   CStdString file = m_addon->LibPath();
-  int id = g_pythonParser.evalFile(file, argv,m_addon);
+  int id = CScriptInvocationManager::Get().Execute(file, m_addon, argv);
   if (id >= 0)
   { // wait for our script to finish
     CStdString scriptName = m_addon->Name();
     success = WaitOnScriptResult(file, id, scriptName, retrievingDir);
   }
   else
-#endif
     CLog::Log(LOGERROR, "Unable to run plugin %s", m_addon->Name().c_str());
 
   // free our handle
@@ -470,18 +466,16 @@ bool CPluginDirectory::RunScriptWithParams(const CStdString& strPath)
   // setup our parameters to send the script
   CStdString strHandle;
   strHandle.Format("%i", -1);
-  vector<CStdString> argv;
+  vector<string> argv;
   argv.push_back(basePath);
   argv.push_back(strHandle);
   argv.push_back(options);
 
   // run the script
-#ifdef HAS_PYTHON
   CLog::Log(LOGDEBUG, "%s - calling plugin %s('%s','%s','%s')", __FUNCTION__, addon->Name().c_str(), argv[0].c_str(), argv[1].c_str(), argv[2].c_str());
-  if (g_pythonParser.evalFile(addon->LibPath(), argv,addon) >= 0)
+  if (CScriptInvocationManager::Get().Execute(addon->LibPath(), addon, argv) >= 0)
     return true;
   else
-#endif
     CLog::Log(LOGERROR, "Unable to run plugin %s", addon->Name().c_str());
 
   return false;
@@ -510,9 +504,7 @@ bool CPluginDirectory::WaitOnScriptResult(const CStdString &scriptPath, int scri
       }
     }
     // check our script is still running
-#ifdef HAS_PYTHON
-    if (!g_pythonParser.isRunning(scriptId))
-#endif
+    if (!CScriptInvocationManager::Get().IsRunning(scriptId))
     { // check whether we exited normally
       if (!m_fetchComplete.WaitMSec(0))
       { // python didn't return correctly
@@ -565,14 +557,12 @@ bool CPluginDirectory::WaitOnScriptResult(const CStdString &scriptPath, int scri
     }
     if (cancelled && XbmcThreads::SystemClockMillis() - startTime > timeToKillScript)
     { // cancel our script
-#ifdef HAS_PYTHON
-      if (scriptId != -1 && g_pythonParser.isRunning(scriptId))
+      if (scriptId != -1 && CScriptInvocationManager::Get().IsRunning(scriptId))
       {
         CLog::Log(LOGDEBUG, "%s- cancelling plugin %s (id=%d)", __FUNCTION__, scriptName.c_str(), scriptId);
-        g_pythonParser.stopScript(scriptId);
+        CScriptInvocationManager::Get().Stop(scriptId);
         break;
       }
-#endif
     }
   }
 
index 57f83db..e753b25 100644 (file)
@@ -44,6 +44,7 @@
 #include "addons/AddonInstaller.h"
 #include "addons/AddonManager.h"
 #include "addons/PluginSource.h"
+#include "interfaces/generic/ScriptInvocationManager.h"
 #include "network/NetworkServices.h"
 #include "utils/log.h"
 #include "storage/MediaManager.h"
 
 #endif
 
-#ifdef HAS_PYTHON
-#include "interfaces/python/XBPython.h"
-#endif
-
 #if defined(TARGET_DARWIN)
 #include "filesystem/SpecialProtocol.h"
 #include "osx/CocoaInterface.h"
@@ -422,8 +419,9 @@ int CBuiltins::Execute(const CStdString& execString)
     else
 #endif
     {
-#ifdef HAS_PYTHON
-      vector<CStdString> argv = params;
+      vector<string> argv;
+      for (vector<CStdString>::const_iterator param = params.begin(); param != params.end(); ++param)
+        argv.push_back(*param);
 
       vector<CStdString> path;
       //split the path up to find the filename
@@ -436,8 +434,7 @@ int CBuiltins::Execute(const CStdString& execString)
       if (CAddonMgr::Get().GetAddon(params[0], script))
         scriptpath = script->LibPath();
 
-      g_pythonParser.evalFile(scriptpath, argv,script);
-#endif
+      CScriptInvocationManager::Get().Execute(scriptpath, script, argv);
     }
   }
 #if defined(TARGET_DARWIN_OSX)
@@ -448,7 +445,6 @@ int CBuiltins::Execute(const CStdString& execString)
 #endif
   else if (execute.Equals("stopscript"))
   {
-#ifdef HAS_PYTHON
     CStdString scriptpath(params[0]);
 
     // Test to see if the param is an addon ID
@@ -456,8 +452,7 @@ int CBuiltins::Execute(const CStdString& execString)
     if (CAddonMgr::Get().GetAddon(params[0], script))
       scriptpath = script->LibPath();
 
-    g_pythonParser.StopScript(scriptpath);
-#endif
+    CScriptInvocationManager::Get().Stop(scriptpath);
   }
   else if (execute.Equals("system.exec"))
   {
diff --git a/xbmc/interfaces/generic/ILanguageInvocationHandler.h b/xbmc/interfaces/generic/ILanguageInvocationHandler.h
new file mode 100644 (file)
index 0000000..df2424f
--- /dev/null
@@ -0,0 +1,38 @@
+#pragma once
+/*
+ *      Copyright (C) 2013 Team XBMC
+ *      http://www.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/>.
+ *
+ */
+
+class ILanguageInvoker;
+
+class ILanguageInvocationHandler
+{
+public:
+  ILanguageInvocationHandler() { }
+  virtual ~ILanguageInvocationHandler() { }
+
+  virtual bool Initialize() { return true; }
+  virtual void Process() { }
+  virtual void Uninitialize() { }
+
+  virtual void OnScriptStarted(ILanguageInvoker *invoker) { }
+  virtual void OnScriptEnded(ILanguageInvoker *invoker) { }
+
+  virtual ILanguageInvoker* CreateInvoker() = 0;
+};
diff --git a/xbmc/interfaces/generic/ILanguageInvoker.h b/xbmc/interfaces/generic/ILanguageInvoker.h
new file mode 100644 (file)
index 0000000..b8d442b
--- /dev/null
@@ -0,0 +1,96 @@
+#pragma once
+/*
+ *      Copyright (C) 2013 Team XBMC
+ *      http://www.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/>.
+ *
+ */
+
+#include <string>
+#include <vector>
+
+#include "ILanguageInvocationHandler.h"
+#include "addons/IAddon.h"
+
+class CLanguageInvokerThread;
+
+typedef enum {
+  InvokerStateUninitialized,
+  InvokerStateInitialized,
+  InvokerStateRunning,
+  InvokerStateStopping,
+  InvokerStateDone,
+  InvokerStateFailed
+} InvokerState;
+
+class ILanguageInvoker
+{
+public:
+  ILanguageInvoker(ILanguageInvocationHandler *invocationHandler)
+    : m_id(-1), m_state(InvokerStateUninitialized),
+      m_invocationHandler(invocationHandler)
+  { }
+  virtual ~ILanguageInvoker() { }
+
+  virtual bool Execute(const std::string &script, const std::vector<std::string> &arguments = std::vector<std::string>())
+  {
+    if (m_invocationHandler)
+      m_invocationHandler->OnScriptStarted(this);
+
+    return execute(script, arguments);
+  }
+  virtual bool Stop(bool abort = false) { return stop(abort); }
+
+  void SetId(int id) { m_id = id; }
+  int GetId() const { return m_id; }
+  void SetAddon(const ADDON::AddonPtr &addon) { m_addon = addon; }
+  InvokerState GetState() const { return m_state; }
+  bool IsActive() const { return GetState() > InvokerStateUninitialized && GetState() < InvokerStateDone; }
+  bool IsRunning() const { return GetState() == InvokerStateRunning; }
+  virtual bool IsStopping() const { return GetState() == InvokerStateStopping; }
+
+protected:
+  friend class CLanguageInvokerThread;
+
+  virtual bool execute(const std::string &script, const std::vector<std::string> &arguments) = 0;
+  virtual bool stop(bool abort) = 0;
+
+  virtual void onError()
+  {
+    if (m_invocationHandler)
+      m_invocationHandler->OnScriptEnded(this);
+  }
+  virtual void onDone()
+  {
+    if (m_invocationHandler)
+      m_invocationHandler->OnScriptEnded(this);
+  }
+
+  void setState(InvokerState state)
+  {
+    if (state <= m_state)
+      return;
+
+    m_state = state;
+  }
+
+  ADDON::AddonPtr m_addon;
+
+private:
+  int m_id;
+  InvokerState m_state;
+  ILanguageInvocationHandler *m_invocationHandler;
+};
diff --git a/xbmc/interfaces/generic/LanguageInvokerThread.cpp b/xbmc/interfaces/generic/LanguageInvokerThread.cpp
new file mode 100644 (file)
index 0000000..e4e76e2
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ *      Copyright (C) 2013 Team XBMC
+ *      http://www.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/>.
+ *
+ */
+
+#include "LanguageInvokerThread.h"
+#include "ScriptInvocationManager.h"
+
+CLanguageInvokerThread::CLanguageInvokerThread(ILanguageInvoker *invoker, CScriptInvocationManager *invocationManager)
+  : ILanguageInvoker(NULL),
+    CThread("LanguageInvoker"),
+    m_invoker(invoker),
+    m_invocationManager(invocationManager)
+{ }
+
+CLanguageInvokerThread::~CLanguageInvokerThread()
+{
+  Stop(true);
+  delete m_invoker;
+}
+
+InvokerState CLanguageInvokerThread::GetState()
+{
+  if (m_invoker == NULL)
+    return InvokerStateFailed;
+
+  return m_invoker->GetState();
+}
+
+bool CLanguageInvokerThread::execute(const std::string &script, const std::vector<std::string> &arguments)
+{
+  if (m_invoker == NULL || script.empty())
+    return false;
+
+  m_script = script;
+  m_args = arguments;
+
+  Create();
+  return true;
+}
+
+bool CLanguageInvokerThread::stop(bool wait)
+{
+  if (m_invoker == NULL)
+    return false;
+
+  if (!CThread::IsRunning())
+    return false;
+
+  bool result = true;
+  if (m_invoker->GetState() < InvokerStateDone)
+  {
+    // stop the language-specific invoker
+    result = m_invoker->Stop(wait);
+    // stop the thread
+    CThread::StopThread(wait);
+  }
+
+  return result;
+}
+
+void CLanguageInvokerThread::OnStartup()
+{
+  if (m_invoker == NULL)
+    return;
+
+  m_invoker->SetId(GetId());
+  if (m_addon != NULL)
+    m_invoker->SetAddon(m_addon);
+}
+
+void CLanguageInvokerThread::Process()
+{
+  if (m_invoker == NULL)
+    return;
+
+  m_invoker->Execute(m_script, m_args);
+}
+
+void CLanguageInvokerThread::OnExit()
+{
+  if (m_invoker == NULL)
+    return;
+
+  m_invoker->onDone();
+  m_invocationManager->OnScriptEnded(GetId());
+}
+
+void CLanguageInvokerThread::OnException()
+{
+  if (m_invoker == NULL)
+    return;
+
+  m_invoker->onError();
+  m_invocationManager->OnScriptEnded(GetId());
+}
\ No newline at end of file
diff --git a/xbmc/interfaces/generic/LanguageInvokerThread.h b/xbmc/interfaces/generic/LanguageInvokerThread.h
new file mode 100644 (file)
index 0000000..6f8077a
--- /dev/null
@@ -0,0 +1,49 @@
+#pragma once
+/*
+ *      Copyright (C) 2013 Team XBMC
+ *      http://www.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/>.
+ *
+ */
+
+#include "ILanguageInvoker.h"
+#include "threads/Thread.h"
+
+class CScriptInvocationManager;
+
+class CLanguageInvokerThread : public ILanguageInvoker, protected CThread
+{
+public:
+  CLanguageInvokerThread(ILanguageInvoker *invoker, CScriptInvocationManager *invocationManager);
+  ~CLanguageInvokerThread();
+
+  virtual InvokerState GetState();
+
+protected:
+  virtual bool execute(const std::string &script, const std::vector<std::string> &arguments);
+  virtual bool stop(bool wait);
+
+  virtual void OnStartup();
+  virtual void Process();
+  virtual void OnExit();
+  virtual void OnException();
+
+private:
+  ILanguageInvoker *m_invoker;
+  CScriptInvocationManager *m_invocationManager;
+  std::string m_script;
+  std::vector<std::string> m_args;
+};
\ No newline at end of file
diff --git a/xbmc/interfaces/generic/Makefile b/xbmc/interfaces/generic/Makefile
new file mode 100644 (file)
index 0000000..b0f80bb
--- /dev/null
@@ -0,0 +1,7 @@
+SRCS=LanguageInvokerThread.cpp \
+     ScriptInvocationManager.cpp \
+
+LIB=interfaces-generic.a
+
+include ../../../Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
diff --git a/xbmc/interfaces/generic/ScriptInvocationManager.cpp b/xbmc/interfaces/generic/ScriptInvocationManager.cpp
new file mode 100644 (file)
index 0000000..740a597
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ *      Copyright (C) 2013 Team XBMC
+ *      http://www.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/>.
+ *
+ */
+
+#include <vector>
+
+#include "ScriptInvocationManager.h"
+#include "ILanguageInvocationHandler.h"
+#include "ILanguageInvoker.h"
+#include "LanguageInvokerThread.h"
+#include "filesystem/File.h"
+#include "threads/SingleLock.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+
+using namespace std;
+using namespace XFILE;
+
+CScriptInvocationManager::CScriptInvocationManager()
+  : m_nextId(0)
+{ }
+
+CScriptInvocationManager::~CScriptInvocationManager()
+{
+  Uninitialize();
+}
+
+CScriptInvocationManager& CScriptInvocationManager::Get()
+{
+  static CScriptInvocationManager s_instance;
+  return s_instance;
+}
+
+void CScriptInvocationManager::Process()
+{
+  CSingleLock lock(m_critSection);
+  // go through all active threads and find and remove all which are done
+  vector<LanguageInvokerThread> tempList;
+  for (LanguageInvokerThreadMap::iterator it = m_scripts.begin(); it != m_scripts.end(); )
+  {
+    if (it->second.done)
+    {
+      tempList.push_back(it->second);
+      m_scripts.erase(it++);
+    }
+    else
+      ++it;
+  }
+
+  // remove the finished scripts from the script path map as well
+  for (vector<LanguageInvokerThread>::const_iterator it = tempList.begin(); it != tempList.end(); ++it)
+    m_scriptPaths.erase(it->script);
+
+  // we can leave the lock now
+  lock.Leave();
+
+  // finally remove the finished threads but we do it outside of any locks in
+  // case of any callbacks from the destruction of the CLanguageInvokerThread
+  tempList.clear();
+
+  // let the invocation handlers do their processing
+  for (LanguageInvocationHandlerMap::iterator it = m_invocationHandlers.begin(); it != m_invocationHandlers.end(); ++it)
+    it->second->Process();
+}
+
+void CScriptInvocationManager::Uninitialize()
+{
+  CSingleLock lock(m_critSection);
+
+  // execute Process() once more to handle the remaining scripts
+  Process();
+
+  // make sure all scripts are done
+  vector<LanguageInvokerThread> tempList;
+  for (LanguageInvokerThreadMap::iterator script = m_scripts.begin(); script != m_scripts.end(); ++script)
+    tempList.push_back(script->second);
+
+  m_scripts.clear();
+  m_scriptPaths.clear();
+
+  // we can leave the lock now
+  lock.Leave();
+
+  // finally stop and remove the finished threads but we do it outside of any
+  // locks in case of any callbacks from the stop or destruction logic of
+  // CLanguageInvokerThread or the ILanguageInvoker implementation
+  for (vector<LanguageInvokerThread>::iterator it = tempList.begin(); it != tempList.end(); ++it)
+  {
+    if (!it->done)
+      it->thread->Stop(true);
+  }
+  tempList.clear();
+
+  lock.Enter();
+  // uninitialize all invocation handlers and then remove them
+  for (LanguageInvocationHandlerMap::iterator it = m_invocationHandlers.begin(); it != m_invocationHandlers.end(); ++it)
+    it->second->Uninitialize();
+
+  m_invocationHandlers.clear();
+}
+
+void CScriptInvocationManager::RegisterLanguageInvocationHandler(ILanguageInvocationHandler *invocationHandler, const std::string &extension)
+{
+  if (invocationHandler == NULL || extension.empty())
+    return;
+
+  string ext = extension;
+  StringUtils::ToLower(ext);
+  if (!StringUtils::StartsWith(ext, "."))
+    ext = "." + ext;
+
+  CSingleLock lock(m_critSection);
+  if (m_invocationHandlers.find(ext) != m_invocationHandlers.end())
+    return;
+
+  m_invocationHandlers.insert(make_pair(extension, invocationHandler));
+
+  bool known = false;
+  for (std::map<std::string, ILanguageInvocationHandler*>::const_iterator it = m_invocationHandlers.begin(); it != m_invocationHandlers.end(); ++it)
+  {
+    if (it->second == invocationHandler)
+    {
+      known = true;
+      break;
+    }
+  }
+
+  // automatically initialize the invocation handler if it's a new one
+  if (!known)
+    invocationHandler->Initialize();
+}
+
+void CScriptInvocationManager::RegisterLanguageInvocationHandler(ILanguageInvocationHandler *invocationHandler, const std::set<std::string> &extensions)
+{
+  if (invocationHandler == NULL || extensions.empty())
+    return;
+
+  for (set<string>::const_iterator extension = extensions.begin(); extension != extensions.end(); ++extension)
+    RegisterLanguageInvocationHandler(invocationHandler, *extension);
+}
+
+void CScriptInvocationManager::UnregisterLanguageInvocationHandler(ILanguageInvocationHandler *invocationHandler)
+{
+  if (invocationHandler == NULL)
+    return;
+
+  CSingleLock lock(m_critSection);
+  //  get all extensions of the given language invoker
+  for (map<string, ILanguageInvocationHandler*>::iterator it = m_invocationHandlers.begin(); it != m_invocationHandlers.end(); )
+  {
+    if (it->second == invocationHandler)
+      m_invocationHandlers.erase(it++);
+    else
+      ++it;
+  }
+
+  // automatically uninitialize the invocation handler
+  invocationHandler->Uninitialize();
+}
+
+bool CScriptInvocationManager::HasLanguageInvoker(const std::string &script) const
+{
+  std::string extension = URIUtils::GetExtension(script);
+  StringUtils::ToLower(extension);
+
+  CSingleLock lock(m_critSection);
+  map<string, ILanguageInvocationHandler*>::const_iterator it = m_invocationHandlers.find(extension);
+  return it != m_invocationHandlers.end() && it->second != NULL;
+}
+
+ILanguageInvoker* CScriptInvocationManager::GetLanguageInvoker(const std::string &script) const
+{
+  std::string extension = URIUtils::GetExtension(script);
+  StringUtils::ToLower(extension);
+
+  CSingleLock lock(m_critSection);
+  map<string, ILanguageInvocationHandler*>::const_iterator it = m_invocationHandlers.find(extension);
+  if (it != m_invocationHandlers.end() && it->second != NULL)
+    return it->second->CreateInvoker();
+
+  return NULL;
+}
+
+int CScriptInvocationManager::Execute(const std::string &script, const ADDON::AddonPtr &addon /* = ADDON::AddonPtr() */, const std::vector<std::string> &arguments /* = std::vector<std::string>() */)
+{
+  if (script.empty() || !CFile::Exists(script, false))
+    return -1;
+
+  ILanguageInvoker *invoker = GetLanguageInvoker(script);
+  if (invoker == NULL)
+    return -1;
+
+  CLanguageInvokerThreadPtr invokerThread = CLanguageInvokerThreadPtr(new CLanguageInvokerThread(invoker, this));
+  if (invokerThread == NULL)
+    return -1;
+
+  if (addon != NULL)
+    invokerThread->SetAddon(addon);
+
+  CSingleLock lock(m_critSection);
+  invokerThread->SetId(m_nextId++);
+  lock.Leave();
+
+  LanguageInvokerThread thread = { invokerThread, script, false };
+  m_scripts.insert(make_pair(invokerThread->GetId(), thread));
+  m_scriptPaths.insert(make_pair(script, invokerThread->GetId()));
+  invokerThread->Execute(script, arguments);
+
+  return invokerThread->GetId();
+}
+
+bool CScriptInvocationManager::Stop(int scriptId, bool wait /* = false */)
+{
+  if (scriptId < 0)
+    return false;
+
+  CSingleLock lock(m_critSection);
+  CLanguageInvokerThreadPtr invokerThread = getInvokerThread(scriptId).thread;
+  if (invokerThread == NULL)
+    return false;
+
+  return invokerThread->Stop(wait);
+}
+
+bool CScriptInvocationManager::Stop(const std::string &scriptPath, bool wait /* = false */)
+{
+  if (scriptPath.empty())
+    return false;
+
+  CSingleLock lock(m_critSection);
+  std::map<std::string, int>::const_iterator script = m_scriptPaths.find(scriptPath);
+  if (script == m_scriptPaths.end())
+    return false;
+
+  return Stop(script->second, wait);
+}
+
+bool CScriptInvocationManager::IsRunning(int scriptId) const
+{
+  CSingleLock lock(m_critSection);
+  LanguageInvokerThread invokerThread = getInvokerThread(scriptId);
+  if (invokerThread.thread == NULL)
+    return false;
+
+  return !invokerThread.done;
+}
+
+void CScriptInvocationManager::OnScriptEnded(int scriptId)
+{
+  if (scriptId < 0)
+    return;
+
+  CSingleLock lock(m_critSection);
+  LanguageInvokerThreadMap::iterator script = m_scripts.find(scriptId);
+  if (script != m_scripts.end())
+    script->second.done = true;
+}
+
+CScriptInvocationManager::LanguageInvokerThread CScriptInvocationManager::getInvokerThread(int scriptId) const
+{
+  if (scriptId < 0)
+    return LanguageInvokerThread();
+
+  LanguageInvokerThreadMap::const_iterator script = m_scripts.find(scriptId);
+  if (script == m_scripts.end())
+    return LanguageInvokerThread();
+
+  return script->second;
+}
diff --git a/xbmc/interfaces/generic/ScriptInvocationManager.h b/xbmc/interfaces/generic/ScriptInvocationManager.h
new file mode 100644 (file)
index 0000000..ba18a07
--- /dev/null
@@ -0,0 +1,80 @@
+#pragma once
+/*
+ *      Copyright (C) 2013 Team XBMC
+ *      http://www.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/>.
+ *
+ */
+
+#include <map>
+#include <set>
+#include <boost/shared_ptr.hpp>
+
+#include "addons/IAddon.h"
+#include "threads/CriticalSection.h"
+
+class ILanguageInvocationHandler;
+class ILanguageInvoker;
+class CLanguageInvokerThread;
+typedef boost::shared_ptr<CLanguageInvokerThread> CLanguageInvokerThreadPtr;
+
+class CScriptInvocationManager
+{
+public:
+  static CScriptInvocationManager& Get();
+
+  void Process();
+  void Uninitialize();
+
+  void RegisterLanguageInvocationHandler(ILanguageInvocationHandler *invocationHandler, const std::string &extension);
+  void RegisterLanguageInvocationHandler(ILanguageInvocationHandler *invocationHandler, const std::set<std::string> &extensions);
+  void UnregisterLanguageInvocationHandler(ILanguageInvocationHandler *invocationHandler);
+  bool HasLanguageInvoker(const std::string &script) const;
+  ILanguageInvoker* GetLanguageInvoker(const std::string &script) const;
+
+  int Execute(const std::string &script, const ADDON::AddonPtr &addon = ADDON::AddonPtr(), const std::vector<std::string> &arguments = std::vector<std::string>());
+  bool Stop(int scriptId, bool wait = false);
+  bool Stop(const std::string &scriptPath, bool wait = false);
+
+  bool IsRunning(int scriptId) const;
+
+protected:
+  friend class CLanguageInvokerThread;
+
+  void OnScriptEnded(int scriptId);
+
+private:
+  CScriptInvocationManager();
+  CScriptInvocationManager(const CScriptInvocationManager&);
+  CScriptInvocationManager const& operator=(CScriptInvocationManager const&);
+  virtual ~CScriptInvocationManager();
+
+  typedef struct {
+    CLanguageInvokerThreadPtr thread;
+    std::string script;
+    bool done;
+  } LanguageInvokerThread;
+  typedef std::map<int, LanguageInvokerThread> LanguageInvokerThreadMap;
+  typedef std::map<std::string, ILanguageInvocationHandler*> LanguageInvocationHandlerMap;
+
+  LanguageInvokerThread getInvokerThread(int scriptId) const;
+
+  LanguageInvocationHandlerMap m_invocationHandlers;
+  LanguageInvokerThreadMap m_scripts;
+  std::map<std::string, int> m_scriptPaths;
+  int m_nextId;
+  CCriticalSection m_critSection;
+};
\ No newline at end of file
index 95838c8..509e8a2 100644 (file)
@@ -12,7 +12,7 @@ all: $(LIB)
 include ../../../codegenerator.mk
 
 SRCS=  CallbackHandler.cpp LanguageHook.cpp \
-       XBPyThread.cpp XBPython.cpp swig.cpp PyContext.cpp \
+       PythonInvoker.cpp XBPython.cpp swig.cpp PyContext.cpp \
        $(GENERATED)
 
 INCLUDES += @PYTHON_CPPFLAGS@
diff --git a/xbmc/interfaces/python/PythonInvoker.cpp b/xbmc/interfaces/python/PythonInvoker.cpp
new file mode 100644 (file)
index 0000000..2b3e1ab
--- /dev/null
@@ -0,0 +1,538 @@
+/*
+ *      Copyright (C) 2013 Team XBMC
+ *      http://www.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/>.
+ *
+ */
+
+#if (defined HAVE_CONFIG_H) && (!defined TARGET_WINDOWS)
+  #include "config.h"
+#endif
+
+// python.h should always be included first before any other includes
+#include <Python.h>
+#include <osdefs.h>
+
+#include "system.h"
+#include "PythonInvoker.h"
+#include "Application.h"
+#include "ApplicationMessenger.h"
+#include "addons/AddonManager.h"
+#include "dialogs/GUIDialogKaiToast.h"
+#include "filesystem/File.h"
+#include "filesystem/SpecialProtocol.h"
+#include "guilib/GraphicContext.h"
+#include "guilib/GUIWindowManager.h"
+#include "interfaces/legacy/Addon.h"
+#include "interfaces/python/LanguageHook.h"
+#include "interfaces/python/PyContext.h"
+#include "interfaces/python/pythreadstate.h"
+#include "interfaces/python/swig.h"
+#include "interfaces/python/XBPython.h"
+#include "threads/SingleLock.h"
+#if defined(TARGET_WINDOWS)
+#include "utils/CharsetConverter.h"
+#endif // defined(TARGET_WINDOWS)
+#include "utils/log.h"
+#include "utils/URIUtils.h"
+
+#ifdef TARGET_WINDOWS
+extern "C" FILE *fopen_utf8(const char *_Filename, const char *_Mode);
+#else
+#define fopen_utf8 fopen
+#endif
+
+#define GC_SCRIPT \
+  "import gc\n" \
+  "gc.collect(2)\n"
+
+#define PY_PATH_SEP DELIM
+
+// Time before ill-behaved scripts are terminated
+#define PYTHON_SCRIPT_TIMEOUT 5000 // ms
+
+using namespace std;
+using namespace XFILE;
+
+extern "C"
+{
+  int xbp_chdir(const char *dirname);
+  char* dll_getenv(const char* szKey);
+}
+
+static const CStdString getListOfAddonClassesAsString(XBMCAddon::AddonClass::Ref<XBMCAddon::Python::LanguageHook>& languageHook)
+{
+  CStdString message;
+  XBMCAddon::AddonClass::Synchronize l(*(languageHook.get()));
+  std::set<XBMCAddon::AddonClass*>& acs = languageHook->GetRegisteredAddonClasses();
+  bool firstTime = true;
+  for (std::set<XBMCAddon::AddonClass*>::iterator iter = acs.begin(); iter != acs.end(); ++iter)
+  {
+    if (!firstTime)
+      message += ",";
+    else
+      firstTime = false;
+    message += (*iter)->GetClassname().c_str();
+  }
+
+  return message;
+}
+
+CPythonInvoker::CPythonInvoker(ILanguageInvocationHandler *invocationHandler)
+  : ILanguageInvoker(invocationHandler),
+    m_source(NULL), m_argc(0), m_argv(NULL),
+    m_threadState(NULL), m_stop(false)
+{ }
+
+CPythonInvoker::~CPythonInvoker()
+{
+  // nothing to do for the default invoker used for registration with the
+  // CScriptInvocationManager
+  if (GetId() < 0)
+    return;
+
+  if (GetState() < InvokerStateDone)
+    CLog::Log(LOGDEBUG, "CPythonInvoker(%d): waiting for python thread \"%s\" to stop",
+      GetId(), (m_source != NULL ? m_source : "unknown script"));
+  Stop(true);
+  g_pythonParser.PulseGlobalEvent();
+
+  delete [] m_source;
+  if (m_argv != NULL)
+  {
+    for (unsigned int i = 0; i < m_argc; i++)
+      delete [] m_argv[i];
+    delete [] m_argv;
+  }
+  g_pythonParser.FinalizeScript();
+}
+
+bool CPythonInvoker::Execute(const std::string &script, const std::vector<std::string> &arguments /* = std::vector<std::string>() */)
+{
+  if (script.empty())
+    return false;
+
+  if (!CFile::Exists(script))
+  {
+    CLog::Log(LOGERROR, "CPythonInvoker(%d): python script \"%s\" does not exist", GetId(), CSpecialProtocol::TranslatePath(script).c_str());
+    return false;
+  }
+
+  if (!g_pythonParser.InitializeEngine())
+    return false;
+
+  return ILanguageInvoker::Execute(script, arguments);
+}
+
+bool CPythonInvoker::execute(const std::string &script, const std::vector<std::string> &arguments)
+{
+  // copy the code/script into a local string buffer
+#ifdef TARGET_WINDOWS
+  CStdString strsrc = script;
+  g_charsetConverter.utf8ToSystem(strsrc);
+  m_source = new char[strsrc.length() + 1];
+  strcpy(m_source, strsrc);
+#else
+  m_source = new char[script.length() + 1];
+  strcpy(m_source, script.c_str());
+#endif
+
+  // copy the arguments into a local buffer
+  m_argc = arguments.size();
+  m_argv = new char*[m_argc];
+  for (unsigned int i = 0; i < m_argc; i++)
+  {
+    m_argv[i] = new char[arguments.at(i).length() + 1];
+    strcpy(m_argv[i], arguments.at(i).c_str());
+  }
+
+  CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): start processing", GetId(), m_source);
+  int m_Py_file_input = Py_file_input;
+
+  // get the global lock
+  PyEval_AcquireLock();
+  PyThreadState* state = Py_NewInterpreter();
+  if (state == NULL)
+  {
+    PyEval_ReleaseLock();
+    CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): FAILED to get thread state!", GetId(), m_source);
+    return false;
+  }
+  // swap in my thread state
+  PyThreadState_Swap(state);
+
+  XBMCAddon::AddonClass::Ref<XBMCAddon::Python::LanguageHook> languageHook(new XBMCAddon::Python::LanguageHook(state->interp));
+  languageHook->RegisterMe();
+
+  g_pythonParser.InitializeInterpreter(m_addon);
+  setState(InvokerStateInitialized);
+
+  CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): the source file to load is %s", GetId(), m_source, m_source);
+
+  // get path from script file name and add python path's
+  // this is used for python so it will search modules from script path first
+  CStdString scriptDir;
+  URIUtils::GetDirectory(CSpecialProtocol::TranslatePath(m_source), scriptDir);
+  URIUtils::RemoveSlashAtEnd(scriptDir);
+  addPath(scriptDir);
+
+  // add on any addon modules the user has installed
+  ADDON::VECADDONS addons;
+  ADDON::CAddonMgr::Get().GetAddons(ADDON::ADDON_SCRIPT_MODULE, addons);
+  for (unsigned int i = 0; i < addons.size(); ++i)
+    addPath(CSpecialProtocol::TranslatePath(addons[i]->LibPath()));
+
+  // we want to use sys.path so it includes site-packages
+  // if this fails, default to using Py_GetPath
+  PyObject *sysMod(PyImport_ImportModule((char*)"sys")); // must call Py_DECREF when finished
+  PyObject *sysModDict(PyModule_GetDict(sysMod)); // borrowed ref, no need to delete
+  PyObject *pathObj(PyDict_GetItemString(sysModDict, "path")); // borrowed ref, no need to delete
+
+  if (pathObj != NULL && PyList_Check(pathObj))
+  {
+    for (int i = 0; i < PyList_Size(pathObj); i++)
+    {
+      PyObject *e = PyList_GetItem(pathObj, i); // borrowed ref, no need to delete
+      if (e != NULL && PyString_Check(e))
+        addPath(PyString_AsString(e)); // returns internal data, don't delete or modify
+    }
+  }
+  else
+    addPath(Py_GetPath());
+
+  Py_DECREF(sysMod); // release ref to sysMod
+
+  // set current directory and python's path.
+  if (m_argv != NULL)
+    PySys_SetArgv(m_argc, m_argv);
+
+  CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): setting the Python path to %s", GetId(), m_source, m_pythonPath.c_str());
+  PySys_SetPath((char *)m_pythonPath.c_str());
+
+  CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): entering source directory %s", GetId(), m_source, scriptDir.c_str());
+  PyObject* module = PyImport_AddModule((char*)"__main__");
+  PyObject* moduleDict = PyModule_GetDict(module);
+
+  // when we are done initing we store thread state so we can be aborted
+  PyThreadState_Swap(NULL);
+  PyEval_ReleaseLock();
+
+  // we need to check if we was asked to abort before we had inited
+  bool stopping = false;
+  { CSingleLock lock(m_critical);
+    m_threadState = state;
+    stopping = m_stop;
+  }
+
+  PyEval_AcquireLock();
+  PyThreadState_Swap(state);
+
+  bool failed = false;
+  if (!stopping)
+  {
+    try
+    {
+      // run script from file
+      // We need to have python open the file because on Windows the DLL that python
+      //  is linked against may not be the DLL that xbmc is linked against so
+      //  passing a FILE* to python from an fopen has the potential to crash.
+      PyObject* file = PyFile_FromString((char *) CSpecialProtocol::TranslatePath(m_source).c_str(), (char*)"r");
+      FILE *fp = PyFile_AsFile(file);
+
+      if (fp != NULL)
+      {
+        PyObject *f = PyString_FromString(CSpecialProtocol::TranslatePath(m_source).c_str());
+        PyDict_SetItemString(moduleDict, "__file__", f);
+
+        if (m_addon.get() != NULL)
+        {
+          PyObject *pyaddonid = PyString_FromString(m_addon->ID().c_str());
+          PyDict_SetItemString(moduleDict, "__xbmcaddonid__", pyaddonid);
+
+          CStdString version = ADDON::GetXbmcApiVersionDependency(m_addon);
+          PyObject *pyxbmcapiversion = PyString_FromString(version.c_str());
+          PyDict_SetItemString(moduleDict, "__xbmcapiversion__", pyxbmcapiversion);
+
+          CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): instantiating addon using automatically obtained id of \"%s\" dependent on version %s of the xbmc.python api",
+            GetId(), m_source, m_addon->ID().c_str(), version.c_str());
+        }
+
+        Py_DECREF(f);
+        setState(InvokerStateRunning);
+        XBMCAddon::Python::PyContext pycontext; // this is a guard class that marks this callstack as being in a python context
+        PyRun_FileExFlags(fp, CSpecialProtocol::TranslatePath(m_source).c_str(), m_Py_file_input, moduleDict, moduleDict,1,NULL);
+      }
+      else
+        CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): %s not found!", GetId(), m_source, m_source);
+    }
+    catch (const XbmcCommons::Exception& e)
+    {
+      setState(InvokerStateFailed);
+      e.LogThrowMessage();
+      failed = true;
+    }
+    catch (...)
+    {
+      setState(InvokerStateFailed);
+      CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failure in script", GetId(), m_source);
+      failed = true;
+    }
+  }
+
+  bool systemExitThrown = false;
+  if (!failed && !PyErr_Occurred())
+  {
+    CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): script successfully run", GetId(), m_source);
+    setState(InvokerStateDone);
+  }
+  else if (PyErr_ExceptionMatches(PyExc_SystemExit))
+  {
+    systemExitThrown = true;
+    CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): script aborted", GetId(), m_source);
+    setState(InvokerStateFailed);
+  }
+  else
+  {
+    setState(InvokerStateFailed);
+
+    // if it failed with an exception we already logged the details
+    if (!failed)
+    {
+      PythonBindings::PythonToCppException e;
+      e.LogThrowMessage();
+    }
+
+    {
+      CPyThreadState releaseGil;
+      CSingleLock gc(g_graphicsContext);
+
+      CGUIDialogKaiToast *pDlgToast = (CGUIDialogKaiToast*)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST);
+      if (pDlgToast != NULL)
+      {
+        CStdString desc;
+        CStdString script;
+        if (m_addon.get() != NULL)
+          script = m_addon->Name();
+        else
+        {
+          CStdString path;
+          URIUtils::Split(m_source, path, script);
+          if (script.Equals("default.py"))
+          {
+            CStdString path2;
+            URIUtils::RemoveSlashAtEnd(path);
+            URIUtils::Split(path, path2, script);
+          }
+        }
+
+        desc.Format(g_localizeStrings.Get(2100), script);
+        pDlgToast->QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(257), desc);
+      }
+    }
+  }
+
+  // no need to do anything else because the script has already stopped
+  if (failed)
+    return true;
+
+  PyObject *m = PyImport_AddModule((char*)"xbmc");
+  if (m == NULL || PyObject_SetAttrString(m, (char*)"abortRequested", PyBool_FromLong(1)))
+    CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to set abortRequested", GetId(), m_source);
+
+  // make sure all sub threads have finished
+  for (PyThreadState* s = state->interp->tstate_head, *old = NULL; s;)
+  {
+    if (s == state)
+    {
+      s = s->next;
+      continue;
+    }
+    if (old != s)
+    {
+      CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): waiting on thread %"PRIu64, GetId(), m_source, (uint64_t)s->thread_id);
+      old = s;
+    }
+
+    CPyThreadState pyState;
+    Sleep(100);
+    pyState.Restore();
+
+    s = state->interp->tstate_head;
+  }
+
+  // pending calls must be cleared out
+  XBMCAddon::RetardedAsynchCallbackHandler::clearPendingCalls(state);
+
+  PyThreadState_Swap(NULL);
+  PyEval_ReleaseLock();
+
+  // set stopped event - this allows ::stop to run and kill remaining threads
+  // this event has to be fired without holding m_critical
+  // also the GIL (PyEval_AcquireLock) must not be held
+  // if not obeyed there is still no deadlock because ::stop waits with timeout (smart one!)
+  m_stoppedEvent.Set();
+
+  { CSingleLock lock(m_critical);
+    m_threadState = NULL;
+  }
+
+  PyEval_AcquireLock();
+  PyThreadState_Swap(state);
+
+  g_pythonParser.DeInitializeInterpreter();
+
+  // run the gc before finishing
+  //
+  // if the script exited by throwing a SystemExit excepton then going back
+  // into the interpreter causes this python bug to get hit:
+  //    http://bugs.python.org/issue10582
+  // and that causes major failures. So we are not going to go back in
+  // to run the GC if that's the case.
+  if (!m_stop && languageHook->HasRegisteredAddonClasses() && !systemExitThrown &&
+      PyRun_SimpleString(GC_SCRIPT) == -1)
+    CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to run the gc to clean up after running prior to shutting down the Interpreter", GetId(), m_source);
+
+  Py_EndInterpreter(state);
+
+  // If we still have objects left around, produce an error message detailing what's been left behind
+  if (languageHook->HasRegisteredAddonClasses())
+    CLog::Log(LOGWARNING, "CPythonInvoker(%d, %s): the python script \"%s\" has left several "
+      "classes in memory that we couldn't clean up. The classes include: %s",
+      GetId(), m_source, m_source, getListOfAddonClassesAsString(languageHook).c_str());
+
+  // unregister the language hook
+  languageHook->UnregisterMe();
+
+  PyEval_ReleaseLock();
+
+  return true;
+}
+
+bool CPythonInvoker::stop(bool abort)
+{
+  CSingleLock lock(m_critical);
+  m_stop = true;
+
+  if (!IsRunning())
+    return false;
+
+  setState(InvokerStateStopping);
+
+  if (m_threadState != NULL)
+  {
+    PyEval_AcquireLock();
+    PyThreadState* old = PyThreadState_Swap((PyThreadState*)m_threadState);
+
+    //tell xbmc.Monitor to call onAbortRequested()
+    if (m_addon != NULL)
+      g_pythonParser.OnAbortRequested(m_addon->ID());
+
+    PyObject *m;
+    m = PyImport_AddModule((char*)"xbmc");
+    if (m == NULL || PyObject_SetAttrString(m, (char*)"abortRequested", PyBool_FromLong(1)))
+      CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to set abortRequested", GetId(), m_source);
+
+    PyThreadState_Swap(old);
+    old = NULL;
+    PyEval_ReleaseLock();
+
+    XbmcThreads::EndTime timeout(PYTHON_SCRIPT_TIMEOUT);
+    while (!m_stoppedEvent.WaitMSec(15))
+    {
+      if (timeout.IsTimePast())
+      {
+        CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): script didn't stop in %d seconds - let's kill it", GetId(), m_source, PYTHON_SCRIPT_TIMEOUT / 1000);
+        break;
+      }
+
+      // We can't empty-spin in the main thread and expect scripts to be able to
+      // dismantle themselves. Python dialogs aren't normal XBMC dialogs, they rely
+      // on TMSG_GUI_PYTHON_DIALOG messages, so pump the message loop.
+      if (g_application.IsCurrentThread())
+      {
+        CSingleExit ex(g_graphicsContext);
+        CApplicationMessenger::Get().ProcessMessages();
+      }
+    }
+
+    // Useful for add-on performance metrics
+    if (!timeout.IsTimePast())
+      CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): script termination took %dms", GetId(), m_source, PYTHON_SCRIPT_TIMEOUT - timeout.MillisLeft());
+
+    // everything which didn't exit by now gets killed
+    {
+      // grabbing the PyLock while holding the m_critical is asking for a deadlock
+      CSingleExit ex2(m_critical);
+      PyEval_AcquireLock();
+    }
+
+    // Since we released the m_critical it's possible that the state is cleaned up
+    // so we need to recheck for m_threadState == NULL
+    if (m_threadState != NULL)
+    {
+      old = PyThreadState_Swap((PyThreadState*)m_threadState);
+      for (PyThreadState* state = ((PyThreadState*)m_threadState)->interp->tstate_head; state; state = state->next)
+      {
+        // Raise a SystemExit exception in python threads
+        Py_XDECREF(state->async_exc);
+        state->async_exc = PyExc_SystemExit;
+        Py_XINCREF(state->async_exc);
+      }
+
+      // If a dialog entered its doModal(), we need to wake it to see the exception
+      g_pythonParser.PulseGlobalEvent();
+    }
+
+    if (old != NULL)
+      PyThreadState_Swap(old);
+
+    lock.Leave();
+    PyEval_ReleaseLock();
+  }
+
+  return true;
+}
+
+void CPythonInvoker::onError()
+{
+  PyThreadState_Swap(NULL);
+  PyEval_ReleaseLock();
+
+  setState(InvokerStateFailed);
+  CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): abnormally terminating python thread", GetId(), m_source);
+
+  CSingleLock lock(m_critical);
+  m_threadState = NULL;
+
+  ILanguageInvoker::onError();
+}
+
+void CPythonInvoker::addPath(const std::string path)
+{
+  if (path.empty())
+    return;
+
+  if (!m_pythonPath.empty())
+    m_pythonPath += PY_PATH_SEP;
+
+#if defined(TARGET_WINDOWS)
+  CStdString tmp(path);
+  g_charsetConverter.utf8ToSystem(tmp);
+  m_pythonPath += tmp;
+#else
+  m_pythonPath += path;
+#endif // defined(TARGET_WINDOWS)
+}
diff --git a/xbmc/interfaces/python/PythonInvoker.h b/xbmc/interfaces/python/PythonInvoker.h
new file mode 100644 (file)
index 0000000..0c05a1f
--- /dev/null
@@ -0,0 +1,55 @@
+#pragma once
+/*
+ *      Copyright (C) 2013 Team XBMC
+ *      http://www.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/>.
+ *
+ */
+
+#include <string>
+
+#include "interfaces/generic/ILanguageInvoker.h"
+#include "threads/CriticalSection.h"
+#include "threads/Event.h"
+
+class CPythonInvoker : public ILanguageInvoker
+{
+public:
+  CPythonInvoker(ILanguageInvocationHandler *invocationHandler);
+  virtual ~CPythonInvoker();
+
+  virtual bool Execute(const std::string &script, const std::vector<std::string> &arguments = std::vector<std::string>());
+
+  virtual bool IsStopping() const { return m_stop || ILanguageInvoker::IsStopping(); }
+
+protected:
+  virtual bool execute(const std::string &script, const std::vector<std::string> &arguments);
+  virtual bool stop(bool abort);
+
+  virtual void onError();
+
+private:
+  void addPath(const std::string path);
+
+  char *m_source;
+  unsigned int  m_argc;
+  char **m_argv;
+  std::string m_pythonPath;
+  void *m_threadState;
+  bool m_stop;
+  CEvent m_stoppedEvent;
+  CCriticalSection m_critical;
+};
diff --git a/xbmc/interfaces/python/XBPyThread.cpp b/xbmc/interfaces/python/XBPyThread.cpp
deleted file mode 100644 (file)
index d0e354d..0000000
+++ /dev/null
@@ -1,549 +0,0 @@
-/*
- *      Copyright (C) 2005-2013 Team XBMC
- *      http://www.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, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#if (defined HAVE_CONFIG_H) && (!defined TARGET_WINDOWS)
-  #include "config.h"
-#endif
-
-// python.h should always be included first before any other includes
-#include <Python.h>
-#include <osdefs.h>
-
-#include "system.h"
-#include "filesystem/SpecialProtocol.h"
-#include "guilib/GUIWindowManager.h"
-#include "dialogs/GUIDialogKaiToast.h"
-#include "guilib/LocalizeStrings.h"
-#include "utils/log.h"
-#include "threads/SingleLock.h"
-#include "utils/URIUtils.h"
-#include "addons/AddonManager.h"
-#include "addons/Addon.h"
-#include "Application.h"
-#include "ApplicationMessenger.h"
-
-#include "XBPyThread.h"
-#include "XBPython.h"
-#include "LanguageHook.h"
-
-#include "interfaces/legacy/Exception.h"
-#include "interfaces/legacy/CallbackHandler.h"
-#include "interfaces/legacy/AddonUtils.h"
-#include "interfaces/legacy/ModuleXbmc.h"
-
-#include "interfaces/python/pythreadstate.h"
-#include "interfaces/python/swig.h"
-#include "utils/CharsetConverter.h"
-#include "PyContext.h"
-
-#ifdef TARGET_WINDOWS
-extern "C" FILE *fopen_utf8(const char *_Filename, const char *_Mode);
-#else
-#define fopen_utf8 fopen
-#endif
-
-#define PY_PATH_SEP DELIM
-
-// Time before ill-behaved scripts are terminated
-#define PYTHON_SCRIPT_TIMEOUT 5000 // ms
-
-extern "C"
-{
-  int xbp_chdir(const char *dirname);
-  char* dll_getenv(const char* szKey);
-}
-
-XBPyThread::XBPyThread(XBPython *pExecuter, int id) : CThread("XBPython")
-{
-  CLog::Log(LOGDEBUG,"new python thread created. id=%d", id);
-  m_pExecuter   = pExecuter;
-  m_threadState = NULL;
-  m_id          = id;
-  m_stopping    = false;
-  m_argv        = NULL;
-  m_source      = NULL;
-  m_argc        = 0;
-  m_type        = 0;
-}
-
-XBPyThread::~XBPyThread()
-{
-  stop();
-  g_pythonParser.PulseGlobalEvent();
-  CLog::Log(LOGDEBUG,"waiting for python thread %d (%s) to stop", m_id, (m_source ? m_source : "unknown script"));
-  StopThread();
-  CLog::Log(LOGDEBUG,"python thread %d (%s) destructed", m_id, (m_source ? m_source : "unknown script"));
-  delete [] m_source;
-  if (m_argv)
-  {
-    for (unsigned int i = 0; i < m_argc; i++)
-      delete [] m_argv[i];
-    delete [] m_argv;
-  }
-  g_pythonParser.FinalizeScript();
-}
-
-void XBPyThread::setSource(const CStdString &src)
-{
-  if (m_source) 
-    delete [] m_source;
-#ifdef TARGET_WINDOWS
-  CStdString strsrc;
-  if (m_type == 'F')
-    strsrc = CSpecialProtocol::TranslatePath(src);
-  else
-    strsrc = src;
-  g_charsetConverter.utf8ToSystem(strsrc);
-  m_source  = new char[strsrc.GetLength()+1];
-  strcpy(m_source, strsrc);
-#else
-  m_source  = new char[src.GetLength()+1];
-  strcpy(m_source, src);
-#endif
-}
-
-int XBPyThread::evalFile(const CStdString &src)
-{
-  m_type    = 'F';
-  setSource(src);
-  Create();
-  return 0;
-}
-
-int XBPyThread::evalString(const CStdString &src)
-{
-  m_type    = 'S';
-  setSource(src);
-  Create();
-  return 0;
-}
-
-int XBPyThread::setArgv(const std::vector<CStdString> &argv)
-{
-  m_argc = argv.size();
-  m_argv = new char*[m_argc];
-  for(unsigned int i = 0; i < m_argc; i++)
-  {
-    m_argv[i] = new char[argv[i].GetLength()+1];
-    strcpy(m_argv[i], argv[i].c_str());
-  }
-  return 0;
-}
-
-#define GC_SCRIPT \
-  "import gc\n" \
-  "gc.collect(2)\n"
-
-static const CStdString getListOfAddonClassesAsString(XBMCAddon::AddonClass::Ref<XBMCAddon::Python::LanguageHook>& languageHook)
-{
-  CStdString message;
-  XBMCAddon::AddonClass::Synchronize l(*(languageHook.get()));
-  std::set<XBMCAddon::AddonClass*>& acs = languageHook->GetRegisteredAddonClasses();
-  bool firstTime = true;
-  for (std::set<XBMCAddon::AddonClass*>::iterator iter = acs.begin();
-       iter != acs.end(); ++iter)
-  {
-    if (!firstTime) message += ",";
-    else firstTime = false;
-    message += (*iter)->GetClassname().c_str();
-  }
-
-  return message;
-}
-
-void XBPyThread::Process()
-{
-  CLog::Log(LOGDEBUG,"Python thread: start processing");
-
-  int m_Py_file_input = Py_file_input;
-
-  // get the global lock
-  PyEval_AcquireLock();
-  PyThreadState* state = Py_NewInterpreter();
-  if (!state)
-  {
-    PyEval_ReleaseLock();
-    CLog::Log(LOGERROR,"Python thread: FAILED to get thread state!");
-    return;
-  }
-  // swap in my thread state
-  PyThreadState_Swap(state);
-
-  XBMCAddon::AddonClass::Ref<XBMCAddon::Python::LanguageHook> languageHook(new XBMCAddon::Python::LanguageHook(state->interp));
-  languageHook->RegisterMe();
-
-  m_pExecuter->InitializeInterpreter(addon);
-
-  CLog::Log(LOGDEBUG, "%s - The source file to load is %s", __FUNCTION__, m_source);
-
-  // get path from script file name and add python path's
-  // this is used for python so it will search modules from script path first
-  CStdString scriptDir;
-  URIUtils::GetDirectory(CSpecialProtocol::TranslatePath(m_source), scriptDir);
-  URIUtils::RemoveSlashAtEnd(scriptDir);
-  CStdString path = scriptDir;
-
-  // add on any addon modules the user has installed
-  ADDON::VECADDONS addons;
-  ADDON::CAddonMgr::Get().GetAddons(ADDON::ADDON_SCRIPT_MODULE, addons);
-  for (unsigned int i = 0; i < addons.size(); ++i)
-#ifdef TARGET_WINDOWS
-  {
-    CStdString strTmp(CSpecialProtocol::TranslatePath(addons[i]->LibPath()));
-    g_charsetConverter.utf8ToSystem(strTmp);
-    path += PY_PATH_SEP + strTmp;
-  }
-#else
-    path += PY_PATH_SEP + CSpecialProtocol::TranslatePath(addons[i]->LibPath());
-#endif
-
-  // and add on whatever our default path is
-  path += PY_PATH_SEP;
-
-  // we want to use sys.path so it includes site-packages
-  // if this fails, default to using Py_GetPath
-  PyObject *sysMod(PyImport_ImportModule((char*)"sys")); // must call Py_DECREF when finished
-  PyObject *sysModDict(PyModule_GetDict(sysMod)); // borrowed ref, no need to delete
-  PyObject *pathObj(PyDict_GetItemString(sysModDict, "path")); // borrowed ref, no need to delete
-
-  if( pathObj && PyList_Check(pathObj) )
-  {
-    for( int i = 0; i < PyList_Size(pathObj); i++ )
-    {
-      PyObject *e = PyList_GetItem(pathObj, i); // borrowed ref, no need to delete
-      if( e && PyString_Check(e) )
-      {
-        path += PyString_AsString(e); // returns internal data, don't delete or modify
-        path += PY_PATH_SEP;
-      }
-    }
-  }
-  else
-  {
-    path += Py_GetPath();
-  }
-  Py_DECREF(sysMod); // release ref to sysMod
-
-  // set current directory and python's path.
-  if (m_argv != NULL)
-    PySys_SetArgv(m_argc, m_argv);
-
-  CLog::Log(LOGDEBUG, "%s - Setting the Python path to %s", __FUNCTION__, path.c_str());
-
-  PySys_SetPath((char *)path.c_str());
-
-  CLog::Log(LOGDEBUG, "%s - Entering source directory %s", __FUNCTION__, scriptDir.c_str());
-
-  PyObject* module = PyImport_AddModule((char*)"__main__");
-  PyObject* moduleDict = PyModule_GetDict(module);
-
-  // when we are done initing we store thread state so we can be aborted
-  PyThreadState_Swap(NULL);
-  PyEval_ReleaseLock();
-
-  // we need to check if we was asked to abort before we had inited
-  bool stopping = false;
-  { CSingleLock lock(m_critSec);
-    m_threadState = state;
-    stopping = m_stopping;
-  }
-
-  PyEval_AcquireLock();
-  PyThreadState_Swap(state);
-
-  bool failed = false;
-  if (!stopping)
-  {
-    try
-    {
-    if (m_type == 'F')
-    {
-      // run script from file
-      // We need to have python open the file because on Windows the DLL that python
-      //  is linked against may not be the DLL that xbmc is linked against so
-      //  passing a FILE* to python from an fopen has the potential to crash.
-      PyObject* file = PyFile_FromString((char *) CSpecialProtocol::TranslatePath(m_source).c_str(), (char*)"r");
-      FILE *fp = PyFile_AsFile(file);
-
-      if (fp)
-      {
-        PyObject *f = PyString_FromString(CSpecialProtocol::TranslatePath(m_source).c_str());
-        PyDict_SetItemString(moduleDict, "__file__", f);
-        if (addon.get() != NULL)
-        {
-          PyObject *pyaddonid = PyString_FromString(addon->ID().c_str());
-          PyDict_SetItemString(moduleDict, "__xbmcaddonid__", pyaddonid);
-
-          CStdString version = ADDON::GetXbmcApiVersionDependency(addon);
-          PyObject *pyxbmcapiversion = PyString_FromString(version.c_str());
-          PyDict_SetItemString(moduleDict, "__xbmcapiversion__", pyxbmcapiversion);
-
-          CLog::Log(LOGDEBUG,"Instantiating addon using automatically obtained id of \"%s\" dependent on version %s of the xbmc.python api",addon->ID().c_str(),version.c_str());
-        }
-        Py_DECREF(f);
-        XBMCAddon::Python::PyContext pycontext; // this is a guard class that marks this callstack as being in a python context
-        PyRun_FileExFlags(fp, CSpecialProtocol::TranslatePath(m_source).c_str(), m_Py_file_input, moduleDict, moduleDict,1,NULL);
-      }
-      else
-        CLog::Log(LOGERROR, "%s not found!", m_source);
-    }
-    else
-    {
-      //run script
-      PyRun_String(m_source, m_Py_file_input, moduleDict, moduleDict);
-    }
-    }
-    catch (const XbmcCommons::Exception& e)
-    {
-      e.LogThrowMessage();
-      failed = true;
-    }
-    catch (...)
-    {
-      CLog::Log(LOGERROR, "failure in %s", m_source);
-      failed = true;
-    }
-  }
-
-  bool systemExitThrown = false;
-  if (!failed && !PyErr_Occurred())
-    CLog::Log(LOGINFO, "Scriptresult: Success");
-  else if (PyErr_ExceptionMatches(PyExc_SystemExit))
-  {
-    systemExitThrown = true;
-    CLog::Log(LOGINFO, "Scriptresult: Aborted");
-  }
-  else
-  {
-    // if it failed with an exception we already logged the details
-    if (!failed)
-    {
-      PythonBindings::PythonToCppException e;
-      e.LogThrowMessage();
-    }
-
-    {
-      CPyThreadState releaseGil;
-      CSingleLock gc(g_graphicsContext);
-
-      CGUIDialogKaiToast *pDlgToast = (CGUIDialogKaiToast*)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST);
-      if (pDlgToast)
-      {
-        CStdString desc;
-        CStdString script;
-        if (addon.get() != NULL)
-        {
-          script = addon->Name();
-        }
-        else
-        {
-          CStdString path;
-          URIUtils::Split(m_source, path, script);
-          if (script.Equals("default.py"))
-          {
-            CStdString path2;
-            URIUtils::RemoveSlashAtEnd(path);
-            URIUtils::Split(path, path2, script);
-          }
-        }
-
-        desc.Format(g_localizeStrings.Get(2100), script);
-        pDlgToast->QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(257), desc);
-      }
-    }
-  }
-
-  // no need to do anything else because the script has already stopped
-  if (failed)
-    return;
-
-  PyObject *m = PyImport_AddModule((char*)"xbmc");
-  if(!m || PyObject_SetAttrString(m, (char*)"abortRequested", PyBool_FromLong(1)))
-    CLog::Log(LOGERROR, "Scriptresult: failed to set abortRequested");
-
-  // make sure all sub threads have finished
-  for(PyThreadState* s = state->interp->tstate_head, *old = NULL; s;)
-  {
-    if(s == state)
-    {
-      s = s->next;
-      continue;
-    }
-    if(old != s)
-    {
-      CLog::Log(LOGINFO, "Scriptresult: Waiting on thread %"PRIu64, (uint64_t)s->thread_id);
-      old = s;
-    }
-
-    CPyThreadState pyState;
-    Sleep(100);
-    pyState.Restore();
-
-    s = state->interp->tstate_head;
-  }
-
-  // pending calls must be cleared out
-  XBMCAddon::RetardedAsynchCallbackHandler::clearPendingCalls(state);
-
-  PyThreadState_Swap(NULL);
-  PyEval_ReleaseLock();
-
-  //set stopped event - this allows ::stop to run and kill remaining threads
-  //this event has to be fired without holding m_critSec
-  //
-  //Also the GIL (PyEval_AcquireLock) must not be held
-  //if not obeyed there is still no deadlock because ::stop waits with timeout (smart one!)
-  stoppedEvent.Set();
-
-  { CSingleLock lock(m_critSec);
-    m_threadState = NULL;
-  }
-
-  PyEval_AcquireLock();
-  PyThreadState_Swap(state);
-
-  m_pExecuter->DeInitializeInterpreter();
-
-  // run the gc before finishing
-  //
-  // if the script exited by throwing a SystemExit excepton then going back
-  // into the interpreter causes this python bug to get hit:
-  //    http://bugs.python.org/issue10582
-  // and that causes major failures. So we are not going to go back in
-  // to run the GC if that's the case.
-  if (!m_stopping && languageHook->HasRegisteredAddonClasses() && !systemExitThrown &&
-      PyRun_SimpleString(GC_SCRIPT) == -1)
-    CLog::Log(LOGERROR,"Failed to run the gc to clean up after running prior to shutting down the Interpreter %s",m_source);
-
-  Py_EndInterpreter(state);
-
-  // If we still have objects left around, produce an error message detailing what's been left behind
-  if (languageHook->HasRegisteredAddonClasses())
-    CLog::Log(LOGWARNING, "The python script \"%s\" has left several "
-              "classes in memory that we couldn't clean up. The classes include: %s",
-              m_source, getListOfAddonClassesAsString(languageHook).c_str());
-
-  // unregister the language hook
-  languageHook->UnregisterMe();
-
-  PyEval_ReleaseLock();
-
-}
-
-void XBPyThread::OnExit()
-{
-  m_pExecuter->setDone(m_id);
-}
-
-void XBPyThread::OnException()
-{
-  PyThreadState_Swap(NULL);
-  PyEval_ReleaseLock();
-
-  CSingleLock lock(m_critSec);
-  m_threadState = NULL;
-  CLog::Log(LOGERROR,"%s, abnormally terminating python thread", __FUNCTION__);
-  m_pExecuter->setDone(m_id);
-}
-
-bool XBPyThread::isStopping() {
-  return m_stopping;
-}
-
-void XBPyThread::stop()
-{
-  CSingleLock lock(m_critSec);
-  if(m_stopping)
-    return;
-
-  m_stopping = true;
-
-  if (m_threadState)
-  {
-    PyEval_AcquireLock();
-    PyThreadState* old = PyThreadState_Swap((PyThreadState*)m_threadState);
-
-    //tell xbmc.Monitor to call onAbortRequested()
-    if (addon)
-      g_pythonParser.OnAbortRequested(addon->ID());
-
-    PyObject *m;
-    m = PyImport_AddModule((char*)"xbmc");
-    if(!m || PyObject_SetAttrString(m, (char*)"abortRequested", PyBool_FromLong(1)))
-      CLog::Log(LOGERROR, "XBPyThread::stop - failed to set abortRequested");
-
-    PyThreadState_Swap(old);
-    old = NULL;
-    PyEval_ReleaseLock();
-
-    XbmcThreads::EndTime timeout(PYTHON_SCRIPT_TIMEOUT);
-    while (!stoppedEvent.WaitMSec(15))
-    {
-      if (timeout.IsTimePast())
-      {
-        CLog::Log(LOGERROR, "XBPyThread::stop - script %s didn't stop in %d seconds - let's kill it", m_source, PYTHON_SCRIPT_TIMEOUT / 1000);
-        break;
-      }
-      // We can't empty-spin in the main thread and expect scripts to be able to
-      // dismantle themselves. Python dialogs aren't normal XBMC dialogs, they rely
-      // on TMSG_GUI_PYTHON_DIALOG messages, so pump the message loop.
-      if (g_application.IsCurrentThread())
-      {
-        CSingleExit ex(g_graphicsContext);
-        CApplicationMessenger::Get().ProcessMessages();
-      }
-    }
-    // Useful for add-on performance metrics
-    if (!timeout.IsTimePast())
-      CLog::Log(LOGDEBUG, "XBPyThread::stop - script termination took %dms", PYTHON_SCRIPT_TIMEOUT - timeout.MillisLeft());
-    
-    //everything which didn't exit by now gets killed
-    {
-      // grabbing the PyLock while holding the m_critSec is asking for a deadlock
-      CSingleExit ex2(m_critSec);
-      PyEval_AcquireLock();
-    }
-
-    // Since we released the m_critSec it's possible that the state is cleaned up 
-    // so we need to recheck for m_threadState == NULL
-    if (m_threadState)
-    {
-      old = PyThreadState_Swap((PyThreadState*)m_threadState);    
-      for(PyThreadState* state = ((PyThreadState*)m_threadState)->interp->tstate_head; state; state = state->next)
-      {
-        // Raise a SystemExit exception in python threads
-        Py_XDECREF(state->async_exc);
-        state->async_exc = PyExc_SystemExit;
-        Py_XINCREF(state->async_exc);
-      }
-
-      // If a dialog entered its doModal(), we need to wake it to see the exception
-      g_pythonParser.PulseGlobalEvent();
-
-    }
-
-    if (old != NULL)
-      PyThreadState_Swap(old);
-
-    lock.Leave();
-    PyEval_ReleaseLock();
-  }
-}
diff --git a/xbmc/interfaces/python/XBPyThread.h b/xbmc/interfaces/python/XBPyThread.h
deleted file mode 100644 (file)
index 8b77441..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- *      Copyright (C) 2005-2013 Team XBMC
- *      http://www.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, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#ifndef XBPYTHREAD_H_
-#define XBPYTHREAD_H_
-
-#include "threads/Thread.h"
-#include "threads/Event.h"
-#include "threads/CriticalSection.h"
-#include "addons/IAddon.h"
-
-class XBPython;
-
-class XBPyThread : public CThread
-{
-public:
-  XBPyThread(XBPython *pExecuter, int id);
-  virtual ~XBPyThread();
-  int evalFile(const CStdString &src);
-  int evalString(const CStdString &src);
-  int setArgv(const std::vector<CStdString> &argv);
-  bool isStopping();
-  void stop();
-
-  void setAddon(ADDON::AddonPtr _addon) { addon = _addon; }
-
-protected:
-  CCriticalSection m_critSec;
-  XBPython *m_pExecuter;
-  CEvent stoppedEvent;
-  void *m_threadState;
-
-  char m_type;
-  char *m_source;
-  char **m_argv;
-  unsigned int  m_argc;
-  bool m_stopping;
-  int  m_id;
-  ADDON::AddonPtr addon;
-
-  void setSource(const CStdString &src);
-
-  virtual void Process();
-  virtual void OnExit();
-  virtual void OnException();
-};
-
-#endif // XBPYTHREAD_H_
index 3a95293..18eca56 100644 (file)
@@ -34,7 +34,6 @@
 #include "XBPython.h"
 #include "filesystem/File.h"
 #include "filesystem/SpecialProtocol.h"
-#include "guilib/GraphicContext.h"
 #include "profiles/ProfilesManager.h"
 #include "utils/log.h"
 #include "pythreadstate.h"
@@ -51,6 +50,7 @@
 
 #include "interfaces/legacy/Monitor.h"
 #include "interfaces/legacy/AddonUtils.h"
+#include "interfaces/python/PythonInvoker.h"
 
 using namespace ANNOUNCEMENT;
 
@@ -67,8 +67,6 @@ using namespace PythonBindings;
 XBPython::XBPython()
 {
   m_bInitialized      = false;
-  m_bLogin            = false;
-  m_nextid            = 0;
   m_mainThreadState   = NULL;
   m_ThreadId          = CThread::GetCurrentThreadId();
   m_iDllScriptCounter = 0;
@@ -96,16 +94,6 @@ XBPython::~XBPython()
 #define CHECK_FOR_ENTRY(l,v) \
   (l.hadSomethingRemoved ? (std::find(l.begin(),l.end(),v) != l.end()) : true)
 
-// message all registered callbacks that xbmc stopped playing
-void XBPython::OnPlayBackEnded()
-{
-  TRACE;
-  LOCK_AND_COPY(std::vector<PVOID>,tmp,m_vecPlayerCallbackList);
-  for (PlayerCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
-    if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList,(*it)))
-      ((IPlayerCallback*)(*it))->OnPlayBackEnded();
-}
-
 void XBPython::Announce(AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data)
 {
   if (flag & VideoLibrary)
@@ -125,7 +113,7 @@ void XBPython::Announce(AnnouncementFlag flag, const char *sender, const char *m
   else if (flag & GUI)
   {
    if (strcmp(message, "OnScreensaverDeactivated") == 0)
-     OnScreensaverDeactivated();   
+     OnScreensaverDeactivated();
    else if (strcmp(message, "OnScreensaverActivated") == 0)
      OnScreensaverActivated();
   }
@@ -137,8 +125,10 @@ void XBPython::OnPlayBackStarted()
   TRACE;
   LOCK_AND_COPY(std::vector<PVOID>,tmp,m_vecPlayerCallbackList);
   for (PlayerCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
+  {
     if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList,(*it)))
       ((IPlayerCallback*)(*it))->OnPlayBackStarted();
+  }
 }
 
 // message all registered callbacks that we paused playing
@@ -147,8 +137,10 @@ void XBPython::OnPlayBackPaused()
   TRACE;
   LOCK_AND_COPY(std::vector<PVOID>,tmp,m_vecPlayerCallbackList);
   for (PlayerCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
+  {
     if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList,(*it)))
       ((IPlayerCallback*)(*it))->OnPlayBackPaused();
+  }
 }
 
 // message all registered callbacks that we resumed playing
@@ -157,8 +149,22 @@ void XBPython::OnPlayBackResumed()
   TRACE;
   LOCK_AND_COPY(std::vector<PVOID>,tmp,m_vecPlayerCallbackList);
   for (PlayerCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
+  {
     if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList,(*it)))
       ((IPlayerCallback*)(*it))->OnPlayBackResumed();
+  }
+}
+
+// message all registered callbacks that xbmc stopped playing
+void XBPython::OnPlayBackEnded()
+{
+  TRACE;
+  LOCK_AND_COPY(std::vector<PVOID>,tmp,m_vecPlayerCallbackList);
+  for (PlayerCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
+  {
+    if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList,(*it)))
+      ((IPlayerCallback*)(*it))->OnPlayBackEnded();
+  }
 }
 
 // message all registered callbacks that user stopped playing
@@ -167,8 +173,10 @@ void XBPython::OnPlayBackStopped()
   TRACE;
   LOCK_AND_COPY(std::vector<PVOID>,tmp,m_vecPlayerCallbackList);
   for (PlayerCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
+  {
     if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList,(*it)))
       ((IPlayerCallback*)(*it))->OnPlayBackStopped();
+  }
 }
 
 // message all registered callbacks that playback speed changed (FF/RW)
@@ -177,8 +185,10 @@ void XBPython::OnPlayBackSpeedChanged(int iSpeed)
   TRACE;
   LOCK_AND_COPY(std::vector<PVOID>,tmp,m_vecPlayerCallbackList);
   for (PlayerCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
+  {
     if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList,(*it)))
       ((IPlayerCallback*)(*it))->OnPlayBackSpeedChanged(iSpeed);
+  }
 }
 
 // message all registered callbacks that player is seeking
@@ -187,8 +197,10 @@ void XBPython::OnPlayBackSeek(int iTime, int seekOffset)
   TRACE;
   LOCK_AND_COPY(std::vector<PVOID>,tmp,m_vecPlayerCallbackList);
   for (PlayerCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
+  {
     if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList,(*it)))
       ((IPlayerCallback*)(*it))->OnPlayBackSeek(iTime, seekOffset);
+  }
 }
 
 // message all registered callbacks that player chapter seeked
@@ -197,8 +209,10 @@ void XBPython::OnPlayBackSeekChapter(int iChapter)
   TRACE;
   LOCK_AND_COPY(std::vector<PVOID>,tmp,m_vecPlayerCallbackList);
   for (PlayerCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
+  {
     if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList,(*it)))
       ((IPlayerCallback*)(*it))->OnPlayBackSeekChapter(iChapter);
+  }
 }
 
 // message all registered callbacks that next item has been queued
@@ -207,8 +221,10 @@ void XBPython::OnQueueNextItem()
   TRACE;
   LOCK_AND_COPY(std::vector<PVOID>,tmp,m_vecPlayerCallbackList);
   for (PlayerCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
+  {
     if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList,(*it)))
       ((IPlayerCallback*)(*it))->OnQueueNextItem();
+  }
 }
 
 void XBPython::RegisterPythonPlayerCallBack(IPlayerCallback* pCallback)
@@ -264,44 +280,54 @@ void XBPython::OnSettingsChanged(const CStdString &ID)
   TRACE;
   LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>,tmp,m_vecMonitorCallbackList);
   for (MonitorCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
+  {
     if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList,(*it)) && ((*it)->GetId() == ID))
       (*it)->OnSettingsChanged();
-}  
+  }
+}
 
 void XBPython::OnScreensaverActivated()
 {
   TRACE;
   LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>,tmp,m_vecMonitorCallbackList);
   for (MonitorCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
+  {
     if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList,(*it)))
       (*it)->OnScreensaverActivated();
-} 
+  }
+}
 
 void XBPython::OnScreensaverDeactivated()
 {
   TRACE;
   LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>,tmp,m_vecMonitorCallbackList);
   for (MonitorCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
+  {
     if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList,(*it)))
       (*it)->OnScreensaverDeactivated();
-} 
+  }
+}
 
 void XBPython::OnDatabaseUpdated(const std::string &database)
 {
   TRACE;
   LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>,tmp,m_vecMonitorCallbackList);
   for (MonitorCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
+  {
     if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList,(*it)))
       (*it)->OnDatabaseUpdated(database);
-} 
+  }
+}
 
 void XBPython::OnDatabaseScanStarted(const std::string &database)
 {
   TRACE;
   LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>,tmp,m_vecMonitorCallbackList);
   for (MonitorCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it)
+  {
     if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList,(*it)))
       (*it)->OnDatabaseScanStarted(database);
+  }
 }
 
 void XBPython::OnAbortRequested(const CStdString &ID)
@@ -317,8 +343,8 @@ void XBPython::OnAbortRequested(const CStdString &ID)
       else if ((*it)->GetId() == ID)
         (*it)->OnAbortRequested();
     }
-  }  
-} 
+  }
+}
 
 /**
 * Check for file and print an error if needed
@@ -343,7 +369,7 @@ void XBPython::RegisterExtensionLib(LibraryLoader *pLib)
 
   CSingleLock lock(m_critSection);
 
-  CLog::Log(LOGDEBUG,"%s, adding %s (%p)", __FUNCTION__, pLib->GetName(), (void*)pLib);
+  CLog::Log(LOGDEBUG, "%s, adding %s (%p)", __FUNCTION__, pLib->GetName(), (void*)pLib);
   m_extensions.push_back(pLib);
 }
 
@@ -353,7 +379,7 @@ void XBPython::UnregisterExtensionLib(LibraryLoader *pLib)
     return;
 
   CSingleLock lock(m_critSection);
-  CLog::Log(LOGDEBUG,"%s, removing %s (0x%p)", __FUNCTION__, pLib->GetName(), (void *)pLib);
+  CLog::Log(LOGDEBUG, "%s, removing %s (0x%p)", __FUNCTION__, pLib->GetName(), (void *)pLib);
   PythonExtensionLibraries::iterator iter = m_extensions.begin();
   while (iter != m_extensions.end())
   {
@@ -368,7 +394,7 @@ void XBPython::UnregisterExtensionLib(LibraryLoader *pLib)
 
 void XBPython::UnloadExtensionLibs()
 {
-  CLog::Log(LOGDEBUG,"%s, clearing python extension libraries", __FUNCTION__);
+  CLog::Log(LOGDEBUG, "%s, clearing python extension libraries", __FUNCTION__);
   CSingleLock lock(m_critSection);
   PythonExtensionLibraries::iterator iter = m_extensions.begin();
   while (iter != m_extensions.end())
@@ -418,7 +444,7 @@ void XBPython::UnloadExtensionLibs()
         "os.chdir_orignal   = os.chdir\n" \
         "os.chdir           = chdir_xbmc\n" \
         ""
+
 #define RUNSCRIPT_POSTSCRIPT \
         "print '-->Python Interpreter Initialized<--'\n" \
         ""
@@ -447,9 +473,7 @@ void XBPython::InitializeInterpreter(ADDON::AddonPtr addon)
 
   // redirecting default output to debug console
   if (PyRun_SimpleString(runscript) == -1)
-  {
     CLog::Log(LOGFATAL, "Python Initialize Error");
-  }
 }
 
 void XBPython::DeInitializeInterpreter()
@@ -460,10 +484,10 @@ void XBPython::DeInitializeInterpreter()
 /**
 * Should be called before executing a script
 */
-void XBPython::Initialize()
+bool XBPython::InitializeEngine()
 {
   TRACE;
-  CLog::Log(LOGINFO, "initializing python engine. ");
+  CLog::Log(LOGINFO, "initializing python engine.");
   CSingleLock lock(m_critSection);
   m_iDllScriptCounter++;
   if (!m_bInitialized)
@@ -479,7 +503,7 @@ void XBPython::Initialize()
       {
         CLog::Log(LOGERROR, "Python: Missing files, unable to execute script");
         Finalize();
-        return;
+        return false;
       }
 #endif
 
@@ -550,6 +574,8 @@ void XBPython::Initialize()
 
       m_bInitialized = true;
   }
+
+  return m_bInitialized;
 }
 
 /**
@@ -567,7 +593,6 @@ void XBPython::FinalizeScript()
   m_endtime = XbmcThreads::SystemClockMillis();
 }
 
-
 // Always called with the lock held on m_critSection
 void XBPython::Finalize()
 {
@@ -607,7 +632,7 @@ void XBPython::Finalize()
   }
 }
 
-void XBPython::FreeResources()
+void XBPython::Uninitialize()
 {
   LOCK_AND_COPY(std::vector<PyElem>,tmpvec,m_vecPyList);
   m_vecPyList.clear();
@@ -621,24 +646,10 @@ void XBPython::FreeResources()
 
 void XBPython::Process()
 {
-  if (m_bLogin)
-  {
-    m_bLogin = false;
-
-    // autoexec.py - profile
-    CStdString strAutoExecPy = CSpecialProtocol::TranslatePath("special://profile/autoexec.py");
-
-    if ( XFILE::CFile::Exists(strAutoExecPy) )
-      evalFile(strAutoExecPy,ADDON::AddonPtr());
-    else
-      CLog::Log(LOGDEBUG, "%s - no profile autoexec.py (%s) found, skipping", __FUNCTION__, strAutoExecPy.c_str());
-  }
-
-  CSingleLock lock(m_vecPyList);
-
   if (m_bInitialized)
   {
     PyList tmpvec;
+    CSingleLock lock(m_vecPyList);
     for (PyList::iterator it = m_vecPyList.begin(); it != m_vecPyList.end();)
     {
       if (it->bDone)
@@ -663,74 +674,31 @@ void XBPython::Process()
   }
 }
 
-bool XBPython::StopScript(const CStdString &path)
+void XBPython::OnScriptStarted(ILanguageInvoker *invoker)
 {
-  TRACE;
-  int id = getScriptId(path);
-  if (id != -1)
-  {
-    /* if we are here we already know that this script is running.
-     * But we will check it again to be sure :)
-     */
-    if (isRunning(id))
-    {
-      stopScript(id);
-      return true;
-    }
-  }
-  return false;
-}
-
-int XBPython::evalFile(const CStdString &src, ADDON::AddonPtr addon)
-{
-  std::vector<CStdString> argv;
-  return evalFile(src, argv, addon);
-}
-// execute script, returns -1 if script doesn't exist
-int XBPython::evalFile(const CStdString &src, const std::vector<CStdString> &argv, ADDON::AddonPtr addon)
-{
-  CSingleExit ex(g_graphicsContext);
-  // 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 (CProfilesManager::Get().GetCurrentProfile().programsLocked() && !g_passwordManager.IsMasterLockUnlocked(true))
-    return -1;
-
-  CSingleLock lock(m_vecPyList);
-  Initialize();
+  if (invoker == NULL)
+    return;
 
-  if (!m_bInitialized) return -1;
+  if (!m_bInitialized)
+    return;
 
-  m_nextid++;
-  boost::shared_ptr<XBPyThread> pyThread = boost::shared_ptr<XBPyThread>(new XBPyThread(this, m_nextid));
-  pyThread->setArgv(argv);
-  pyThread->setAddon(addon);
-  pyThread->evalFile(src);
   PyElem inf;
-  inf.id        = m_nextid;
+  inf.id        = invoker->GetId();
   inf.bDone     = false;
-  inf.strFile   = src;
-  inf.pyThread  = pyThread;
-
+  inf.pyThread  = static_cast<CPythonInvoker*>(invoker);
+  CSingleLock lock(m_vecPyList);
   m_vecPyList.push_back(inf);
-
-  return m_nextid;
 }
 
-void XBPython::setDone(int id)
+void XBPython::OnScriptEnded(ILanguageInvoker *invoker)
 {
   CSingleLock lock(m_vecPyList);
   PyList::iterator it = m_vecPyList.begin();
   while (it != m_vecPyList.end())
   {
-    if (it->id == id)
+    if (it->id == invoker->GetId())
     {
-      if (it->pyThread->isStopping())
+      if (it->pyThread->IsStopping())
         CLog::Log(LOGINFO, "Python script interrupted by user");
       else
         CLog::Log(LOGINFO, "Python script stopped");
@@ -740,104 +708,9 @@ void XBPython::setDone(int id)
   }
 }
 
-void XBPython::stopScript(int id)
-{
-  CSingleExit ex(g_graphicsContext);
-  CSingleLock lock(m_vecPyList);
-  PyList::iterator it = m_vecPyList.begin();
-  while (it != m_vecPyList.end())
-  {
-    if (it->id == id) {
-      CLog::Log(LOGINFO, "Stopping script with id: %i", id);
-      it->pyThread->stop();
-      return;
-    }
-    ++it;
-  }
-}
-
-void* XBPython::getMainThreadState()
+ILanguageInvoker* XBPython::CreateInvoker()
 {
-  CSingleLock lock(m_critSection);
-  return m_mainThreadState;
-}
-
-int XBPython::ScriptsSize()
-{
-  CSingleLock lock(m_vecPyList);
-  return m_vecPyList.size();
-}
-
-const char* XBPython::getFileName(int scriptId)
-{
-  const char* cFileName = NULL;
-
-  CSingleLock lock(m_vecPyList);
-  PyList::iterator it = m_vecPyList.begin();
-  while (it != m_vecPyList.end())
-  {
-    if (it->id == scriptId)
-      cFileName = it->strFile.c_str();
-    ++it;
-  }
-
-  return cFileName;
-}
-
-int XBPython::getScriptId(const CStdString &strFile)
-{
-  int iId = -1;
-
-  CSingleLock lock(m_vecPyList);
-
-  PyList::iterator it = m_vecPyList.begin();
-  while (it != m_vecPyList.end())
-  {
-    if (it->strFile == strFile)
-      iId = it->id;
-    ++it;
-  }
-
-  return iId;
-}
-
-bool XBPython::isRunning(int scriptId)
-{
-  CSingleLock lock(m_vecPyList);
-
-  for(PyList::iterator it = m_vecPyList.begin(); it != m_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_vecPyList);
-  PyList::iterator it = m_vecPyList.begin();
-  while (it != m_vecPyList.end())
-  {
-    if (it->id == scriptId)
-      bStopping = it->pyThread->isStopping();
-    ++it;
-  }
-
-  return bStopping;
-}
-
-int XBPython::GetPythonScriptId(int scriptPosition)
-{
-  CSingleLock lock(m_vecPyList);
-  return (int)m_vecPyList[scriptPosition].id;
+  return new CPythonInvoker(this);
 }
 
 void XBPython::PulseGlobalEvent()
@@ -852,39 +725,5 @@ bool XBPython::WaitForEvent(CEvent& hEvent, unsigned int milliseconds)
   CEvent* ret = eventGroup.wait(milliseconds);
   if (ret)
     m_globalEvent.Reset();
-  return ret == NULL ? false : true;
-}
-
-// execute script, returns -1 if script doesn't exist
-int XBPython::evalString(const CStdString &src, const std::vector<CStdString> &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
-  m_nextid++;
-  boost::shared_ptr<XBPyThread> pyThread = boost::shared_ptr<XBPyThread>(new XBPyThread(this, m_nextid));
-  pyThread->setArgv(argv);
-  pyThread->evalString(src);
-
-  PyElem inf;
-  inf.id        = m_nextid;
-  inf.bDone     = false;
-  inf.strFile   = "<string>";
-  inf.pyThread  = pyThread;
-
-  lock.Leave();
-  CSingleLock l2(m_vecPyList);
-
-  m_vecPyList.push_back(inf);
-
-  return m_nextid;
+  return ret != NULL;
 }
index 3faa99f..d6d006b 100644 (file)
  *
  */
 
-#include "XBPyThread.h"
 #include "cores/IPlayerCallback.h"
 #include "threads/CriticalSection.h"
+#include "threads/Event.h"
+#include "threads/Thread.h"
 #include "interfaces/IAnnouncer.h"
+#include "interfaces/generic/ILanguageInvocationHandler.h"
 #include "addons/IAddon.h"
 
 #include <boost/shared_ptr.hpp>
 #include <vector>
 
+class CPythonInvoker;
+
 typedef struct {
   int id;
   bool bDone;
-  std::string strFile;
-  boost::shared_ptr<XBPyThread> pyThread;
+  CPythonInvoker* pyThread;
 }PyElem;
 
 class LibraryLoader;
@@ -47,7 +50,7 @@ namespace XBMCAddon
   }
 }
 
-template <class T> struct LockableType : public T, public CCriticalSection 
+template <class T> struct LockableType : public T, public CCriticalSection
 { bool hadSomethingRemoved; };
 
 typedef LockableType<std::vector<void*> > PlayerCallbackList;
@@ -55,11 +58,11 @@ typedef LockableType<std::vector<XBMCAddon::xbmc::Monitor*> > MonitorCallbackLis
 typedef LockableType<std::vector<PyElem> > PyList;
 typedef std::vector<LibraryLoader*> PythonExtensionLibraries;
 
-class XBPython : 
+class XBPython :
   public IPlayerCallback,
-  public ANNOUNCEMENT::IAnnouncer
+  public ANNOUNCEMENT::IAnnouncer,
+  public ILanguageInvocationHandler
 {
-  void Finalize();
 public:
   XBPython();
   virtual ~XBPython();
@@ -84,30 +87,19 @@ public:
   void OnDatabaseUpdated(const std::string &database);
   void OnDatabaseScanStarted(const std::string &database);
   void OnAbortRequested(const CStdString &ID="");
-  void Initialize();
+
+  virtual void Process();
+  virtual void Uninitialize();
+  virtual void OnScriptStarted(ILanguageInvoker *invoker);
+  virtual void OnScriptEnded(ILanguageInvoker *invoker);
+  virtual ILanguageInvoker* CreateInvoker();
+
+  bool InitializeEngine();
   void FinalizeScript();
-  void FreeResources();
-  void Process();
 
   void PulseGlobalEvent();
   bool WaitForEvent(CEvent& hEvent, unsigned int milliseconds);
 
-  int ScriptsSize();
-  int GetPythonScriptId(int scriptPosition);
-  int evalFile(const CStdString &src, ADDON::AddonPtr addon);
-  int evalFile(const CStdString &src, const std::vector<CStdString> &argv, ADDON::AddonPtr addon);
-  int evalString(const CStdString &src, const std::vector<CStdString> &argv);
-
-  bool isRunning(int scriptId);
-  bool isStopping(int scriptId);
-  void setDone(int id);
-  
-  /*! \brief Stop a script if it's running
-   \param path path to the script
-   \return true if the script was running and is now stopped, false otherwise
-   */
-  bool StopScript(const CStdString &path);
-
   // inject xbmc stuff into the interpreter.
   // should be called for every new interpreter
   void InitializeInterpreter(ADDON::AddonPtr addon);
@@ -119,23 +111,12 @@ public:
   void UnregisterExtensionLib(LibraryLoader *pLib);
   void UnloadExtensionLibs();
 
-  //only should be called from thread which is running the script
-  void  stopScript(int scriptId);
-
-  // returns NULL if script doesn't exist or if script doesn't have a filename
-  const char* getFileName(int scriptId);
-
-  // returns -1 if no scripts exist with specified filename
-  int getScriptId(const CStdString &strFile);
-
-  void* getMainThreadState();
-
-  bool m_bLogin;
 private:
+  void Finalize();
+
   CCriticalSection    m_critSection;
   bool              FileExist(const char* strFile);
 
-  int               m_nextid;
   void*             m_mainThreadState;
   ThreadIdentifier  m_ThreadId;
   bool              m_bInitialized;
index 0a2728f..7836dec 100644 (file)
@@ -41,9 +41,7 @@
 #include "URIUtils.h"
 #include "log.h"
 #include "addons/AddonManager.h"
-#ifdef HAS_PYTHON
-#include "interfaces/python/XBPython.h"
-#endif
+#include "interfaces/generic/ScriptInvocationManager.h"
 #include "CharsetConverter.h"
 #include "addons/GUIDialogAddonSettings.h"
 
@@ -87,22 +85,22 @@ bool CWeatherJob::DoWork()
     return false;
 
   // initialize our sys.argv variables
-  std::vector<CStdString> argv;
+  std::vector<std::string> argv;
   argv.push_back(addon->LibPath());
 
   CStdString strSetting;
   strSetting.Format("%i", m_location);
   argv.push_back(strSetting);
 
-#ifdef HAS_PYTHON
   // Download our weather
   CLog::Log(LOGINFO, "WEATHER: Downloading weather");
   // call our script, passing the areacode
-  if (g_pythonParser.evalFile(argv[0], argv,addon))
+  int scriptId = -1;
+  if ((scriptId = CScriptInvocationManager::Get().Execute(argv[0], addon, argv)) >= 0)
   {
     while (true)
     {
-      if (!g_pythonParser.isRunning(g_pythonParser.getScriptId(addon->LibPath().c_str())))
+      if (!CScriptInvocationManager::Get().IsRunning(scriptId))
         break;
       Sleep(100);
     }
@@ -120,7 +118,6 @@ bool CWeatherJob::DoWork()
     g_windowManager.SendThreadMessage(msg);
   }
   else
-#endif
     CLog::Log(LOGERROR, "WEATHER: Weather download failed!");
 
   return true;
index 0b82fb3..335d79d 100644 (file)
 #include "utils/FileUtils.h"
 #include "guilib/GUIEditControl.h"
 #include "guilib/GUIKeyboardFactory.h"
-#ifdef HAS_PYTHON
-#include "interfaces/python/XBPython.h"
-#endif
 #include "interfaces/Builtins.h"
+#include "interfaces/generic/ScriptInvocationManager.h"
 #include "dialogs/GUIDialogKaiToast.h"
 #include "dialogs/GUIDialogMediaFilter.h"
 #include "filesystem/SmartPlaylistDirectory.h"
@@ -998,10 +996,8 @@ bool CGUIMediaWindow::OnClick(int iItem)
     AddonPtr addon;
     if (CAddonMgr::Get().GetAddon(url.GetHostName(), addon, ADDON_SCRIPT))
     {
-#ifdef HAS_PYTHON
-      if (!g_pythonParser.StopScript(addon->LibPath()))
-        g_pythonParser.evalFile(addon->LibPath(),addon);
-#endif
+      if (!CScriptInvocationManager::Get().Stop(addon->LibPath()))
+        CScriptInvocationManager::Get().Execute(addon->LibPath(), addon);
       return true;
     }
   }
index b3ec41e..e82a6b4 100644 (file)
@@ -31,9 +31,7 @@
 #include "dialogs/GUIDialogMediaSource.h"
 #include "GUIPassword.h"
 #include "GUIUserMessages.h"
-#ifdef HAS_PYTHON
-#include "interfaces/python/XBPython.h"
-#endif
+#include "interfaces/generic/ScriptInvocationManager.h"
 #include "pictures/GUIWindowSlideShow.h"
 #include "playlists/PlayListFactory.h"
 #include "network/Network.h"
@@ -619,7 +617,7 @@ void CGUIWindowFileManager::OnStart(CFileItem *pItem)
 #ifdef HAS_PYTHON
   if (pItem->IsPythonScript())
   {
-    g_pythonParser.evalFile(pItem->GetPath().c_str(),ADDON::AddonPtr());
+    CScriptInvocationManager::Get().Execute(pItem->GetPath());
     return ;
   }
 #endif
index d30124b..f196f02 100644 (file)
@@ -28,9 +28,6 @@
 #include "profiles/windows/GUIWindowSettingsProfile.h"
 #include "dialogs/GUIDialogContextMenu.h"
 #include "GUIPassword.h"
-#ifdef HAS_PYTHON
-#include "interfaces/python/XBPython.h"
-#endif
 #ifdef HAS_JSONRPC
 #include "interfaces/json-rpc/JSONRPC.h"
 #endif
@@ -306,9 +303,7 @@ void CGUIWindowLoginScreen::LoadProfile(unsigned int profile)
   ADDON::CAddonMgr::Get().ReInit();
 
   g_weatherManager.Refresh();
-#ifdef HAS_PYTHON
-  g_pythonParser.m_bLogin = true;
-#endif
+  g_application.SetLoggingIn(true);
 
 #ifdef HAS_JSONRPC
   JSONRPC::CJSONRPC::Initialize();