/ vfe / vfe.cpp
vfe.cpp
   1  /*******************************************************************************
   2   * vfe.cpp
   3   *
   4   * This module contains the C++ implementation for the virtual frontend.
   5   *
   6   * Author: Christopher J. Cason
   7   *
   8   * ---------------------------------------------------------------------------
   9   * Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
  10   * Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
  11   *
  12   * POV-Ray is free software: you can redistribute it and/or modify
  13   * it under the terms of the GNU Affero General Public License as
  14   * published by the Free Software Foundation, either version 3 of the
  15   * License, or (at your option) any later version.
  16   *
  17   * POV-Ray is distributed in the hope that it will be useful,
  18   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20   * GNU Affero General Public License for more details.
  21   *
  22   * You should have received a copy of the GNU Affero General Public License
  23   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  24   * ---------------------------------------------------------------------------
  25   * POV-Ray is based on the popular DKB raytracer version 2.12.
  26   * DKBTrace was originally written by David K. Buck.
  27   * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
  28   * ---------------------------------------------------------------------------
  29   * $File: //depot/public/povray/3.x/vfe/vfe.cpp $
  30   * $Revision: #1 $
  31   * $Change: 6069 $
  32   * $DateTime: 2013/11/06 11:59:40 $
  33   * $Author: chrisc $
  34   *******************************************************************************/
  35  
  36  #ifdef _MSC_VER
  37  #pragma warning(disable : 4244)
  38  #pragma warning(disable : 4267)
  39  #endif
  40  
  41  #include "frame.h"
  42  #include "povray.h"
  43  #include "vfe.h"
  44  
  45  // this must be the last file included
  46  #include "base/povdebug.h"
  47  
  48  namespace vfe
  49  {
  50  
  51  using namespace pov_base;
  52  using boost::format;
  53  
  54  ////////////////////////////////////////////////////////////////////////////////////////
  55  //
  56  // class POVMSMessageDetails
  57  //
  58  ////////////////////////////////////////////////////////////////////////////////////////
  59  
  60  class POVMSMessageDetails
  61  {
  62    public:
  63      POVMSMessageDetails (POVMS_Object &Obj);
  64      virtual ~POVMSMessageDetails () {} ;
  65      string GetContext (int NumLines) ;
  66  
  67    protected:
  68      string File ;
  69      UCS2String UCS2File;
  70      string URL ;
  71      string Message ;
  72      POVMSInt Line ;
  73      POVMSInt Col ;
  74      POVMSLong Offset ;
  75  } ;
  76  
  77  POVMSMessageDetails::POVMSMessageDetails (POVMS_Object& Obj)
  78  {
  79    char                  buffer [2048] = "";
  80    UCS2                  ubuffer [2048];
  81    POVMSInt              l = sizeof (ubuffer);
  82    POVMSLong             ll ;
  83    POVMSObject           msgobj (Obj());
  84    POVMSObjectPtr        msg = &msgobj;
  85  
  86    Line = Col = 0 ;
  87    Offset = -1 ;
  88    ubuffer[0] = 0 ;
  89  
  90    if (POVMSUtil_GetUCS2String (msg, kPOVAttrib_FileName, ubuffer, &l) == kNoErr)
  91    {
  92      UCS2File = ubuffer ;
  93      File = UCS2toASCIIString (UCS2File);
  94    }
  95    if (POVMSUtil_GetLong (msg, kPOVAttrib_Line, &ll) == kNoErr)
  96      Line = POVMSInt(ll) ;
  97    if (POVMSUtil_GetLong (msg, kPOVAttrib_Column, &ll) == kNoErr)
  98      Col = POVMSInt(ll + 1) ;
  99    if(POVMSUtil_GetLong(msg, kPOVAttrib_FilePosition, &ll) == kNoErr)
 100      Offset = ll ;
 101    l = sizeof (buffer) ;
 102    if (POVMSUtil_GetString (msg, kPOVAttrib_EnglishText, buffer, &l) == kNoErr)
 103      Message = buffer ;
 104  
 105    POVMSObject_Delete(msg);
 106  }
 107  
 108  string POVMSMessageDetails::GetContext (int NumLines)
 109  {
 110    return ("") ;
 111  }
 112  
 113  ////////////////////////////////////////////////////////////////////////////////////////
 114  //
 115  // class ParseErrorDetails, ParseWarningDetails
 116  //
 117  ////////////////////////////////////////////////////////////////////////////////////////
 118  
 119  class ParseWarningDetails : public POVMSMessageDetails
 120  {
 121    public:
 122      ParseWarningDetails (POVMS_Object &Obj) : POVMSMessageDetails (Obj) {} ;
 123      virtual ~ParseWarningDetails () {} ;
 124  
 125    public:
 126      using POVMSMessageDetails::File ;
 127      using POVMSMessageDetails::UCS2File ;
 128      using POVMSMessageDetails::Message ;
 129      using POVMSMessageDetails::Line ;
 130      using POVMSMessageDetails::Col ;
 131      using POVMSMessageDetails::Offset ;
 132  } ;
 133  
 134  class ParseErrorDetails : public POVMSMessageDetails
 135  {
 136    public:
 137      ParseErrorDetails (POVMS_Object &Obj) : POVMSMessageDetails (Obj) {} ;
 138      virtual ~ParseErrorDetails () {} ;
 139  
 140    public:
 141      using POVMSMessageDetails::File ;
 142      using POVMSMessageDetails::UCS2File ;
 143      using POVMSMessageDetails::Message ;
 144      using POVMSMessageDetails::Line ;
 145      using POVMSMessageDetails::Col ;
 146      using POVMSMessageDetails::Offset ;
 147  } ;
 148  
 149  ////////////////////////////////////////////////////////////////////////////////////////
 150  //
 151  // class vfeConsole
 152  //
 153  ////////////////////////////////////////////////////////////////////////////////////////
 154  
 155  vfeConsole::vfeConsole(vfeSession *session, int width) : Console(width == -1 ? session->GetConsoleWidth() : width)
 156  {
 157    m_Session = session;
 158    Initialise();
 159  }
 160  
 161  vfeConsole::~vfeConsole()
 162  {
 163  }
 164  
 165  void vfeConsole::Initialise()
 166  {
 167    rawBuffer [0] = '\0' ;
 168  }
 169  
 170  void vfeConsole::BufferOutput(const char *str, unsigned int chars, vfeSession::MessageType mType)
 171  {
 172    char                  *s ;
 173  
 174    // HACK FIXME - this is to prevent duplicate messages
 175    if (m_Session->HadErrorMessage() && strncmp (str, "Fatal error in parser: ", 23) == 0)
 176      return ;
 177  
 178    if (str [0] == '\n' && str [1] == '\0')
 179    {
 180      m_Session->AppendStreamMessage (mType, rawBuffer) ;
 181      rawBuffer [0] = '\0' ;
 182      return ;
 183    }
 184  
 185    size_t sLen = chars ;
 186    if (sLen == 0)
 187      sLen = strlen (str) ;
 188    size_t bLen = strlen (rawBuffer) ;
 189    if (sLen > sizeof (rawBuffer) - bLen - 1)
 190      sLen = sizeof (rawBuffer) - bLen - 1 ;
 191    strncat (rawBuffer, str, sLen) ;
 192    if ((s = strrchr (rawBuffer, '\n')) != NULL)
 193    {
 194      *s++ = '\0' ;
 195      m_Session->AppendStreamMessage (mType, rawBuffer) ;
 196      strcpy (rawBuffer, s) ;
 197    }
 198  }
 199  
 200  void vfeConsole::Output(const char *str, vfeSession::MessageType mType)
 201  {
 202    BufferOutput (str, (unsigned int) strlen (str), mType) ;
 203    BufferOutput ("\n", 1, mType) ;
 204  }
 205  
 206  void vfeConsole::Output(const string& str, vfeSession::MessageType mType)
 207  {
 208    Output (str.c_str(), mType) ;
 209  }
 210  
 211  void vfeConsole::Output(const string& str)
 212  {
 213    Output (str.c_str()) ;
 214  }
 215  
 216  ////////////////////////////////////////////////////////////////////////////////////////
 217  //
 218  // class vfePlatformBase
 219  //
 220  ////////////////////////////////////////////////////////////////////////////////////////
 221  
 222  vfePlatformBase::vfePlatformBase(vfeSession& session) : m_Session(&session), PlatformBase()
 223  {
 224  }
 225  
 226  vfePlatformBase::~vfePlatformBase()
 227  {
 228  }
 229  
 230  IStream *vfePlatformBase::CreateIStream(const unsigned int stype)
 231  {
 232    return (new IStream (stype)) ;
 233  }
 234  
 235  OStream *vfePlatformBase::CreateOStream(const unsigned int stype)
 236  {
 237    return (new OStream (stype)) ;
 238  }
 239  
 240  UCS2String vfePlatformBase::GetTemporaryPath(void)
 241  {
 242    return m_Session->GetTemporaryPath();
 243  }
 244  
 245  UCS2String vfePlatformBase::CreateTemporaryFile(void)
 246  {
 247    return m_Session->CreateTemporaryFile();
 248  }
 249  
 250  void vfePlatformBase::DeleteTemporaryFile(const UCS2String& filename)
 251  {
 252    m_Session->DeleteTemporaryFile(filename);
 253  }
 254  
 255  bool vfePlatformBase::ReadFileFromURL(OStream *file, const UCS2String& url, const UCS2String& referrer)
 256  {
 257    return false;
 258  }
 259  
 260  ////////////////////////////////////////////////////////////////////////////////////////
 261  //
 262  // class vfeParserMessageHandler
 263  //
 264  ////////////////////////////////////////////////////////////////////////////////////////
 265  
 266  vfeParserMessageHandler::vfeParserMessageHandler() : ParserMessageHandler()
 267  {
 268    m_Session = vfeSession::GetSessionFromThreadID();
 269  }
 270  
 271  vfeParserMessageHandler::~vfeParserMessageHandler()
 272  {
 273  }
 274  
 275  void vfeParserMessageHandler::Options(Console *Con, POVMS_Object& Obj, bool conout)
 276  {
 277    if (Obj.TryGetBool (kPOVAttrib_OutputAlpha, false))
 278      m_Session->SetUsingAlpha();
 279    if (Obj.TryGetBool (kPOVAttrib_ClocklessAnimation, false))
 280      m_Session->SetClocklessAnimation();
 281    if (Obj.TryGetBool (kPOVAttrib_RealTimeRaytracing, false))
 282      m_Session->SetRealTimeRaytracing();
 283    ParserMessageHandler::Options (Con, Obj, conout) ;
 284  }
 285  
 286  void vfeParserMessageHandler::Statistics(Console *Con, POVMS_Object& Obj, bool conout)
 287  {
 288    ParserMessageHandler::Statistics (Con, Obj, conout) ;
 289  }
 290  
 291  void vfeParserMessageHandler::Progress(Console *Con, POVMS_Object& Obj, bool verbose)
 292  {
 293    switch(Obj.GetType(kPOVMSObjectClassID))
 294    {
 295      case kPOVObjectClass_ParserProgress:
 296      {
 297        m_Session->AppendStatusMessage (format ("Parsing %uK tokens") % (Obj.GetLong (kPOVAttrib_CurrentTokenCount) / 1000));
 298        break;
 299      }
 300      case kPOVObjectClass_BoundingProgress:
 301      {
 302        m_Session->AppendStatusMessage (format ("Constructed %uK BSP nodes") % (Obj.GetLong (kPOVAttrib_CurrentNodeCount) / 1000));
 303        break;
 304      }
 305    }
 306  }
 307  
 308  void vfeParserMessageHandler::Warning(Console *Con, POVMS_Object& Obj, bool conout)
 309  {
 310    ParseWarningDetails   d (Obj) ;
 311  
 312    if (d.Message == "")
 313      return ;
 314  
 315    // as we provide special treatment for warning messages here if we're not
 316    // optimized for console output, we don't duplicate them to the console
 317    // regardless of whether or not conout is set.
 318    if (m_Session->m_OptimizeForConsoleOutput == false)
 319      m_Session->AppendWarningMessage (d.Message, d.UCS2File, d.Line, d.Col) ;
 320  
 321    if (!d.File.empty() && (d.Line > 0))
 322    {
 323      format f = format ("File '%s' line %d: %s") % d.File % d.Line % d.Message ;
 324      if (m_Session->m_OptimizeForConsoleOutput == false)
 325        m_Session->AppendStatusMessage (f) ;
 326      else if (conout)
 327        Con->puts (f.str().c_str()) ;
 328    }
 329    else
 330    {
 331      if (m_Session->m_OptimizeForConsoleOutput == false)
 332        m_Session->AppendStatusMessage (d.Message) ;
 333      else if (conout)
 334        Con->puts (d.Message.c_str()) ;
 335    }
 336  }
 337  
 338  void vfeParserMessageHandler::Error(Console *Con, POVMS_Object& Obj, bool conout)
 339  {
 340    ParseErrorDetails     d (Obj) ;
 341  
 342    if (d.Message == "" && (d.File == "" || d.Line <= 0))
 343      return ;
 344  
 345    // as we provide special treatment for parser errors here if we're not
 346    // optimized for console output, we don't duplicate them to the console
 347    // regardless of whether or not conout is set.
 348    if (m_Session->m_OptimizeForConsoleOutput == false)
 349      m_Session->AppendErrorMessage (d.Message, d.UCS2File, d.Line, d.Col) ;
 350  
 351    if (!d.Message.empty())
 352    {
 353      if (!d.File.empty() && (d.Line > 0))
 354      {
 355        format f = format ("File '%s' line %d: %s") % d.File % d.Line % d.Message ;
 356        if (m_Session->m_OptimizeForConsoleOutput == false)
 357          m_Session->AppendStatusMessage (f) ;
 358        else if (conout)
 359          Con->puts (f.str().c_str()) ;
 360      }
 361      else
 362      {
 363        if (m_Session->m_OptimizeForConsoleOutput == false)
 364          m_Session->AppendStatusMessage (d.Message) ;
 365        if (conout)
 366          if (m_Session->m_OptimizeForConsoleOutput == true)
 367            Con->puts (d.Message.c_str()) ;
 368      }
 369    }
 370    else
 371    {
 372      format f = format ("Parse error in file '%s' at line %d") % d.File % d.Line ;
 373      if (m_Session->m_OptimizeForConsoleOutput == false)
 374        m_Session->AppendStatusMessage (f) ;
 375      if (conout)
 376        if (m_Session->m_OptimizeForConsoleOutput == true)
 377          Con->puts (f.str().c_str()) ;
 378    }
 379  }
 380  
 381  void vfeParserMessageHandler::FatalError(Console *Con, POVMS_Object& Obj, bool conout)
 382  {
 383    m_Session->SetFailed();
 384    Error (Con, Obj, conout) ;
 385  }
 386  
 387  void vfeParserMessageHandler::DebugInfo(Console *Con, POVMS_Object& Obj, bool conout)
 388  {
 389    string str(Obj.GetString(kPOVAttrib_EnglishText));
 390    if (m_Session->m_OptimizeForConsoleOutput == true)
 391    {
 392      if (conout)
 393          Con->puts (str.c_str()) ;
 394    }
 395    else
 396      m_Session->AppendStreamMessage (vfeSession::mDebug, str.c_str()) ;
 397  }
 398  
 399  ////////////////////////////////////////////////////////////////////////////////////////
 400  //
 401  // class vfeRenderMessageHandler
 402  //
 403  ////////////////////////////////////////////////////////////////////////////////////////
 404  
 405  vfeRenderMessageHandler::vfeRenderMessageHandler() : RenderMessageHandler()
 406  {
 407    m_Session = vfeSession::GetSessionFromThreadID();
 408  }
 409  
 410  vfeRenderMessageHandler::~vfeRenderMessageHandler()
 411  {
 412  }
 413  
 414  void vfeRenderMessageHandler::Options(Console *Con, POVMS_Object& Obj, bool conout)
 415  {
 416    RenderMessageHandler::Options (Con, Obj, conout) ;
 417  }
 418  
 419  void vfeRenderMessageHandler::Statistics(Console *Con, POVMS_Object& Obj, bool conout)
 420  {
 421    RenderMessageHandler::Statistics (Con, Obj, conout) ;
 422  }
 423  
 424  void vfeRenderMessageHandler::Progress(Console *Con, POVMS_Object& Obj, bool verbose)
 425  {
 426    switch (Obj.GetType(kPOVMSObjectClassID))
 427    {
 428      case kPOVObjectClass_PhotonProgress:
 429      {
 430        int cpc (Obj.GetInt (kPOVAttrib_CurrentPhotonCount)) ;
 431        m_Session->AppendStatusMessage (format ("Photon count %u") % cpc, 250) ;
 432        break;
 433      }
 434      case kPOVObjectClass_RadiosityProgress:
 435      {
 436        int pc (Obj.GetInt (kPOVAttrib_Pixels)) ;
 437        int cc (Obj.GetInt (kPOVAttrib_PixelsCompleted)) ;
 438        m_Session->SetPixelsRendered(cc, pc);
 439        int percent = pc > 0 ? int ((cc * 100.0) / pc) : 0 ;
 440        m_Session->SetPercentComplete (percent);
 441        m_Session->AppendStatusMessage (format ("Performing radiosity pretrace: %d of %d pixels (%d%%)") % cc % pc % percent, 250) ;
 442        break;
 443      }
 444      case kPOVObjectClass_RenderProgress:
 445      {
 446        int pc (Obj.GetInt (kPOVAttrib_Pixels)) ;
 447        int cc (Obj.GetInt (kPOVAttrib_PixelsCompleted)) ;
 448  
 449        if (m_Session->GetRealTimeRaytracing() == false)
 450        {
 451          m_Session->SetPixelsRendered(cc, pc);
 452          int percent = pc > 0 ? (int) ((cc * 100.0) / pc) : 0 ;
 453          m_Session->SetPercentComplete (percent);
 454          if (verbose == true || m_Session->m_OptimizeForConsoleOutput == false)
 455            m_Session->AppendStatusMessage (format ("Rendered %u of %u pixels (%d%%)") % cc % pc % percent, 250) ;
 456        }
 457        else
 458        {
 459          m_Session->SetPixelsRendered(cc % pc, pc);
 460          float elapsed = m_Session->GetElapsedTime() / 1000.0f;
 461          float frames = (float) cc / pc;
 462          float fps = frames / elapsed;
 463          if (verbose == true || m_Session->m_OptimizeForConsoleOutput == false)
 464            m_Session->AppendStatusMessage (format ("Rendered %g frames over %g seconds (%g FPS)") % frames % elapsed % fps, 250) ;
 465        }
 466        break;
 467      }
 468    }
 469  }
 470  
 471  void vfeRenderMessageHandler::Warning(Console *Con, POVMS_Object& Obj, bool conout)
 472  {
 473    RenderMessageHandler::Warning (Con, Obj, conout) ;
 474  }
 475  
 476  void vfeRenderMessageHandler::Error(Console *Con, POVMS_Object& Obj, bool conout)
 477  {
 478    m_Session->SetFailed();
 479    RenderMessageHandler::Error (Con, Obj, conout) ;
 480  }
 481  
 482  void vfeRenderMessageHandler::FatalError(Console *Con, POVMS_Object& Obj, bool conout)
 483  {
 484    m_Session->SetFailed();
 485    RenderMessageHandler::FatalError (Con, Obj, conout) ;
 486  }
 487  
 488  ////////////////////////////////////////////////////////////////////////////////////////
 489  //
 490  // class vfeProcessRenderOptions
 491  //
 492  ////////////////////////////////////////////////////////////////////////////////////////
 493  
 494  vfeProcessRenderOptions::vfeProcessRenderOptions(vfeSession *Session) : ProcessRenderOptions(), m_Session(Session)
 495  {
 496  }
 497  
 498  vfeProcessRenderOptions::~vfeProcessRenderOptions()
 499  {
 500  }
 501  
 502  int vfeProcessRenderOptions::ReadSpecialOptionHandler(INI_Parser_Table *Table, char *Param, POVMSObjectPtr Obj)
 503  {
 504    return ProcessRenderOptions::ReadSpecialOptionHandler (Table, Param, Obj);
 505  }
 506  
 507  int vfeProcessRenderOptions::ReadSpecialSwitchHandler(Cmd_Parser_Table *Table, char *Param, POVMSObjectPtr Obj, bool On)
 508  {
 509    return ProcessRenderOptions::ReadSpecialSwitchHandler (Table, Param, Obj, On);
 510  }
 511  
 512  int vfeProcessRenderOptions::WriteSpecialOptionHandler(INI_Parser_Table *Table, POVMSObjectPtr Obj, OTextStream *S)
 513  {
 514    return ProcessRenderOptions::WriteSpecialOptionHandler (Table, Obj, S);
 515  }
 516  
 517  bool vfeProcessRenderOptions::WriteOptionFilter(INI_Parser_Table *Table)
 518  {
 519    return ProcessRenderOptions::WriteOptionFilter (Table);
 520  }
 521  
 522  int vfeProcessRenderOptions::ProcessUnknownString(char *String, POVMSObjectPtr Obj)
 523  {
 524    return ProcessRenderOptions::ProcessUnknownString (String, Obj);
 525  }
 526  
 527  ITextStream *vfeProcessRenderOptions::OpenFileForRead(const char *Name, POVMSObjectPtr Obj)
 528  {
 529    return (ProcessRenderOptions::OpenFileForRead (Name, Obj)) ;
 530  }
 531  
 532  OTextStream *vfeProcessRenderOptions::OpenFileForWrite(const char *Name, POVMSObjectPtr Obj)
 533  {
 534    return (ProcessRenderOptions::OpenFileForWrite (Name, Obj)) ;
 535  }
 536  
 537  void vfeProcessRenderOptions::ParseError(const char *format, ...)
 538  {
 539    char str[1024];
 540    va_list marker;
 541  
 542    va_start(marker, format);
 543    vsnprintf(str, sizeof(str)-2, format, marker);
 544    va_end(marker);
 545  
 546    m_Session->AppendStatusMessage (str);
 547    m_Session->AppendErrorMessage (str) ;
 548    m_Session->SetFailed();
 549  }
 550  
 551  void vfeProcessRenderOptions::ParseErrorAt(ITextStream *file, const char *format, ...)
 552  {
 553    char str[1024];
 554    va_list marker;
 555  
 556    va_start(marker, format);
 557    vsnprintf(str, sizeof(str)-2, format, marker);
 558    va_end(marker);
 559  
 560    m_Session->AppendStatusMessage (str);
 561    m_Session->AppendErrorMessage (str, file->name(), file->line(), 0) ;
 562    m_Session->SetFailed();
 563  }
 564  
 565  void vfeProcessRenderOptions::WriteError(const char *format, ...)
 566  {
 567    char str[1024];
 568    va_list marker;
 569  
 570    va_start(marker, format);
 571    vsnprintf(str, sizeof(str)-2, format, marker);
 572    va_end(marker);
 573  
 574    m_Session->AppendStatusMessage (str);
 575    m_Session->AppendErrorMessage (str) ;
 576    m_Session->SetFailed();
 577  }
 578  
 579  ////////////////////////////////////////////////////////////////////////////////////////
 580  //
 581  // class VirtualFrontEnd
 582  //
 583  ////////////////////////////////////////////////////////////////////////////////////////
 584  
 585  VirtualFrontEnd::VirtualFrontEnd(vfeSession& session, POVMSContext ctx, POVMSAddress addr, POVMS_Object& msg, POVMS_Object *result, shared_ptr<Console>& console) :
 586    m_Session(&session), m_PlatformBase(session), renderFrontend (ctx)
 587  {
 588    backendAddress = addr ;
 589    state = kReady ;
 590    m_PostPauseState = kReady;
 591    consoleResult = NULL ;
 592    displayResult = NULL ;
 593    m_PauseRequested = m_PausedAfterFrame = false;
 594    renderFrontend.ConnectToBackend(backendAddress, msg, result, console);
 595  }
 596  
 597  VirtualFrontEnd::~VirtualFrontEnd()
 598  {
 599    // file-backed images may require a reference to PlatformBase to delete temporary files
 600    // we need to explicitly delete it here since otherwise PlatformBase will have been destroyed
 601    // before the shared_ptr does its cleanup
 602    imageProcessing.reset();
 603    if (backendAddress != POVMSInvalidAddress)
 604      renderFrontend.DisconnectFromBackend(backendAddress);
 605    state = kUnknown;
 606  }
 607  
 608  bool VirtualFrontEnd::Start(POVMS_Object& opts)
 609  {
 610    if (state != kReady)
 611      return false;
 612  
 613    m_Session->Clear();
 614    animationProcessing.reset() ;
 615    m_PauseRequested = m_PausedAfterFrame = false;
 616    m_PostPauseState = kReady;
 617  
 618    Path ip (m_Session->GetInputFilename());
 619    shelloutProcessing.reset(m_Session->CreateShelloutProcessing(opts, UCS2toASCIIString(ip.GetFile()), m_Session->GetRenderWidth(), m_Session->GetRenderHeight())) ;
 620    shelloutProcessing->SetCancelMessage("Render halted because the %1% shell-out ('%6%') requested POV-Ray to %5%.");
 621    shelloutProcessing->SetSkipMessage("The %1% shell-out ('%3%') requested POV-Ray to %2%.");
 622  
 623    POVMS_List declares;
 624    if(opts.Exist(kPOVAttrib_Declare) == true)
 625      opts.Get(kPOVAttrib_Declare, declares);
 626  
 627    POVMS_Object image_width(kPOVMSType_WildCard);
 628    image_width.SetString(kPOVAttrib_Identifier, "image_width");
 629    image_width.SetFloat(kPOVAttrib_Value, opts.TryGetInt(kPOVAttrib_Width, 160));
 630    declares.Append(image_width);
 631  
 632    POVMS_Object image_height(kPOVMSType_WildCard);
 633    image_height.SetString(kPOVAttrib_Identifier, "image_height");
 634    image_height.SetFloat(kPOVAttrib_Value, opts.TryGetInt(kPOVAttrib_Height, 120));
 635    declares.Append(image_height);
 636  
 637    POVMS_Object input_file_name(kPOVMSType_WildCard);
 638    input_file_name.SetString(kPOVAttrib_Identifier, "input_file_name");
 639    input_file_name.SetString(kPOVAttrib_Value, UCS2toASCIIString(ip.GetFile()).c_str());
 640    declares.Append(input_file_name);
 641  
 642    int initialFrame = opts.TryGetInt (kPOVAttrib_InitialFrame, 0) ;
 643    int finalFrame = opts.TryGetInt (kPOVAttrib_FinalFrame, 0) ;
 644    if ((initialFrame == 0 && finalFrame == 0) || (initialFrame == 1 && finalFrame == 1))
 645    {
 646      POVMS_Object clock_delta(kPOVMSType_WildCard);
 647      clock_delta.SetString(kPOVAttrib_Identifier, "clock_delta");
 648      clock_delta.SetFloat(kPOVAttrib_Value, 0.0f);
 649      declares.Append(clock_delta);
 650  
 651      POVMS_Object final_clock(kPOVMSType_WildCard);
 652      final_clock.SetString(kPOVAttrib_Identifier, "final_clock");
 653      final_clock.SetFloat(kPOVAttrib_Value, 0.0f);
 654      declares.Append(final_clock);
 655  
 656      POVMS_Object final_frame(kPOVMSType_WildCard);
 657      final_frame.SetString(kPOVAttrib_Identifier, "final_frame");
 658      final_frame.SetFloat(kPOVAttrib_Value, 0.0f);
 659      declares.Append(final_frame);
 660  
 661      POVMS_Object frame_number(kPOVMSType_WildCard);
 662      frame_number.SetString(kPOVAttrib_Identifier, "frame_number");
 663      frame_number.SetFloat(kPOVAttrib_Value, 0.0f);
 664      declares.Append(frame_number);
 665  
 666      POVMS_Object initial_clock(kPOVMSType_WildCard);
 667      initial_clock.SetString(kPOVAttrib_Identifier, "initial_clock");
 668      initial_clock.SetFloat(kPOVAttrib_Value, 0.0f);
 669      declares.Append(initial_clock);
 670  
 671      POVMS_Object initial_frame(kPOVMSType_WildCard);
 672      initial_frame.SetString(kPOVAttrib_Identifier, "initial_frame");
 673      initial_frame.SetFloat(kPOVAttrib_Value, 0.0f);
 674      declares.Append(initial_frame);
 675  
 676      opts.Set(kPOVAttrib_Declare, declares);
 677      // optimization: reset imageProcessing now even though the following assign
 678      // will free the old pointer (if it exists). this can potentially free a
 679      // significant amount of memory and may in some circumstances prevent the
 680      // image allocation from failing. TODO: it may be useful to check whether
 681      // we can re-use an old imageProcessing instance (e.g. if image options are
 682      // the same).
 683      imageProcessing.reset();
 684  
 685      // TODO: update ImageProcessing with the means of accepting and caching 
 686      // blocks of pixels as opposed to individual ones, with a back-end that
 687      // can serialize completed rows to the final image output file.
 688      options = opts;
 689  
 690      if (m_Session->OutputToFileSet())
 691      {
 692        imageProcessing = shared_ptr<ImageProcessing> (new ImageProcessing (opts));
 693        UCS2String filename = imageProcessing->GetOutputFilename (opts, 0, 0);
 694        options.SetUCS2String (kPOVAttrib_OutputFile, filename.c_str());
 695  
 696        if ((imageProcessing->OutputIsStdout() || imageProcessing->OutputIsStderr()) && m_Session->ImageOutputToStdoutSupported() == false)
 697          throw POV_EXCEPTION(kCannotOpenFileErr, "Image output to STDOUT/STDERR not supported on this platform");
 698  
 699        // test access permission now to avoid surprise later after waiting for
 700        // the render to complete.
 701        if (imageProcessing->OutputIsStdout() == false && imageProcessing->OutputIsStderr() == false && m_Session->TestAccessAllowed(filename, true) == false)
 702        {
 703          string str ("IO Restrictions prohibit write access to '") ;
 704          str += UCS2toASCIIString(filename);
 705          str += "'";
 706          throw POV_EXCEPTION(kCannotOpenFileErr, str);
 707        }
 708        shelloutProcessing->SetOutputFile(UCS2toASCIIString(filename));
 709        m_Session->AdviseOutputFilename (filename);
 710      }
 711    }
 712    else
 713    {
 714      // the output filename is set in Process()
 715      m_Session->SetRenderingAnimation();
 716      opts.Set(kPOVAttrib_Declare, declares);
 717      imageProcessing.reset();
 718      if (m_Session->OutputToFileSet())
 719        imageProcessing = shared_ptr<ImageProcessing> (new ImageProcessing (opts)) ;
 720      animationProcessing = shared_ptr<AnimationProcessing> (new AnimationProcessing (opts)) ;
 721      options = animationProcessing->GetFrameRenderOptions () ;
 722    }
 723  
 724    state = kStarting;
 725  
 726    return true;
 727  }
 728  
 729  bool VirtualFrontEnd::Stop()
 730  {
 731    bool result = false;
 732  
 733    try
 734    {
 735      switch(state)
 736      {
 737        case kStarting:
 738          state = kStopped;
 739          m_Session->SetFailed();
 740          result = true;
 741          break;
 742  
 743        case kPreSceneShellout:
 744        case kPreFrameShellout:
 745        case kPostFrameShellout:
 746        case kPostSceneShellout:
 747          if (shelloutProcessing->ShelloutRunning())
 748            if (!shelloutProcessing->KillShellouts(2, false) && !shelloutProcessing->KillShellouts(1, true))
 749              m_Session->AppendErrorAndStatusMessage("Failed to terminate currently-running shellout process") ;
 750          if (state == kPostSceneShellout)
 751          {
 752            state = kDone;
 753            return true;
 754          }
 755          m_Session->SetFailed();
 756          state = kStopped;
 757          result = true;
 758          break;
 759  
 760        case kPostShelloutPause:
 761          m_Session->SetFailed();
 762          state = kStopping;
 763          result = true;
 764          break;
 765  
 766        case kParsing:
 767        case kPausedParsing:
 768          // the parser could be already in a finished state, even if it accepted a pause earlier
 769          try { renderFrontend.StopParser(sceneId); } catch (pov_base::Exception&) { }
 770          m_Session->SetFailed();
 771          state = kStopping;
 772          result = true;
 773          break;
 774  
 775        case kRendering:
 776        case kPausedRendering:
 777          m_Session->SetFailed();
 778          if (m_PausedAfterFrame == true)
 779          {
 780            m_PausedAfterFrame = false;
 781            state = kStopped;
 782          }
 783          else
 784          {
 785            // the renderer could be already in a finished state, even if it accepted a pause earlier
 786            try { renderFrontend.StopRender(viewId); } catch (pov_base::Exception&) { }
 787            state = kStopping;
 788          }
 789          result = true;
 790          break;
 791      }
 792    }
 793    catch (pov_base::Exception& e)
 794    {
 795      m_Session->SetFailed();
 796      m_Session->AppendErrorAndStatusMessage (e.what()) ;
 797    }
 798    try
 799    {
 800      shelloutProcessing->ProcessEvent(ShelloutProcessing::userAbort);
 801    }
 802    catch (pov_base::Exception& e)
 803    {
 804      // if it's a kCannotOpenFileErr, it means permission to run the process was denied
 805      // we don't set failed in that case as we allow shelloutprocessing to handle it
 806      if (!e.codevalid() || (e.code() != kCannotOpenFileErr))
 807        m_Session->SetFailed();
 808      m_Session->AppendErrorAndStatusMessage (e.what()) ;
 809    }
 810  
 811    return result;
 812  }
 813  
 814  bool VirtualFrontEnd::Pause()
 815  {
 816    try
 817    {
 818      switch(state)
 819      {
 820        case kParsing:
 821          renderFrontend.PauseParser(sceneId);
 822          state = kPausedParsing;
 823          return true;
 824  
 825        case kPreSceneShellout:
 826        case kPreFrameShellout:
 827        case kPostFrameShellout:
 828          m_PauseRequested = true;
 829          return true;
 830  
 831        case kRendering:
 832          renderFrontend.PauseRender(viewId);
 833          state = kPausedRendering;
 834          return true;
 835  
 836        default:
 837          break;
 838      }
 839    }
 840    catch (pov_base::Exception&)
 841    {
 842      return false;
 843    }
 844    return false;
 845  }
 846  
 847  bool VirtualFrontEnd::Resume()
 848  {
 849    try
 850    {
 851      switch(state)
 852      {
 853        case kPostShelloutPause:
 854          state = m_PostPauseState;
 855          return true;
 856  
 857        case kPausedParsing:
 858          if (renderFrontend.GetSceneState(sceneId) == SceneData::Scene_Paused)
 859            renderFrontend.ResumeParser(sceneId);
 860          state = kParsing;
 861          return true;
 862  
 863        case kPausedRendering:
 864          if (m_PausedAfterFrame)
 865          {
 866            state = kStarting;
 867            m_PausedAfterFrame = false;
 868            return true;
 869          }
 870          if (renderFrontend.GetViewState(viewId) == ViewData::View_Paused)
 871            renderFrontend.ResumeRender(viewId);
 872          state = kRendering;
 873          return true;
 874  
 875        default:
 876          break;
 877      }
 878    }
 879    catch (pov_base::Exception&)
 880    {
 881      return (false) ;
 882    }
 883    return false;
 884  }
 885  
 886  bool VirtualFrontEnd::HandleShelloutCancel()
 887  {
 888    if (!shelloutProcessing->RenderCancelled())
 889      return false;
 890  
 891    int code(shelloutProcessing->ExitCode());
 892    string str(shelloutProcessing->GetCancelMessage());
 893    if (code)
 894    {
 895      state = kFailed;
 896      m_Session->SetFailed();
 897    }
 898    else
 899    {
 900      state = kStopped;
 901      m_Session->SetSucceeded(true);
 902    }
 903    m_Session->AppendErrorMessage(str) ;
 904    m_Session->AppendStatusMessage(str) ;
 905    return true;
 906  }
 907  
 908  State VirtualFrontEnd::Process()
 909  {
 910    if (state == kReady)
 911      return kReady;
 912  
 913    switch(state)
 914    {
 915      case kStarting:
 916        try
 917        {
 918          m_Session->SetSucceeded (false);
 919          if (animationProcessing != NULL)
 920          {
 921            shelloutProcessing->SetFrameClock(animationProcessing->GetFrameNumber(), animationProcessing->GetClockValue());
 922            if (shelloutProcessing->SkipNextFrame() == false)
 923            {
 924              UCS2String filename;
 925              int frame = animationProcessing->GetFrameNumber() - animationProcessing->GetStartFrame() ;
 926              options = animationProcessing->GetFrameRenderOptions ();
 927              if (m_Session->OutputToFileSet())
 928              {
 929                filename = imageProcessing->GetOutputFilename (options, animationProcessing->GetFrameNumber(), animationProcessing->GetFrameNumberDigits());
 930                options.SetUCS2String (kPOVAttrib_OutputFile, filename.c_str());
 931  
 932                // test access permission now to avoid surprise later after waiting for
 933                // the render to complete.
 934                if (m_Session->TestAccessAllowed(filename, true) == false)
 935                {
 936                  string str ("IO Restrictions prohibit write access to '") ;
 937                  str += UCS2toASCIIString(filename);
 938                  str += "'";
 939                  throw POV_EXCEPTION(kCannotOpenFileErr, str);
 940                }
 941                shelloutProcessing->SetOutputFile(UCS2toASCIIString(filename));
 942                m_Session->AdviseOutputFilename (filename);
 943              }
 944              m_Session->AppendAnimationStatus (frame + 1, animationProcessing->GetTotalFrames(), filename) ;
 945            }
 946          }
 947  
 948          bool hadPreScene = shelloutProcessing->HadPreScene();
 949  
 950          // will do pre-scene instead if it hasn't yet been done
 951          try
 952          {
 953            shelloutProcessing->ProcessEvent(ShelloutProcessing::preFrame);
 954          }
 955          catch (pov_base::Exception& e)
 956          {
 957            // if it's a kCannotOpenFileErr, it means permission to run the process was denied
 958            // we don't set failed in that case as we allow shelloutprocessing to handle it
 959            m_Session->AppendErrorAndStatusMessage (e.what()) ;
 960            if (!e.codevalid() || (e.code() != kCannotOpenFileErr))
 961            {
 962              m_Session->SetFailed();
 963              return state = kFailed;
 964            }
 965          }
 966  
 967          // returns true if cancel has been requested
 968          if (HandleShelloutCancel())
 969            return state;
 970  
 971          state = hadPreScene ? kPreFrameShellout : kPreSceneShellout;
 972          return state;
 973        }
 974        catch(pov_base::Exception& e)
 975        {
 976          m_Session->SetFailed();
 977          m_Session->AppendErrorAndStatusMessage (e.what()) ;
 978          return state = kFailed;
 979        }
 980  
 981      case kPreSceneShellout:
 982        if (shelloutProcessing->ShelloutRunning() || HandleShelloutCancel())
 983          return state;
 984  
 985        // if a pause was requested by the user whilst the shellout was still running, do it now
 986        if (m_PauseRequested)
 987        {
 988          m_PostPauseState = kStarting;
 989          m_PauseRequested = false;
 990          return state = kPostShelloutPause;
 991        }
 992  
 993        // go back to kStarting; it won't run pre-scene again
 994        return state = kStarting;
 995  
 996      case kPreFrameShellout:
 997        if (shelloutProcessing->ShelloutRunning() || HandleShelloutCancel())
 998          return state;
 999  
1000        if (shelloutProcessing->SkipNextFrame())
1001        {
1002          string str(shelloutProcessing->GetSkipMessage());
1003          m_Session->AppendStatusMessage (str) ;
1004          m_Session->AppendStreamMessage (vfeSession::mInformation, str.c_str()) ;
1005          if ((animationProcessing != NULL) && (animationProcessing->MoreFrames() == true))
1006          {
1007            animationProcessing->ComputeNextFrame();
1008            m_Session->SetPixelsRendered(0, m_Session->GetTotalPixels());
1009            m_Session->SetPercentComplete(0);
1010            if (m_PauseRequested)
1011            {
1012              m_PostPauseState = kStarting;
1013              m_PauseRequested = false;
1014              return state = kPostShelloutPause;
1015            }
1016            return state = kStarting;
1017          }
1018          else
1019          {
1020            m_Session->SetSucceeded (true);
1021            if (m_PauseRequested)
1022            {
1023              m_PostPauseState = kStopped;
1024              m_PauseRequested = false;
1025              return state = kPostShelloutPause;
1026            }
1027            return state = kStopped;
1028          }
1029        }
1030  
1031        // now set up the scene in preparation for parsing, then start the parser
1032        try { sceneId = renderFrontend.CreateScene(backendAddress, options, boost::bind(&vfe::VirtualFrontEnd::CreateConsole, this)); }
1033        catch(pov_base::Exception& e)
1034        {
1035          m_Session->SetFailed();
1036          m_Session->AppendErrorMessage (e.what()) ;
1037          m_Session->AppendStatusMessage (e.what()) ;
1038          return state = kFailed;
1039        }
1040        try { renderFrontend.StartParser(sceneId, options); }
1041        catch(pov_base::Exception& e)
1042        {
1043          m_Session->SetFailed();
1044          m_Session->AppendErrorMessage (e.what()) ;
1045          m_Session->AppendStatusMessage (e.what()) ;
1046          return state = kFailed;
1047        }
1048        if (m_PauseRequested)
1049        {
1050          m_PostPauseState = kParsing;
1051          m_PauseRequested = false;
1052          return state = kPostShelloutPause;
1053        }
1054        return state = kParsing;
1055  
1056      case kParsing:
1057      case kPausedParsing:
1058        switch(renderFrontend.GetSceneState(sceneId))
1059        {
1060          case SceneData::Scene_Paused:
1061            return state = kPausedParsing;
1062  
1063          case SceneData::Scene_Failed:
1064            m_Session->SetFailed();
1065            return state = kStopped;
1066  
1067          case SceneData::Scene_Stopping:
1068            return state = kStopping;
1069  
1070          case SceneData::Scene_Ready:
1071            if (state == kPausedParsing)
1072            {
1073              // it's possible for the parser to transition to Scene_Ready after a successful pause request.
1074              // this typically happens if the request comes in very close to the end of a parse, since the
1075              // task thread only checks the pause state intermittently. we don't start the renderer in this
1076              // case.
1077              return state;
1078            }
1079            try { viewId = renderFrontend.CreateView(sceneId, options, imageProcessing, boost::bind(&vfe::VirtualFrontEnd::CreateDisplay, this, _1, _2, _3)); }
1080            catch(pov_base::Exception& e)
1081            {
1082              m_Session->SetFailed();
1083              m_Session->AppendErrorMessage (e.what()) ;
1084              m_Session->AppendStatusMessage (e.what()) ;
1085              return state = kFailed;
1086            }
1087            try { renderFrontend.StartRender(viewId, options); }
1088            catch(pov_base::Exception& e)
1089            {
1090              m_Session->ClearStatusMessages();
1091              if (e.codevalid() && e.code() == kImageAlreadyRenderedErr)
1092              {
1093                // this is not a failure; continue has been requested and
1094                // the file has already been rendered, so we skip it.
1095                m_Session->AppendStatusMessage ("File already rendered and continue requested; skipping.") ;
1096                m_Session->AppendStreamMessage (vfeSession::mInformation, "File already rendered and continue requested; skipping.") ;
1097  
1098                /* [JG] the block here is a duplicate of actions done after
1099                 * the post frame shellout (that won't be reached because
1100                 * the image was already there). 
1101                 */
1102                try { renderFrontend.CloseView(viewId); }
1103                catch (pov_base::Exception&) { /* Ignore any error here! */ }
1104                try { renderFrontend.CloseScene(sceneId); }
1105                catch (pov_base::Exception&) { /* Ignore any error here! */ }
1106  
1107                if ((animationProcessing != NULL) && (animationProcessing->MoreFrames() == true))
1108                {
1109                  animationProcessing->ComputeNextFrame();
1110                  m_Session->SetPixelsRendered(0, m_Session->GetTotalPixels());
1111                  m_Session->SetPercentComplete(0);
1112                  return state = kStarting;
1113                }
1114                else
1115                {
1116                  m_Session->SetSucceeded (true);
1117                  return state = kStopped;
1118                }
1119              }
1120  
1121              m_Session->SetFailed();
1122              m_Session->AppendErrorMessage (e.what()) ;
1123              m_Session->AppendStatusMessage (e.what()) ;
1124              return state = kFailed;
1125            }
1126  
1127            // now we display the render window, if enabled
1128            shared_ptr<Display> display(GetDisplay());
1129            if (display != NULL)
1130            {
1131              vfeDisplay *disp = dynamic_cast<vfeDisplay *>(display.get());
1132              if (disp != NULL)
1133                disp->Show () ;
1134            }
1135            return state = kRendering;
1136        }
1137        return kParsing;
1138  
1139      case kRendering:
1140      case kPausedRendering:
1141        switch(renderFrontend.GetViewState(viewId))
1142        {
1143          case ViewData::View_Paused:
1144            return state = kPausedRendering;
1145  
1146          case ViewData::View_Failed:
1147            m_Session->SetFailed();
1148            return state = kStopped;
1149  
1150          case ViewData::View_Stopping:
1151            return state = kStopping;
1152  
1153          case ViewData::View_Rendered:
1154            if (state == kPausedRendering)
1155            {
1156              // it's possible for the renderer to transition to View_Rendered after a successful pause request.
1157              return kPausedRendering;
1158            }
1159            try
1160            {
1161              if (animationProcessing != NULL)
1162              {
1163                if (m_Session->OutputToFileSet())
1164                  m_Session->AdviseOutputFilename (imageProcessing->WriteImage(options, animationProcessing->GetFrameNumber(), animationProcessing->GetFrameNumberDigits()));
1165                m_Session->AdviseFrameCompleted();
1166              }
1167              else
1168                if (m_Session->OutputToFileSet())
1169                  m_Session->AdviseOutputFilename (imageProcessing->WriteImage(options));
1170            }
1171            catch (pov_base::Exception& e)
1172            {
1173              m_Session->SetFailed();
1174              m_Session->AppendErrorMessage (e.what()) ;
1175              m_Session->AppendStatusMessage (e.what()) ;
1176              // TODO: perhaps we should allow them to pause the queue/insert render
1177              //       here if need be.
1178              return state = kFailed;
1179            }
1180            try
1181            {
1182              shelloutProcessing->ProcessEvent(ShelloutProcessing::postFrame);
1183            }
1184            catch (pov_base::Exception& e)
1185            {
1186              // if it's a kCannotOpenFileErr, it means permission to run the process was denied
1187              // we don't set failed in that case as we allow shelloutprocessing to handle it
1188              m_Session->AppendErrorAndStatusMessage (e.what());
1189              if (!e.codevalid() || (e.code() != kCannotOpenFileErr))
1190              {
1191                m_Session->SetFailed();
1192                return state = kFailed;
1193              }
1194            }
1195  
1196            // check for cancel here: if the return value is true, state has already been changed
1197            if (HandleShelloutCancel())
1198              return state;
1199  
1200            return state = kPostFrameShellout;
1201  
1202          default:
1203            break;
1204        }
1205        return kRendering;
1206  
1207      case kPostFrameShellout:
1208        if (shelloutProcessing->ShelloutRunning() || HandleShelloutCancel())
1209          return state;
1210        if ((animationProcessing == NULL) || animationProcessing->MoreFrames() == false)
1211        {
1212          m_Session->SetSucceeded (true);
1213          if (m_PauseRequested)
1214          {
1215            m_PostPauseState = kStopped;
1216            m_PauseRequested = false;
1217            return state = kPostShelloutPause;
1218          }
1219          return state = kStopped;
1220        }
1221        if (shelloutProcessing->SkipAllFrames())
1222        {
1223          string str(shelloutProcessing->GetSkipMessage());
1224          m_Session->SetSucceeded (true);
1225          m_Session->AppendStatusMessage (str) ;
1226          m_Session->AppendStreamMessage (vfeSession::mInformation, str.c_str()) ;
1227          if (m_PauseRequested)
1228          {
1229            m_PostPauseState = kStopped;
1230            m_PauseRequested = false;
1231            return state = kPostShelloutPause;
1232          }
1233          return state = kStopped;
1234        }
1235        /* [JG] the actions hereafter should be also done 
1236         * when the image already existed: tidy up the data before next frame or stop
1237         */
1238        try { renderFrontend.CloseView(viewId); }
1239        catch (pov_base::Exception&) { /* Ignore any error here! */ }
1240        try { renderFrontend.CloseScene(sceneId); }
1241        catch (pov_base::Exception&) { /* Ignore any error here! */ }
1242        animationProcessing->ComputeNextFrame();
1243        if (m_Session->GetPauseWhenDone())
1244        {
1245          // wait for a manual continue
1246          m_PausedAfterFrame = true;
1247          m_PauseRequested = false;
1248          return state = kPausedRendering;
1249        }
1250        if (m_PauseRequested)
1251        {
1252          m_PostPauseState = kStarting;
1253          m_PauseRequested = false;
1254          return state = kPostShelloutPause;
1255        }
1256        return state = kStarting;
1257  
1258      case kPostSceneShellout:
1259        if (shelloutProcessing->ShelloutRunning())
1260          return state;
1261        return state = kDone;
1262  
1263      case kPostShelloutPause:
1264        break;
1265  
1266      case kStopping:
1267        if (renderFrontend.GetSceneState(sceneId) == SceneData::Scene_Ready || renderFrontend.GetSceneState(sceneId) == SceneData::Scene_Failed)
1268          return state = kStopped;
1269        if (renderFrontend.GetViewState(viewId) == ViewData::View_Rendered || renderFrontend.GetViewState(viewId) == ViewData::View_Failed)
1270          return state = kStopped;
1271        return kStopping;
1272  
1273      case kFailed:
1274        m_Session->SetFailed();
1275        // ShelloutProcessing ignores the fatal error event if it requested a cancel
1276        try { shelloutProcessing->ProcessEvent(ShelloutProcessing::fatalError); }
1277        catch (pov_base::Exception&) { /* Ignore any error here */ }
1278        return state = kStopped;
1279  
1280      case kStopped:
1281        try { renderFrontend.CloseView(viewId); }
1282        catch (pov_base::Exception&) { /* Ignore any error here! */ }
1283        try { renderFrontend.CloseScene(sceneId); }
1284        catch (pov_base::Exception&) { /* Ignore any error here! */ }
1285        animationProcessing.reset();
1286        imageProcessing.reset();
1287  
1288        // we only run the post-scene or failed action if we have passed the pre-scene point
1289        // i.e. if we stop before pre-scene, we don't run post-scene
1290        if (shelloutProcessing->HadPreScene())
1291        {
1292          if (m_Session->Failed())
1293          {
1294            // it is possible for us to get to kStopped without going through kFailed
1295            // so we call the failed shellout event here just in case: ShelloutProcessing
1296            // can handle being called twice for the same event.
1297            try { shelloutProcessing->ProcessEvent(ShelloutProcessing::fatalError); }
1298            catch (pov_base::Exception&) { /* Ignore any error here */ }
1299          }
1300  
1301          if (!shelloutProcessing->HadPostScene())
1302          {
1303            try { shelloutProcessing->ProcessEvent(ShelloutProcessing::postScene); }
1304            catch (pov_base::Exception&) { /* Ignore any error here! */ }
1305            return state = kPostSceneShellout;
1306          }
1307        }
1308  
1309        return state = kDone;
1310  
1311      case kDone:
1312        return state = kReady;
1313    }
1314  
1315    return state;
1316  }
1317  
1318  void VirtualFrontEnd::SetResultPointers(Console **cr, Image **ir, Display **dr)
1319  {
1320    consoleResult = cr;
1321    displayResult = dr;
1322  }
1323  
1324  bool VirtualFrontEnd::IsPausable (void)
1325  {
1326    switch (GetState ())
1327    {
1328      case kParsing :
1329      case kPausedParsing :
1330      case kRendering :
1331      case kPausedRendering :
1332      case kPreSceneShellout:
1333      case kPreFrameShellout:
1334      case kPostFrameShellout:
1335      case kPostShelloutPause:
1336           return (true) ;
1337  
1338      default :
1339           return (false) ;
1340    }
1341  }
1342  
1343  bool VirtualFrontEnd::Paused (void)
1344  {
1345    switch (GetState ())
1346    {
1347      case kPausedParsing :
1348      case kPausedRendering :
1349      case kPostShelloutPause:
1350           return (true) ;
1351  
1352      case kPreSceneShellout:
1353      case kPreFrameShellout:
1354      case kPostFrameShellout:
1355           return m_PauseRequested;
1356  
1357      default :
1358           return (false) ;
1359    }
1360  }
1361  
1362  ////////////////////////////////////////////////////////////////////////////////////////
1363  //
1364  // helper funtions
1365  //
1366  ////////////////////////////////////////////////////////////////////////////////////////
1367  
1368  int Allow_File_Write (const char *Filename, const unsigned int FileType)
1369  {
1370    if (strcmp(Filename, "stdout") == 0 || strcmp(Filename, "stderr") == 0)
1371      return true;
1372    return (vfeSession::GetSessionFromThreadID()->TestAccessAllowed(Filename, true));
1373  }
1374  
1375  int Allow_File_Write (const unsigned short *Filename, const unsigned int FileType)
1376  {
1377    if (strcmp(UCS2toASCIIString(Filename).c_str(), "stdout") == 0 || strcmp(UCS2toASCIIString(Filename).c_str(), "stderr") == 0)
1378      return true;
1379    return (vfeSession::GetSessionFromThreadID()->TestAccessAllowed(Filename, true));
1380  }
1381  
1382  int Allow_File_Read (const char *Filename, const unsigned int FileType)
1383  {
1384    return (vfeSession::GetSessionFromThreadID()->TestAccessAllowed(Filename, false));
1385  }
1386  
1387  int Allow_File_Read (const unsigned short *Filename, const unsigned int FileType)
1388  {
1389    return (vfeSession::GetSessionFromThreadID()->TestAccessAllowed(Filename, false));
1390  }
1391  
1392  FILE *vfeFOpen (const std::basic_string<unsigned short>& name, const char *mode)
1393  {
1394    return (fopen (UCS2toASCIIString (name).c_str(), mode)) ;
1395  }
1396  
1397  bool vfeRemove(const UCS2String& Filename)
1398  {
1399    return (DELETE_FILE (UCS2toASCIIString (Filename).c_str()) == 0);
1400  }
1401  
1402  }
1403