povray/vfe/unix/unixconsole.cpp
Campbell Barton b3846f5723 quiet compiler warnings building with gcc and clang.
- define variables and functions as static when not declared by headers.
- remove some unused defines.
- other minor changes to quiet warnings.
2013-11-17 07:34:00 +11:00

556 lines
15 KiB
C++

/*******************************************************************************
* unixconsole.cpp
*
* Adapted from vfe/win/console/winconsole.cpp
*
* ---------------------------------------------------------------------------
* Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
* Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
*
* POV-Ray is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* POV-Ray 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ---------------------------------------------------------------------------
* POV-Ray is based on the popular DKB raytracer version 2.12.
* DKBTrace was originally written by David K. Buck.
* DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
* ---------------------------------------------------------------------------
* $File: //depot/public/povray/3.x/vfe/unix/unixconsole.cpp $
* $Revision: #1 $
* $Change: 6069 $
* $DateTime: 2013/11/06 11:59:40 $
* $Author: chrisc $
*******************************************************************************/
#include <signal.h>
#include <sys/select.h>
#include <termios.h>
#include <unistd.h>
#include <boost/shared_ptr.hpp>
#include "vfe.h"
#include "backend/control/benchmark.h"
#include "povray.h"
#include "disp.h"
#include "disp_text.h"
#include "disp_sdl.h"
namespace pov_frontend
{
boost::shared_ptr<Display> gDisplay;
}
using namespace vfe;
using namespace vfePlatform;
enum DispMode
{
DISP_MODE_NONE,
DISP_MODE_TEXT,
DISP_MODE_SDL
};
static DispMode gDisplayMode;
enum ReturnValue
{
RETURN_OK=0,
RETURN_ERROR,
RETURN_USER_ABORT
};
static bool gCancelRender = false;
// for handling asynchronous (external) signals
static int gSignalNumber = 0;
static boost::mutex gSignalMutex;
static void SignalHandler (void)
{
sigset_t sigset;
int signum;
while(true)
{
sigfillset(&sigset);
sigwait(&sigset, &signum); // wait till a signal is caught
boost::mutex::scoped_lock lock(gSignalMutex);
gSignalNumber = signum;
}
}
static void ProcessSignal (void)
{
boost::mutex::scoped_lock lock(gSignalMutex);
switch (gSignalNumber)
{
case 0:
break;
#ifdef SIGQUIT
case SIGQUIT:
fprintf(stderr, "\n%s: received signal SIGQUIT: Quit; requested render cancel\n", PACKAGE);
gCancelRender = true;
break;
#endif
#ifdef SIGTERM
case SIGTERM:
fprintf(stderr, "\n%s: received signal SIGTERM: Termination; requested render cancel\n", PACKAGE);
gCancelRender = true;
break;
#endif
#ifdef SIGINT
case SIGINT:
fprintf(stderr, "\n%s: received signal SIGINT: Interrupt; requested render cancel\n", PACKAGE);
gCancelRender = true;
break;
#endif
#ifdef SIGPIPE
case SIGPIPE:
fprintf(stderr, "\n%s: received signal SIGPIPE: Broken pipe; requested render cancel\n", PACKAGE);
gCancelRender = true;
break;
#endif
case SIGCHLD:
// for now, ignore this (side-effect of the shell-out code).
// once properly implemented, the shell-out code would want to know this has happened, though.
break;
default:
// fprintf(stderr, "\n%s: received signal %d\n", PACKAGE, gSignalNumber);
break;
}
gSignalNumber = 0;
}
static vfeDisplay *UnixDisplayCreator (unsigned int width, unsigned int height, GammaCurvePtr gamma, vfeSession *session, bool visible)
{
UnixDisplay *display = GetRenderWindow () ;
switch (gDisplayMode)
{
#ifdef HAVE_LIBSDL
case DISP_MODE_SDL:
if (display != NULL && display->GetWidth() == width && display->GetHeight() == height)
{
UnixDisplay *p = new UnixSDLDisplay (width, height, gamma, session, false) ;
if (p->TakeOver (display))
return p;
delete p;
}
return new UnixSDLDisplay (width, height, gamma, session, visible) ;
break;
#endif
case DISP_MODE_TEXT:
return new UnixTextDisplay (width, height, gamma, session, visible) ;
break;
default:
return NULL;
}
}
static void PrintStatus (vfeSession *session)
{
string str;
vfeSession::MessageType type;
static vfeSession::MessageType lastType = vfeSession::mUnclassified;
while (session->GetNextCombinedMessage (type, str))
{
if (type != vfeSession::mGenericStatus)
{
if (lastType == vfeSession::mGenericStatus)
fprintf (stderr, "\n") ;
fprintf (stderr, "%s\n", str.c_str());
}
else
fprintf (stderr, "%s\r", str.c_str());
lastType = type;
}
}
static void PrintStatusChanged (vfeSession *session, State force = kUnknown)
{
if (force == kUnknown)
force = session->GetBackendState();
switch (force)
{
case kParsing:
fprintf (stderr, "==== [Parsing...] ==========================================================\n");
break;
case kRendering:
fprintf (stderr, "==== [Rendering...] ========================================================\n");
break;
case kPausedRendering:
fprintf (stderr, "==== [Paused] ==============================================================\n");
break;
}
}
static void PrintVersion(void)
{
fprintf(stderr,
"%s %s\n\n"
"%s\n%s\n%s\n"
"%s\n%s\n%s\n\n",
PACKAGE_NAME, POV_RAY_VERSION,
DISTRIBUTION_MESSAGE_1, DISTRIBUTION_MESSAGE_2, DISTRIBUTION_MESSAGE_3,
POV_RAY_COPYRIGHT, DISCLAIMER_MESSAGE_1, DISCLAIMER_MESSAGE_2
);
fprintf(stderr,
"Built-in features:\n"
" I/O restrictions: %s\n"
" X Window display: %s\n"
" Supported image formats: %s\n"
" Unsupported image formats: %s\n\n",
BUILTIN_IO_RESTRICTIONS, BUILTIN_XWIN_DISPLAY, BUILTIN_IMG_FORMATS, MISSING_IMG_FORMATS
);
fprintf(stderr,
"Compilation settings:\n"
" Build architecture: %s\n"
" Built/Optimized for: %s\n"
" Compiler vendor: %s\n"
" Compiler version: %s\n"
" Compiler flags: %s\n",
BUILD_ARCH, BUILT_FOR, COMPILER_VENDOR, COMPILER_VERSION, CXXFLAGS
);
}
static void ErrorExit(vfeSession *session)
{
fprintf(stderr, "%s\n", session->GetErrorString());
session->Shutdown();
delete session;
exit(RETURN_ERROR);
}
static void CancelRender(vfeSession *session)
{
session->CancelRender(); // request the backend to cancel
PrintStatus (session);
while (session->GetBackendState() != kReady) // wait for the render to effectively shut down
Delay(10);
PrintStatus (session);
}
static void PauseWhenDone(vfeSession *session)
{
GetRenderWindow()->UpdateScreen(true);
GetRenderWindow()->PauseWhenDoneNotifyStart();
while (GetRenderWindow()->PauseWhenDoneResumeIsRequested() == false)
{
ProcessSignal();
if (gCancelRender)
break;
else
Delay(10);
}
GetRenderWindow()->PauseWhenDoneNotifyEnd();
}
static ReturnValue PrepareBenchmark(vfeSession *session, vfeRenderOptions& opts, string& ini, string& pov, int argc, char **argv)
{
// parse command-line options
while (*++argv)
{
string s = string(*argv);
boost::to_lower(s);
// set number of threads to run the benchmark
if (boost::starts_with(s, "+wt") || boost::starts_with(s, "-wt"))
{
s.erase(0, 3);
int n = atoi(s.c_str());
if (n)
opts.SetThreadCount(n);
else
fprintf(stderr, "%s: ignoring malformed '%s' command-line option\n", PACKAGE, *argv);
}
// add library path
else if (boost::starts_with(s, "+l") || boost::starts_with(s, "-l"))
{
s.erase(0, 2);
opts.AddLibraryPath(s);
}
}
int benchversion = pov::Get_Benchmark_Version();
fprintf(stderr, "\
%s %s%s\n\n\
Entering the standard POV-Ray %s benchmark version %x.%02x.\n\n\
This built-in benchmark requires POV-Ray to be installed on your system\n\
before running it. There will be neither display nor file output, and\n\
any additional command-line option except setting the number of render\n\
threads (+wtN for N threads) and library paths (+Lpath) will be ignored.\n\
To get an accurate benchmark result you might consider running POV-Ray\n\
with the Unix 'time' command (e.g. 'time povray -benchmark').\n\n\
The benchmark will run using %d render thread(s).\n\
Press <Enter> to continue or <Ctrl-C> to abort.\n\
",
PACKAGE_NAME, POV_RAY_VERSION, COMPILER_VER,
VERSION_BASE, benchversion / 256, benchversion % 256,
opts.GetThreadCount()
);
// wait for user input from stdin (including abort signals)
while (true)
{
ProcessSignal();
if (gCancelRender)
{
fprintf(stderr, "Render cancelled by user\n");
return RETURN_USER_ABORT;
}
fd_set readset;
struct timeval tv = {0,0}; // no timeout
FD_ZERO(&readset);
FD_SET(STDIN_FILENO, &readset);
if (select(STDIN_FILENO+1, &readset, NULL, NULL, &tv) < 0)
break;
if (FD_ISSET(STDIN_FILENO, &readset)) // user input is available
{
char s[3];
read(STDIN_FILENO, s, 1); // read till <ENTER> is hit
tcflush(STDIN_FILENO, TCIFLUSH); // discard unread data
break;
}
Delay(20);
}
string basename = UCS2toASCIIString(session->CreateTemporaryFile());
ini = basename + ".ini";
pov = basename + ".pov";
if (pov::Write_Benchmark_File(pov.c_str(), ini.c_str()))
{
fprintf(stderr, "%s: creating %s\n", PACKAGE, ini.c_str());
fprintf(stderr, "%s: creating %s\n", PACKAGE, pov.c_str());
fprintf(stderr, "Running standard POV-Ray benchmark version %x.%02x\n", benchversion / 256, benchversion % 256);
}
else
{
fprintf(stderr, "%s: failed to write temporary files for benchmark\n", PACKAGE);
return RETURN_ERROR;
}
return RETURN_OK;
}
static void CleanupBenchmark(vfeUnixSession *session, string& ini, string& pov)
{
fprintf(stderr, "%s: removing %s\n", PACKAGE, ini.c_str());
session->DeleteTemporaryFile(ASCIItoUCS2String(ini.c_str()));
fprintf(stderr, "%s: removing %s\n", PACKAGE, pov.c_str());
session->DeleteTemporaryFile(ASCIItoUCS2String(pov.c_str()));
}
int main (int argc, char **argv)
{
vfeUnixSession *session;
vfeStatusFlags flags;
vfeRenderOptions opts;
ReturnValue retval = RETURN_OK;
bool running_benchmark = false;
string bench_ini_name;
string bench_pov_name;
sigset_t sigset;
boost::thread *sigthread;
char ** argv_copy=argv; /* because argv is updated later */
int argc_copy=argc; /* because it might also be updated */
/*fprintf(stderr, "%s: This is a RELEASE CANDIDATE version of POV-Ray. General distribution is discouraged.\n", PACKAGE);*/
// block some signals for this thread as well as those created afterwards
sigemptyset(&sigset);
#ifdef SIGQUIT
sigaddset(&sigset, SIGQUIT);
#endif
#ifdef SIGTERM
sigaddset(&sigset, SIGTERM);
#endif
#ifdef SIGINT
sigaddset(&sigset, SIGINT);
#endif
#ifdef SIGPIPE
sigaddset(&sigset, SIGPIPE);
#endif
#ifdef SIGCHLD
sigaddset(&sigset, SIGCHLD);
#endif
pthread_sigmask(SIG_BLOCK, &sigset, NULL);
// create the signal handling thread
sigthread = new boost::thread(SignalHandler);
session = new vfeUnixSession();
if (session->Initialize(NULL, NULL) != vfeNoError)
ErrorExit(session);
// display mode registration
#ifdef HAVE_LIBSDL
if (UnixSDLDisplay::Register(session))
gDisplayMode = DISP_MODE_SDL;
else
#endif
if (UnixTextDisplay::Register(session))
gDisplayMode = DISP_MODE_TEXT;
else
gDisplayMode = DISP_MODE_NONE;
// default number of work threads: number of CPUs or 4
int nthreads = 1;
#ifdef _SC_NPROCESSORS_ONLN // online processors
nthreads = sysconf(_SC_NPROCESSORS_ONLN);
#endif
#ifdef _SC_NPROCESSORS_CONF // configured processors
if (nthreads < 2)
nthreads = sysconf(_SC_NPROCESSORS_CONF);
#endif
if (nthreads < 2)
nthreads = 4;
opts.SetThreadCount(nthreads);
// process command-line options
session->GetUnixOptions()->ProcessOptions(&argc, &argv);
if (session->GetUnixOptions()->isOptionSet("general", "help"))
{
session->Shutdown() ;
PrintStatus (session) ;
// TODO: general usage display (not yet in core code)
session->GetUnixOptions()->PrintOptions();
delete sigthread;
delete session;
return RETURN_OK;
}
else if (session->GetUnixOptions()->isOptionSet("general", "version"))
{
session->Shutdown() ;
PrintVersion();
delete sigthread;
delete session;
return RETURN_OK;
}
else if (session->GetUnixOptions()->isOptionSet("general", "benchmark"))
{
retval = PrepareBenchmark(session, opts, bench_ini_name, bench_pov_name, argc, argv);
if (retval == RETURN_OK)
running_benchmark = true;
else
{
session->Shutdown();
delete sigthread;
delete session;
return retval;
}
}
// process INI settings
if (running_benchmark)
{
// read only the provided INI file and set minimal lib paths
opts.AddLibraryPath(string(POVLIBDIR "/include"));
opts.AddINI(bench_ini_name.c_str());
opts.SetSourceFile(bench_pov_name.c_str());
}
else
{
char *s = getenv ("POVINC");
session->SetDisplayCreator(UnixDisplayCreator);
session->GetUnixOptions()->Process_povray_ini(opts);
if (s != NULL)
opts.AddLibraryPath (s);
while (*++argv)
opts.AddCommand (*argv);
}
// set all options and start rendering
if (session->SetOptions(opts) != vfeNoError)
{
fprintf(stderr,"\nProblem with option setting\n");
for(int loony=0;loony<argc_copy;loony++)
{
fprintf(stderr,"%s%c",argv_copy[loony],loony+1<argc_copy?' ':'\n');
}
ErrorExit(session);
}
if (session->StartRender() != vfeNoError)
ErrorExit(session);
// set inter-frame pause for animation
if (session->RenderingAnimation() && session->GetBoolOption("Pause_When_Done", false))
session->PauseWhenDone(true);
// main render loop
session->SetEventMask(stBackendStateChanged); // immediatly notify this event
while (((flags = session->GetStatus(true, 200)) & stRenderShutdown) == 0)
{
ProcessSignal();
if (gCancelRender)
{
CancelRender(session);
break;
}
if (flags & stAnimationStatus)
fprintf(stderr, "\nRendering frame %d of %d\n", session->GetCurrentFrame(), session->GetTotalFrames());
if (flags & stAnyMessage)
PrintStatus (session);
if (flags & stBackendStateChanged)
PrintStatusChanged (session);
if (GetRenderWindow() != NULL)
{
// early exit
if (GetRenderWindow()->HandleEvents())
{
gCancelRender = true; // will set proper return value
CancelRender(session);
break;
}
GetRenderWindow()->UpdateScreen();
// inter-frame pause
if (session->GetCurrentFrame() < session->GetTotalFrames()
&& session->GetPauseWhenDone()
&& (flags & stAnimationFrameCompleted) != 0
&& session->Failed() == false)
{
PauseWhenDone(session);
if (! gCancelRender)
session->Resume();
}
}
}
// pause when done for single or last frame of an animation
if (session->Failed() == false && GetRenderWindow() != NULL && session->GetBoolOption("Pause_When_Done", false))
{
PrintStatusChanged(session, kPausedRendering);
PauseWhenDone(session);
gCancelRender = false;
}
if (running_benchmark)
CleanupBenchmark(session, bench_ini_name, bench_pov_name);
if (session->Succeeded() == false)
retval = gCancelRender ? RETURN_USER_ABORT : RETURN_ERROR;
session->Shutdown();
PrintStatus (session);
delete sigthread;
delete session;
return retval;
}