예제 #1
0
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);

  m_pExecuter->InitializeInterpreter();

  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(_P(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)
    path += PY_PATH_SEP + _P(addons[i]->LibPath());

  // 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_pExecuter->m_critSection);
    m_threadState = state;
    stopping = m_stopping;
  }

  PyEval_AcquireLock();
  PyThreadState_Swap(state);

  if (!stopping)
  {
    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 *) _P(m_source).c_str(), (char*)"r");
      FILE *fp = PyFile_AsFile(file);

      if (fp)
      {
        PyObject *f = PyString_FromString(_P(m_source).c_str());
        PyDict_SetItemString(moduleDict, "__file__", f);
        Py_DECREF(f);
        PyRun_File(fp, _P(m_source).c_str(), m_Py_file_input, moduleDict, moduleDict);

        // Get a reference to the main module
        // and global dictionary
        PyObject* main_module = PyImport_AddModule((char*)"__main__");
        PyObject* global_dict = PyModule_GetDict(main_module);

        // Extract a reference to the function "func_name"
        // from the global dictionary
        PyObject* expression = PyDict_GetItemString(global_dict, "xbmcclosefilehack");

        if (!PyObject_CallFunction(expression,(char*)"(O)",file))
          CLog::Log(LOGERROR,"Failed to close the script file %s",_P(m_source).c_str());
      }
      else
        CLog::Log(LOGERROR, "%s not found!", m_source);
    }
    else
    {
      //run script
      PyRun_String(m_source, m_Py_file_input, moduleDict, moduleDict);
    }
  }

  if (!PyErr_Occurred())
    CLog::Log(LOGINFO, "Scriptresult: Success");
  else if (PyErr_ExceptionMatches(PyExc_SystemExit))
    CLog::Log(LOGINFO, "Scriptresult: Aborted");
  else
  {
    PyObject* exc_type;
    PyObject* exc_value;
    PyObject* exc_traceback;
    PyObject* pystring;
    pystring = NULL;

    PyErr_Fetch(&exc_type, &exc_value, &exc_traceback);
    if (exc_type == 0 && exc_value == 0 && exc_traceback == 0)
    {
      CLog::Log(LOGINFO, "Strange: No Python exception occured");
    }
    else
    {
      if (exc_type != NULL && (pystring = PyObject_Str(exc_type)) != NULL && (PyString_Check(pystring)))
      {
          PyObject *tracebackModule;

          CLog::Log(LOGINFO, "-->Python script returned the following error<--");
          CLog::Log(LOGERROR, "Error Type: %s", PyString_AsString(PyObject_Str(exc_type)));
          if (PyObject_Str(exc_value))
            CLog::Log(LOGERROR, "Error Contents: %s", PyString_AsString(PyObject_Str(exc_value)));

          tracebackModule = PyImport_ImportModule((char*)"traceback");
          if (tracebackModule != NULL)
          {
            PyObject *tbList, *emptyString, *strRetval;

            tbList = PyObject_CallMethod(tracebackModule, (char*)"format_exception", (char*)"OOO", exc_type, exc_value == NULL ? Py_None : exc_value, exc_traceback == NULL ? Py_None : exc_traceback);
            emptyString = PyString_FromString("");
            strRetval = PyObject_CallMethod(emptyString, (char*)"join", (char*)"O", tbList);

            CLog::Log(LOGERROR, "%s", PyString_AsString(strRetval));

            Py_DECREF(tbList);
            Py_DECREF(emptyString);
            Py_DECREF(strRetval);
            Py_DECREF(tracebackModule);
          }
          CLog::Log(LOGINFO, "-->End of Python script error report<--");
      }
      else
      {
        pystring = NULL;
        CLog::Log(LOGINFO, "<unknown exception type>");
      }

      CGUIDialogKaiToast *pDlgToast = (CGUIDialogKaiToast*)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST);
      if (pDlgToast)
      {
        CStdString desc;
        CStdString path;
        CStdString script;
        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);
      }
    }

    Py_XDECREF(exc_type);
    Py_XDECREF(exc_value); // caller owns all 3
    Py_XDECREF(exc_traceback); // already NULL'd out
    Py_XDECREF(pystring);
  }

  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
  PyXBMC_ClearPendingCalls(state);

  PyThreadState_Swap(NULL);
  PyEval_ReleaseLock();

  { CSingleLock lock(m_pExecuter->m_critSection);
    m_threadState = NULL;
  }

  PyEval_AcquireLock();
  PyThreadState_Swap(state);

  m_pExecuter->DeInitializeInterpreter();

  Py_EndInterpreter(state);
  PyThreadState_Swap(NULL);

  PyEval_ReleaseLock();
}
예제 #2
0
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_pExecuter->m_critSection);
    m_threadState = state;
    stopping = m_stopping;
  }

  PyEval_AcquireLock();
  PyThreadState_Swap(state);

  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();
    }
    catch (...)
    {
      CLog::Log(LOGERROR, "failure in %s", m_source);
    }
  }

  if (!PyErr_Occurred())
    CLog::Log(LOGINFO, "Scriptresult: Success");
  else if (PyErr_ExceptionMatches(PyExc_SystemExit))
    CLog::Log(LOGINFO, "Scriptresult: Aborted");
  else
  {
    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 path;
        CStdString script;
        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);
      }
    }
  }

  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_pExecuter->m_critSection
  //before
  //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_pExecuter->m_critSection);
    m_threadState = NULL;
  }

  PyEval_AcquireLock();
  PyThreadState_Swap(state);

  m_pExecuter->DeInitializeInterpreter();

  // run the gc before finishing
  if (!m_stopping && languageHook->HasRegisteredAddonClasses() && 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);

  // This is a total hack. Python doesn't necessarily release
  // all of the objects associated with the interpreter when
  // you end the interpreter. As a result there are objects 
  // managed by the windowing system that still receive events
  // until python decides to clean them up. Python will eventually
  // clean them up on the creation or ending of a subsequent
  // interpreter. So we are going to keep creating and ending
  // interpreters until we have no more python objects hanging
  // around.
  if (languageHook->HasRegisteredAddonClasses())
  {
    CLog::Log(LOGDEBUG, "The python script \"%s\" has left several "
              "classes in memory that we will be attempting to clean up. The classes include: %s",
              m_source, getListOfAddonClassesAsString(languageHook).c_str());

    int countLimit;
    for (countLimit = 0; languageHook->HasRegisteredAddonClasses() && countLimit < 100; countLimit++)
    {
      PyThreadState* tmpstate = Py_NewInterpreter();
      PyThreadState* oldstate = PyThreadState_Swap(tmpstate);
      if (PyRun_SimpleString(GC_SCRIPT) == -1)
        CLog::Log(LOGERROR,"Failed to run the gc to clean up after running %s",m_source);
      PyThreadState_Swap(oldstate);
      Py_EndInterpreter(tmpstate);
    }

    // If necessary and successfull, debug log the results.
    if (countLimit > 0 && !languageHook->HasRegisteredAddonClasses())
      CLog::Log(LOGDEBUG,"It took %d Py_NewInterpreter/Py_EndInterpreter calls"
                " to clean up the classes leftover from running \"%s.\"",
                countLimit,m_source);

    // If not successful, produce an error message detailing what's been left behind
    if (languageHook->HasRegisteredAddonClasses())
      CLog::Log(LOGERROR, "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();

  PyThreadState_Swap(NULL);
  PyEval_ReleaseLock();
}
    virtual void Update(double time){
        
        StartScreenState* s = _state;

        if(_introActionEndTime > 0){
            double progress = CalculateProgress(_introActionStartTime, time, _introActionEndTime);
            

            //_rotationSpeed = sysMax(0.05, -(sign(progress - 0.5) * pow(abs(progress - 0.5) / (0.5 / pow(0.5, 2.0)), 1.0 / 2.0) - 0.5));
            _rotationSpeed = max(0.05, (-pow(progress*1.5,2.0)+1)*1.2);
            
            
            //_iconCircleRadius = exp(-0.5 * (pow(progress,(1.0/3.0)) - 0.5) * (pow(progress,(1.0/3.0)) - 0.5) / pow(0.399, 4)) / (0.399 * sqrt(pow(2.0, 3.14159265359))) + 0.6;
            _iconCircleRadius = 0.6;
            
            if(_isInitial){
                double x = -pow(progress - 1, 3.0) - 1;
                double y = -((10.0*progress*progress) -pow(progress,(1.0/3.0)))*2.0;
                
                s->BtnPlay->Position(s->FrogPos.x - x * 300.0,s->FrogPos.y + y * 300.0);
            }
            
            double p = sysMin(pow(progress, 3)*6, 1.0);
            
            vec2 offScreen = vec2(-60,60);
            
            s->BtnMusic->Pos(offScreen + (s->BtnMusicPos - offScreen) * pow(p,3.0));
            s->BtnEffects->Pos(offScreen + (s->BtnEffectsPos - offScreen) * pow(p,2.5));
            s->BtnAchievements->Pos(offScreen + (s->BtnAchievementsPos - offScreen) * pow(p,2.0));
            s->BtnLeaderboards->Pos(offScreen + (s->BtnLeaderboardsPos - offScreen) * pow(p,1.5));
            s->BtnInfo->Pos(offScreen + (s->BtnInfoPos - offScreen) * p);
            
            // ease out
            p = (33*(progress * (progress * progress))*(progress * progress) + -106*(progress * progress)*(progress * progress) + 126*(progress * (progress * progress)) + -67*(progress * progress) + 15*progress) - pow(progress,1.0/8.0);
            
            s->Title->Pos(s->TitlePos + vec2(0, 200*p));
            
            if(progress > 1.0){
                s->BtnInfo->Pos(s->BtnInfoPos);
                s->BtnMusic->Pos(s->BtnMusicPos);
                s->BtnEffects->Pos(s->BtnEffectsPos);
                s->BtnAchievements->Pos(s->BtnAchievementsPos);
                s->BtnLeaderboards->Pos(s->BtnLeaderboardsPos);
                
                _introActionEndTime = -1;
            }
        }

        
        vec2 center = s->FrogPos;
        center.y -= 30;
        double radius = 100 + _iconCircleRadius*200;
            
            
        vec2 initialPos = center + vec2(0,radius);
        
        --_framesWithNoRotation;
        if(_framesWithNoRotation < 0){
            _rotation += _rotationSpeed*10*DTR;
        }
        
        double rotClassic = sysMod(_rotation, PI2);
        double rotSprint = sysMod(CIRC_PART+_rotation, PI2);
        double rotEasyPlay = sysMod(-CIRC_PART+_rotation, PI2);
        if(rotEasyPlay < 0) rotEasyPlay += PI2;
        
        if(_introActionEndTime < 0 && _framesWithNoRotation < -5 &&
           (IsNearPi(rotClassic) || IsNearPi(rotSprint) || IsNearPi(rotEasyPlay))){
            _framesWithNoRotation = 300;
        }

        rotClassic = UpdateRotation(rotClassic);
        rotSprint = UpdateRotation(rotSprint);
        rotEasyPlay = UpdateRotation(rotEasyPlay);

        s->BtnClassic->Pos(vec2(initialPos).Rotate(center, rotClassic));
        s->BtnSprint->Pos(vec2(initialPos).Rotate(center, rotSprint)); 
        s->BtnEasyPlay->Pos(vec2(initialPos).Rotate(center,rotEasyPlay));
//cout << sysMod(_rotation, 2*Pi) << "|" << CircLocation(sysMod(_rotation, 2*Pi)) << endl;
 
        
        Ellipsify(s->IsClassicHit, s->BtnClassic, s->InsectClassic, center, radius, rotClassic);
        Ellipsify(s->IsSprintHit, s->BtnSprint, s->InsectSprint, center, radius, rotSprint);
        Ellipsify(s->IsEasyPlayHit, s->BtnEasyPlay, s->InsectEasyPlay, center, radius, rotEasyPlay);
        
        
        if(_frame % 5 == 0){
            s->Drawables->sort(SortDrawableFunc, s->Drawables->head->next, s->Drawables->tail);
        }
   

        if(!s->IsClassicHit) s->InsectClassic->Pos(s->BtnClassic->Pos());
        if(!s->IsSprintHit) s->InsectSprint->Pos(s->BtnSprint->Pos());
        if(!s->IsEasyPlayHit) s->InsectEasyPlay->Pos(s->BtnEasyPlay->Pos());
        
        double rotateIconSpeed = -(_rotationSpeed/10.0);
        RotateImg(s->BtnClassic, rotateIconSpeed);
        RotateImg(s->BtnSprint, rotateIconSpeed);
        RotateImg(s->BtnEasyPlay, rotateIconSpeed);
    }