/*******************************************************************************
* pvengine.cpp
*
* This file implements Windows specific routines, WinMain, and message loops.
*
* Primary author: Christopher J. Cason.
*
* ---------------------------------------------------------------------------
* 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 .
* ---------------------------------------------------------------------------
* 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/windows/pvengine.cpp $
* $Revision: #1 $
* $Change: 6069 $
* $DateTime: 2013/11/06 11:59:40 $
* $Author: chrisc $
*******************************************************************************/
/*****************************************************************************/
/* NOTICE */
/* */
/* Much of this source code (all of pvengine.exe) should be considered on */
/* its last legs. The base code was originally written back in early 1995 */
/* (with some of the code even predating that), and targeted at Win32s under */
/* Windows 3.1. An additional constraint was a desire for the code to be */
/* able to be compiled on a DEC Alpha under NT for the Alpha using only a */
/* 'C' compiler, and for it to be able to run with or without the editor. */
/* */
/* With the introduction of VFE in version 3.7, creating a new UI for POVWIN */
/* should be a lot less painful as almost all of the bindings with the core */
/* POV-Ray renderer have been abstracted into the above class library. */
/* */
/*****************************************************************************/
#define POVWIN_FILE
#define _WIN32_IE COMMONCTRL_VERSION
#if !defined( __BORLANDC__ ) && !defined( __DMC__ ) && !defined( __MINGW32__ )
#pragma comment(lib, "htmlhelp")
#pragma comment(lib, "winmm")
#endif
#define _CRT_RAND_S
#define PSAPI_VERSION 1
#pragma comment(lib, "psapi")
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// #define DEVELOPMENT
#include
#include
#include
#include "pvengine.h"
#include "resource.h"
#include "pvdialog.h"
#include "pvguiext.h"
#include "pvedit.h"
#include "backend/control/benchmark.h"
#include "pvdisplay.h"
#include "syspovprotofrontend.h"
#include "povray.h"
#ifdef RTR_SUPPORT
#include "rtrsupport.h"
#endif
#if defined(USE_AVX_FMA4_FOR_NOISE)
#include "backend/texture/avxfma4check.h"
#endif
// this must be the last file included
#include "syspovdebug.h"
typedef HRESULT (CALLBACK* DLLGETVERSIONPROC) (DLLVERSIONINFO *);
typedef DWORD __stdcall shCopyType (HKEY, LPCTSTR, HKEY, DWORD) ;
#ifndef SM_CMONITORS
#define SM_XVIRTUALSCREEN 76
#define SM_YVIRTUALSCREEN 77
#define SM_CXVIRTUALSCREEN 78
#define SM_CYVIRTUALSCREEN 79
#define SM_CMONITORS 80
#endif
#ifndef PROCESS_MODE_BACKGROUND_BEGIN
#define PROCESS_MODE_BACKGROUND_BEGIN 0x00100000
#define PROCESS_MODE_BACKGROUND_END 0x00200000
#endif
#define CONFIRM_STOP_THRESHOLD 900
typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);
char *WriteDump(struct _EXCEPTION_POINTERS *pExceptionInfo, bool full, long timestamp);
namespace pov_frontend
{
extern shared_ptr gDisplay;
}
namespace vfePlatform
{
extern bool GetCPUCount(unsigned int *TotAvailLogical, unsigned int *TotAvailCore, unsigned int *PhysicalNum);
}
using namespace pov;
using namespace pov_frontend;
void GenerateDumpMeta(bool brief);
namespace povwin
{
typedef struct
{
int id ;
HWND hwnd ;
SIZE size ;
} StatusPanelItem ;
bool rendering ;
bool stop_rendering ;
bool rendersleep ;
bool render_cooperate = false ;
bool InFrontend = false ;
bool ErrorNotified ;
bool ErrorOccurred ;
bool HideRenderWithMain ;
bool UseAlpha;
bool IsExpired;
bool BackendFailedFlag = false;
bool PreventSleep = true;
char DumpMeta[65536];
string ErrorMessage ;
string ErrorFilename ;
unsigned ErrorLine ;
unsigned ErrorCol ;
unsigned render_priority = CM_RENDERPRIORITY_NORMAL ;
unsigned Duty_Cycle = 9 ;
unsigned renderwin_8bits ;
extern int renderwin_xoffset ;
extern int renderwin_yoffset ;
extern int renderwin_left ;
extern int renderwin_top ;
extern char PovLegacyRenderWinClass [] ;
extern unsigned renderwin_max_width ;
extern unsigned renderwin_max_height ;
extern unsigned renderwin_flags ;
extern bool MakeRenderwinActive ;
int render_bitmap_depth ;
unsigned render_width ;
unsigned render_height ;
int alert_sound ;
int run_count ;
int render_anim_count ;
int argc ;
int io_restrictions ;
int tb_combo_sel ;
int povray_return_code ;
int PerformanceScale = 1 ;
int seconds_for_last_line = -1 ;
int delay_next_status ;
int screen_origin_x ;
int screen_origin_y ;
int virtual_screen_width ;
int virtual_screen_height ;
int LastRenderPercentage ;
int renderwin_transparency ;
int cb_expect_selchange ;
char command_line [_MAX_PATH * 3] ;
char old_command_line [_MAX_PATH * 3] ;
char *argv [MAX_ARGV] ;
char source_file_name [_MAX_PATH] ;
char modulePath [_MAX_PATH] ;
char engineHelpPath [_MAX_PATH] ;
char lastRenderName [_MAX_PATH] ;
char lastBitmapName [_MAX_PATH] ;
char lastRenderPath [_MAX_PATH] ;
char lastBitmapPath [_MAX_PATH] ;
char lastQueuePath [_MAX_PATH] ;
char lastSecondaryIniFilePath [_MAX_PATH] ;
char DefaultRenderIniFileName [_MAX_PATH] ;
char SecondaryRenderIniFileName [_MAX_PATH] ;
char SecondaryRenderIniFileSection [64] ;
char background_file [_MAX_PATH] ;
char EngineIniFileName[_MAX_PATH] ;
char BinariesPath [_MAX_PATH] ;
char DocumentsPath [_MAX_PATH] ;
char LastInferredHome [_MAX_PATH] ;
char ToolIniFileName [_MAX_PATH] ;
char tool_commands [MAX_TOOLCMD] [MAX_TOOLCMDTEXT] ;
char tool_help [MAX_TOOLCMD] [MAX_TOOLHELPTEXT] ;
char requested_render_file [_MAX_PATH] ;
char RegionStr [128] ;
char TempRegionStr [128] ;
char demo_file_name [_MAX_PATH] ;
char demo_ini_name [_MAX_PATH] ;
char status_buffer [1024] ;
char render_complete_sound [_MAX_PATH] ;
char parse_error_sound [_MAX_PATH] ;
char render_error_sound [_MAX_PATH] ;
char FontPath [_MAX_PATH] ;
char *EditDLLPath ;
bool render_complete_sound_enabled ;
bool parse_error_sound_enabled ;
bool render_error_sound_enabled ;
bool alert_on_completion ;
bool save_settings ;
bool IsW95UserInterface = true;
bool IsW98 ;
bool IsWNT ;
bool IsW2k ;
bool IsWXP ;
bool IsVista ;
bool running_demo ;
bool running_benchmark ;
bool benchmark_multithread ;
bool fast_scroll ;
bool no_shellout_wait ;
bool tile_background = false ;
bool debugging ;
bool no_palette_warn ;
bool render_lock_up ;
bool RenderwinIsChild = true ;
bool demo_mode ;
bool benchmark_mode;
bool ignore_auto_ini ;
bool newVersion ;
bool exit_after_render ;
bool system_noactive ;
bool one_instance = true ;
bool run_renderer ;
bool use_toolbar = true ;
bool use_tooltips = true ;
bool use_editors = true ;
bool editors_enabled = true;
bool resizing ;
bool drop_to_editor ;
bool restore_command_line ;
bool render_requested ;
bool render_auto_close ;
bool noexec ;
bool ExtensionsEnabled = true ;
bool use_taskbar = true ;
bool main_window_hidden ;
bool about_showing ;
bool NoRestore ;
bool IsComCtl5 = false ;
bool IsComCtl6 = false ;
bool allow_rw_source ;
bool no_shell_outs = true ;
bool hide_newuser_help ;
bool info_render_complete = false ;
bool no_status_output = false ;
bool temp_render_region = false ;
bool rendering_insert_menu = false ;
bool was_insert_render = false ;
bool rendering_animation = false ;
bool preserve_bitmap = false ;
bool first_frame = false ;
bool check_new_version ;
bool check_news ;
bool send_system_info ;
bool homeInferred = false ;
bool editSettingsCopied = false ;
bool FreshInstall = false;
bool output_to_file ;
bool UpdateCheckDone = false ;
bool AutoAppendPaths ;
HWND toolbar_window ;
HWND aux_toolbar_window ;
HWND window_list [MAX_WINDOWS] ;
HWND toolbar_combobox ;
HWND rebar_window ;
HWND StatusWindow ;
HWND StatusTooltip ;
HWND toolbar_cmdline ;
HWND tab_window ;
HICON ourIcon ;
HICON renderIcon ;
HFONT about_font ;
HANDLE hMainThread ;
string InputFileName ;
time_t SecondCountStart ;
time_t quit ;
HBITMAP hBmpBackground ;
HBITMAP hBmpRendering ;
HBITMAP hBmpIcon ;
HBITMAP hBmpAbout ;
__int64 PerformanceFrequency ;
__int64 PerformanceCounter1 ;
__int64 PerformanceCounter2 ;
__int64 KernelTimeStart ;
__int64 KernelTimeEnd ;
__int64 UserTimeStart ;
__int64 UserTimeEnd ;
__int64 KernelTimeTotal ;
__int64 UserTimeTotal ;
__int64 CPUTimeTotal ;
clock_t ClockTimeStart ;
clock_t ClockTimeEnd ;
clock_t ClockTimeTotal ;
clock_t SleepTimeStart ;
clock_t SleepTimeEnd ;
clock_t SleepTimeTotal ;
unsigned class_registered = 0 ;
unsigned currentX = 0 ;
unsigned currentY = 0 ;
unsigned screen_width ;
unsigned screen_height ;
unsigned screen_depth ;
unsigned background_width ;
unsigned background_height ;
unsigned seconds = 0 ;
unsigned toolheight = 0 ;
unsigned statusheight = 0 ;
unsigned on_completion = CM_COMPLETION_NOTHING ;
unsigned window_count = 0 ;
unsigned ThreadCount = 2 ;
unsigned NumberOfCPUs ;
unsigned NumLogicalCPUs ;
unsigned NumCPUCores ;
unsigned NumPhysicalCPUs ;
HPALETTE hPalApp ;
HPALETTE hPalBitmap ;
COLORREF background_colour ;
COLORREF text_colours [3] ;
COLORREF custom_colours [16] ;
COLORREF background_shade = RGB (1, 1, 1) ;
HINSTANCE hInstance ;
HH_AKLINK hh_aklink ;
OSVERSIONINFO version_info ;
StatusPanelItem StatusPanelItems [IDC_STATUS_ID_LAST + 1] ;
CRITICAL_SECTION critical_section ;
// key is the name of an included file (all lower case).
// content is the name of the most recent rendered file that caused it to be included.
map IncludeToSourceMap;
map IncludeAlternateDecisionMap;
char queued_files [MAX_QUEUE] [_MAX_PATH] ;
char dir [_MAX_PATH] ;
unsigned queued_file_count = 0 ;
unsigned auto_render = true ;
unsigned timer_id ;
unsigned about_timer_id ;
unsigned timer_ticks;
unsigned panel_size ;
char PovMainWinClass [] = CLASSNAMEPREFIX "MainWinClass" ;
unsigned mainwin_xpos ;
unsigned mainwin_ypos ;
HWND main_window ;
HWND message_window ;
WINDOWPLACEMENT mainwin_placement ;
char PovAboutWinClass [] = CLASSNAMEPREFIX "AboutWinClass" ;
HWND about_window ;
HWND about_buttons [3] ;
WNDPROC about_button_wndproc ;
unsigned about_width ;
unsigned about_height ;
HPALETTE about_palette ;
HWND statuspanel ;
char PovMessageWinClass [] = CLASSNAMEPREFIX "MessageWinClass" ;
#define NUM_ABOUT_LINKS 7
RECT AboutLinks[NUM_ABOUT_LINKS] =
{
{ 23, 14, 188, 87 },
{ 104, 397, 184, 409 },
{ 185, 397, 262, 409 },
{ 263, 397, 370, 409 },
{ 371, 397, 480, 409 },
{ 481, 397, 560, 409 },
{ 32, 411, 154, 423 }
};
char *AboutURLs[NUM_ABOUT_LINKS] =
{
"http://www.povray.org/",
"http://www.digicert.com/",
"http://ntplx.net/",
"http://opensourcelaw.biz/",
"http://www.perforce.com/",
"http://softwarefreedom.org/",
"http://softwarefreedom.org/"
};
bool handle_main_command (WPARAM wParam, LPARAM lParam) ;
void SetStatusPanelItemText (int id, LPCSTR format, ...) ;
void ShowAboutBox (void);
extern int message_xchar ;
extern int message_ychar ;
extern int message_scroll_pos_x ;
extern int message_scroll_pos_y ;
extern int top_message_row ;
extern int message_count ;
extern int message_cols ;
extern int message_rows ;
extern int listbox_xchar ;
extern int listbox_ychar ;
extern int EditFileCount ;
extern int message_output_x ;
extern char message_font_name [256] ;
extern char *EditFiles [] ;
extern bool ListenMode ;
extern bool keep_messages ;
extern bool MenuBarDraw ;
extern bool TrackMem;
extern __int64 PeakMem;
extern unsigned message_font_size ;
extern unsigned message_font_weight ;
extern HFONT message_font ;
extern HFONT tab_font ;
extern HMENU hMenuBar ;
extern HMENU hMainMenu ;
extern HMENU hPopupMenus ;
extern HMENU hVidcapMenu ;
extern HACCEL hAccelerators ;
extern HINSTANCE hLibPovEdit ;
#define MAX_INSERT_MENU_SECTIONS 8192
typedef std::vector InsMenuSecList;
int InsertMenuSection;
bool StartInsertRender;
InsMenuSecList InsertMenuSections;
typedef struct
{
WORD wVirtkey ;
int iMessage ;
WORD wRequest ;
} SCROLLKEYS ;
SCROLLKEYS key2scroll [] =
{
{ VK_END, WM_VSCROLL, SB_BOTTOM },
{ VK_PRIOR, WM_VSCROLL, SB_PAGEUP },
{ VK_NEXT, WM_VSCROLL, SB_PAGEDOWN },
{ VK_UP, WM_VSCROLL, SB_LINEUP },
{ VK_DOWN, WM_VSCROLL, SB_LINEDOWN },
{ VK_LEFT, WM_HSCROLL, SB_PAGEUP },
{ VK_RIGHT, WM_HSCROLL, SB_PAGEDOWN },
{ -1, -1, -1 }
} ;
string StripFilePath (const string& str)
{
string::size_type pos = str.find_last_of ("\\/") ;
if (pos == string::npos)
return (str) ;
return string (str, pos + 1) ;
}
bool StripPathComponent (char *path, int number)
{
if (number > 1)
if (!StripPathComponent (path, number - 1))
return (false) ;
char *s = path + strlen (path) - 1 ;
if (isPathSeparator(*s))
*s-- = '\0' ;
while (s > path && !isPathSeparator(*s))
s-- ;
*s = '\0' ;
return (path [0] != '\0') ;
}
void debug_output (const char *format, ...)
{
char str [2048] ;
va_list arg_ptr ;
FILE *f ;
if (format == NULL)
{
_unlink ("c:\\temp\\povdebug.txt") ;
return ;
}
va_start (arg_ptr, format) ;
vsprintf (str, format, arg_ptr) ;
va_end (arg_ptr) ;
OutputDebugString (str) ;
if ((f = fopen ("c:\\temp\\povdebug.txt", "a+t")) != NULL)
{
fprintf (f, sizeof(time_t) == 4 ? "%u: %s" : "%I64u: %s", time (NULL), str) ;
fclose (f) ;
}
}
// this function is declared in syspovconfig.h ... use pov::WIN32_DEBUG_OUTPUT() from
// anywhere in the POV-Ray source, core or otherwise
void WIN32_DEBUG_OUTPUT (const char *format,...)
{
char str [2048] ;
va_list arg_ptr ;
sprintf (str, "%5d ", GetCurrentThreadId());
va_start (arg_ptr, format) ;
vsprintf (str + 6, format, arg_ptr) ;
va_end (arg_ptr) ;
OutputDebugString (str) ;
}
// this function is declared in syspovconfig.h ... use pov::WIN32_DEBUG_FILE_OUTPUT() from
// anywhere in the POV-Ray source, core or otherwise. it's also thread-safe.
void WIN32_DEBUG_FILE_OUTPUT (const char *format,...)
{
va_list arg_ptr ;
static FILE *f ;
static boost::mutex mtx;
if (format == NULL)
{
if (f != NULL)
{
fclose (f) ;
f = NULL ;
}
return ;
}
if (f == NULL)
{
f = fopen ("c:\\temp\\povdebug.txt", "at") ;
if (f == NULL)
return ;
}
boost::mutex::scoped_lock l(mtx);
fprintf (f, "%u [%d]: ", GetTickCount (), GetCurrentThreadId ()) ;
va_start (arg_ptr, format) ;
vfprintf (f, format, arg_ptr) ;
va_end (arg_ptr) ;
fflush (f);
}
void SetCaption (LPCSTR str)
{
char buffer [1024] ;
static char lastStr [1024] ;
vfeSession& Session (GetSession());
if (str != NULL)
strcpy (lastStr, str) ;
else
str = lastStr ;
if (Session.BackendFailed())
{
SetWindowText (main_window, "Backend Failed") ;
return;
}
#ifndef DEVELOPMENT
if (Session.GetBackendState() == kRendering && InputFileName.size () > 0)
{
string filename = StripFilePath (InputFileName) ;
if (strstr (str, filename.c_str ()) != NULL)
{
// if the filename is in the caption already, we're probably rendering
// the current editor file (exception: if a file with the same name but
// a different path is being viewed).
sprintf (buffer, "%s [%s %d%%]", str, Session.GetBackendStateName(), Session.GetPercentComplete()) ;
}
else
{
// the render file name is not in the caption: in that case we are
// probably viewing another file or the message window.
sprintf (buffer, "%s [Rendering %s: %d%%]", str, filename.c_str (), Session.GetPercentComplete()) ;
}
}
else
sprintf (buffer, "%s [%s]", str, Session.GetBackendStateName ()) ;
SetWindowText (main_window, buffer) ;
#else
if (Session.GetBackendState() == kRendering && InputFileName.size () > 0)
{
string filename = StripFilePath (InputFileName) ;
if (strstr (str, filename.c_str ()) != NULL)
{
// if the filename is in the caption already, we're probably rendering
// the current editor file (exception: if a file with the same name but
// a different path is being viewed).
sprintf (buffer, CAPTIONPREFIX " %s [%s %d%%]", str, Session.GetBackendStateName(), Session.GetPercentComplete()) ;
}
else
{
// the render file name is not in the caption: in that case we are
// probably viewing another file or the message window.
sprintf (buffer, CAPTIONPREFIX " %s [Rendering %s:%d%%]", str, filename.c_str (), Session.GetPercentComplete()) ;
}
}
else
sprintf (buffer, CAPTIONPREFIX " %s [%s]", str, Session.GetBackendStateName()) ;
SetWindowText (main_window, buffer) ;
#endif
}
// returned value is in microseconds
__int64 GetCPUTime (bool Kernel = true, bool User = true)
{
__int64 kt ;
__int64 ut ;
__int64 total = 0 ;
FILETIME ct ;
FILETIME et ;
if (IsWNT)
{
if (!GetProcessTimes (GetCurrentProcess (), &ct, &et, (FILETIME *) &kt, (FILETIME *) &ut))
{
assert (false) ;
return (0) ;
}
if (Kernel)
total += kt ;
if (User)
total += ut ;
return (total / 10) ;
}
else
{
// have to simulate the results for now
// TODO: handle pause time
ut = clock () ;
if (User)
total += ut * 1000 ;
return (total) ;
}
}
char *PPS_String (unsigned pixels, unsigned renderseconds)
{
static char str [128] ;
if (rendersleep)
return ("PAUSED") ;
if (renderseconds == 0)
return ("??? PPS") ;
if (pixels / renderseconds < 5)
{
if (pixels * 60 / renderseconds < 5)
sprintf (str, "%u PPH", pixels * 3600 / renderseconds) ;
else
sprintf (str, "%u PPM", pixels * 60 / renderseconds) ;
}
else
sprintf (str, "%u PPS", pixels / renderseconds) ;
return (str) ;
}
void PrintRenderTimes (int Finished, int NormalCompletion)
{
int PixelsRendered = GetSession().GetPixelsRendered();
unsigned STT = SleepTimeTotal ;
if (rendersleep)
{
SleepTimeEnd = clock () ;
if (Finished)
{
SleepTimeTotal += SleepTimeEnd - SleepTimeStart ;
STT = SleepTimeTotal ;
rendersleep = false ;
}
else
STT += SleepTimeEnd - SleepTimeStart ;
}
KernelTimeEnd = GetCPUTime (true, false) ;
UserTimeEnd = GetCPUTime (false, true) ;
KernelTimeTotal = KernelTimeEnd - KernelTimeStart ;
UserTimeTotal = UserTimeEnd - UserTimeStart ;
CPUTimeTotal = UserTimeTotal + KernelTimeTotal ;
ClockTimeEnd = clock () ;
ClockTimeTotal = ClockTimeEnd - ClockTimeStart - STT ;
if (ClockTimeTotal >= CLOCKS_PER_SEC)
status_printf (StatusPPS, PPS_String (PixelsRendered, ClockTimeTotal / CLOCKS_PER_SEC)) ;
say_status_message (StatusRendertime, get_elapsed_time (ClockTimeTotal / CLOCKS_PER_SEC)) ;
if (IsWNT != 0 && Finished != 0)
{
message_printf ("CPU time used: kernel %.02f seconds, user %.02f seconds, total %.02f seconds.\n",
KernelTimeTotal / 1000000.0, UserTimeTotal / 1000000.0, CPUTimeTotal / 1000000.0) ;
message_printf ("Elapsed time %.02f seconds", (double) ClockTimeTotal / CLOCKS_PER_SEC) ;
if (NumberOfCPUs > 1 && ThreadCount > 1)
{
POV_ULONG cputotal = CPUTimeTotal * CLOCKS_PER_SEC / 1000000 ;
if (cputotal > ClockTimeTotal * 10 / 9)
message_printf (", CPU vs elapsed time ratio %.02f", (double) cputotal / ClockTimeTotal) ;
}
message_printf (".\n") ;
if (PixelsRendered > 0 && CPUTimeTotal > 0 && NormalCompletion)
{
message_printf ("Render averaged %.02f PPS (%.02f PPS CPU time) over %u pixels.\n",
(double) PixelsRendered * CLOCKS_PER_SEC / ClockTimeTotal, (double) PixelsRendered * 1000000 / CPUTimeTotal, PixelsRendered) ;
message_printf ("----------------------------------------------------------------------------\n") ;
#ifndef _DEBUG
char str [2048] ;
if (running_benchmark)
{
char *s = str;
s += sprintf(str, "CPU time used: kernel %.02f seconds, user %.02f seconds, total %.02f seconds.\n",
KernelTimeTotal / 1000000.0, UserTimeTotal / 1000000.0, CPUTimeTotal / 1000000.0) ;
s += sprintf(s, "Elapsed time %.02f seconds", (double) ClockTimeTotal / CLOCKS_PER_SEC) ;
if (NumberOfCPUs > 1 && ThreadCount > 1)
{
POV_ULONG cputotal = CPUTimeTotal * CLOCKS_PER_SEC / 1000000 ;
if (cputotal > ClockTimeTotal * 10 / 9)
s += sprintf(s, ", CPU vs elapsed time ratio %.02f", (double) cputotal / ClockTimeTotal) ;
}
s += sprintf(s, ".\n") ;
s += sprintf (s, "Render averaged %.02f PPS (%.02f PPS CPU time) over %u pixels using %u thread(s).\n",
(double) PixelsRendered * CLOCKS_PER_SEC / ClockTimeTotal, (double) PixelsRendered * 1000000 / CPUTimeTotal, PixelsRendered, ThreadCount) ;
copy_text_to_clipboard(str);
if (benchmark_mode)
{
// special case: we are running the benchmark because of a command-line request.
char fn[_MAX_PATH];
char ts[256];
time_t t = time(NULL);
strftime(ts, sizeof(ts), "%Y%m%d.%H%M%S", gmtime(&t));
sprintf(fn, "%sbenchmark-%s.txt", DocumentsPath, ts);
FILE *f = fopen(fn, "wt");
if (f != NULL)
{
int n = Get_Benchmark_Version();
fprintf(f, "%s", str);
strftime(str, sizeof(str), "%#c", gmtime(&t));
fprintf(f, "\nRender of benchmark version %x.%02x completed at %s UTC.\n", n / 256, n % 256, str);
fprintf(f, "----------------------------------------------------------------------------\n");
GenerateDumpMeta(true);
fwrite(DumpMeta, strlen(DumpMeta), 1, f);
fprintf(f, "povversion=%s\n", POV_RAY_VERSION) ;
fprintf(f, "compilerversion=%s\n", COMPILER_VER) ;
fprintf(f, "platformversion=%s\n", PVENGINE_VER) ;
fclose(f);
}
}
else
{
strcat(str, "\nThese results have been placed in the clipboard.\n");
MessageBox (main_window, str, "Benchmark Complete.", MB_OK) ;
}
}
#endif
}
else
message_printf ("----------------------------------------------------------------------------\n") ;
}
}
bool OkToStopRendering (void)
{
if (GetSession().BackendFailed())
return (true);
if (time (NULL) - SecondCountStart < CONFIRM_STOP_THRESHOLD)
return (true) ;
if (GetSession().GetRealTimeRaytracing() == true)
return (true);
if (MessageBox (main_window, "You've been running this render for quite a while - really cancel ?", "Stop rendering ?", MB_ICONQUESTION | MB_YESNO) == IDYES)
return (true) ;
return (false) ;
}
void menuhelp (UINT idCommand)
{
switch (idCommand)
{
case CM_FILEMENUHELP :
hh_aklink.pszKeywords = "File Menu" ;
break ;
case CM_EDITMENUHELP :
hh_aklink.pszKeywords = "Edit Menu" ;
break ;
case CM_SEARCHMENUHELP :
hh_aklink.pszKeywords = "Search Menu" ;
break ;
case CM_TEXTMENUHELP :
hh_aklink.pszKeywords = "Text Menu" ;
break ;
case CM_EDITORMENUHELP :
hh_aklink.pszKeywords = "Editor Menu" ;
break ;
case CM_RENDERMENUHELP :
hh_aklink.pszKeywords = "Render Menu" ;
break ;
case CM_OPTIONSMENUHELP :
hh_aklink.pszKeywords = "Options Menu" ;
break ;
case CM_TOOLSMENUHELP :
hh_aklink.pszKeywords = "Tools Menu" ;
break ;
case CM_WINDOWMENUHELP :
hh_aklink.pszKeywords = "Window Menu" ;
break ;
case CM_RENDERWINMENUHELP :
hh_aklink.pszKeywords = "Render Window Menu" ;
break ;
case CM_MESSAGEWINMENUHELP :
hh_aklink.pszKeywords = "Message Window Menu" ;
break ;
default :
hh_aklink.pszKeywords = NULL ;
break ;
}
if (hh_aklink.pszKeywords != NULL)
HtmlHelp (NULL, engineHelpPath, HH_KEYWORD_LOOKUP, (DWORD_PTR) &hh_aklink) ;
}
// example taken from MSDN documentation
DWORD GetDllVersion (LPCTSTR lpszDllName)
{
HINSTANCE hinstDll;
DWORD dwVersion = 0;
hinstDll = LoadLibrary(lpszDllName) ;
if (hinstDll)
{
DLLGETVERSIONPROC pDllGetVersion;
pDllGetVersion = (DLLGETVERSIONPROC) GetProcAddress (hinstDll, "DllGetVersion");
if (pDllGetVersion)
{
DLLVERSIONINFO dvi;
HRESULT hr;
ZeroMemory(&dvi, sizeof (dvi));
dvi.cbSize = sizeof (dvi);
hr = (*pDllGetVersion) (&dvi);
if (SUCCEEDED (hr))
dwVersion = MAKELONG (dvi.dwMajorVersion, dvi.dwMinorVersion);
}
FreeLibrary (hinstDll);
}
return dwVersion;
}
void getvars (ExternalVarStruct *v)
{
strcpy (v->command_line, command_line) ;
strcpy (v->source_file_name, source_file_name) ;
strcpy (v->lastRenderName, lastRenderName) ;
strcpy (v->lastRenderPath, lastRenderPath) ;
strcpy (v->lastQueuePath, lastQueuePath) ;
strcpy (v->lastSecondaryIniFilePath, lastSecondaryIniFilePath) ;
strcpy (v->DefaultRenderIniFileName, DefaultRenderIniFileName) ;
strcpy (v->SecondaryRenderIniFileName, SecondaryRenderIniFileName) ;
strcpy (v->SecondaryRenderIniFileSection, SecondaryRenderIniFileSection) ;
strcpy (v->ourPath, modulePath) ;
strcpy (v->engineHelpPath, engineHelpPath) ;
strcpy (v->rendererHelpPath, "") ;
strcpy (v->BinariesPath, BinariesPath) ;
strcpy (v->EngineIniFileName, EngineIniFileName) ;
strcpy (v->ToolIniFileName, ToolIniFileName) ;
memcpy (v->queued_files, queued_files, sizeof (v->queued_files)) ;
v->loadRerun = false ;
v->continueRerun = false ;
v->povray_return_code = povray_return_code ;
v->rendering = rendering ;
v->IsWin32 = true ;
v->IsW95UserInterface = IsW95UserInterface ;
v->running_demo = running_demo ;
v->debugging = debugging ;
v->isMaxiMinimized = false ;
v->newVersion = newVersion ;
v->use_threads = true ;
v->use_toolbar = use_toolbar ;
v->use_tooltips = use_tooltips ;
v->use_editors = use_editors ;
v->drop_to_editor = drop_to_editor ;
v->rendersleep = rendersleep ;
v->ExtensionsEnabled = ExtensionsEnabled ;
v->queued_file_count = queued_file_count > OLD_MAX_QUEUE ? OLD_MAX_QUEUE : queued_file_count ;
v->auto_render = auto_render ;
}
void setvars (ExternalVarStruct *v)
{
strncpy (command_line, v->command_line, sizeof (command_line) - 1) ;
SendMessage (toolbar_cmdline, WM_SETTEXT, 0, (LPARAM) command_line) ;
strncpy (source_file_name, v->source_file_name, sizeof (source_file_name) - 1) ;
strncpy (lastRenderName, v->lastRenderName, sizeof (lastRenderName) - 1) ;
strncpy (lastRenderPath, v->lastRenderPath, sizeof (lastRenderPath) - 1) ;
strncpy (lastQueuePath, v->lastQueuePath, sizeof (lastQueuePath) - 1) ;
strncpy (lastSecondaryIniFilePath, v->lastSecondaryIniFilePath, sizeof (lastSecondaryIniFilePath) - 1) ;
strncpy (DefaultRenderIniFileName, v->DefaultRenderIniFileName, sizeof (DefaultRenderIniFileName) - 1) ;
strncpy (SecondaryRenderIniFileName, v->SecondaryRenderIniFileName, sizeof (SecondaryRenderIniFileName) - 1) ;
strncpy (SecondaryRenderIniFileSection, v->SecondaryRenderIniFileSection, sizeof (SecondaryRenderIniFileSection) - 1) ;
}
bool HaveWin95OrLater (void)
{
return (version_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) ;
}
bool HaveWin98OrLater (void)
{
if (version_info.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS)
return (false) ;
if (version_info.dwMajorVersion < 4)
return (false) ;
if (version_info.dwMajorVersion > 4)
return (true) ;
return (version_info.dwMinorVersion > 0) ;
}
bool HaveNT4OrLater (void)
{
return (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT && version_info.dwMajorVersion >= 4) ;
}
bool HaveWin2kOrLater (void)
{
return (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT && version_info.dwMajorVersion >= 5) ;
}
bool HaveWinXPOrLater (void)
{
if (version_info.dwPlatformId != VER_PLATFORM_WIN32_NT || version_info.dwMajorVersion < 5)
return (false) ;
return (version_info.dwMajorVersion > 5 || (version_info.dwMajorVersion == 5 && version_info.dwMinorVersion > 0)) ;
}
bool HaveVistaOrLater (void)
{
return version_info.dwPlatformId == VER_PLATFORM_WIN32_NT && version_info.dwMajorVersion > 5;
}
void set_render_priority (unsigned priority)
{
switch (priority)
{
case CM_RENDERPRIORITY_BACKGROUND :
SetPriorityClass (GetCurrentProcess(), PROCESS_MODE_BACKGROUND_BEGIN) ;
break ;
case CM_RENDERPRIORITY_LOW :
if (IsVista)
SetPriorityClass (GetCurrentProcess(), PROCESS_MODE_BACKGROUND_END);
SetPriorityClass (GetCurrentProcess(), IDLE_PRIORITY_CLASS) ;
break ;
case CM_RENDERPRIORITY_NORMAL :
if (IsVista)
SetPriorityClass (GetCurrentProcess(), PROCESS_MODE_BACKGROUND_END);
SetPriorityClass (GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS) ;
break ;
case CM_RENDERPRIORITY_HIGH :
if (IsVista)
SetPriorityClass (GetCurrentProcess(), PROCESS_MODE_BACKGROUND_END);
SetPriorityClass (GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS) ;
break ;
}
}
// we can't allow LoadBitmap to load our background bitmaps 'cause if we're running
// a 256-colour mode, it will map the incoming resource to 16 colours ...
// LoadImage () doesn't exist under Win32s, either. sigh.
HBITMAP NonBogusLoadBitmap (HINSTANCE hInst, LPSTR lpszBitmap)
{
void *p ;
HRSRC hres ;
HGLOBAL hg ;
HBITMAP hBitmap ;
if ((hres = FindResource (hInst, lpszBitmap, RT_BITMAP)) == NULL)
return (NULL) ;
if ((hg = LoadResource (hInst, hres)) == NULL)
return (NULL) ;
if ((p = LockResource (hg)) == NULL)
{
FreeResource (hg) ;
return (NULL) ;
}
hBitmap = lpDIBToBitmap (p, hPalApp) ;
FreeResource (hg) ;
return (hBitmap) ;
}
HBITMAP NonBogusLoadBitmapAndPalette (HINSTANCE hInst, LPSTR lpszBitmap)
{
void *p ;
HRSRC hres ;
HGLOBAL hg ;
HBITMAP hBitmap ;
if ((hres = FindResource (hInst, lpszBitmap, RT_BITMAP)) == NULL)
return (NULL) ;
if ((hg = LoadResource (hInst, hres)) == NULL)
return (NULL) ;
if ((p = LockResource (hg)) == NULL)
{
FreeResource (hg) ;
return (NULL) ;
}
hBitmap = lpDIBToBitmapAndPalette (p) ;
FreeResource (hg) ;
return (hBitmap) ;
}
// finds fist separator character in a path
char *findFirstPathSeparator (char *s)
{
size_t pos = strcspn(s, "\\/");
return pos >= strlen(s) ? NULL : s + pos;
}
// finds last separator character in a path
char *findLastPathSeparator (char *s)
{
char *s1 = strrchr (s, '\\');
char *s2 = strrchr (s1 ? s1 : s, '/');
return s2 ? s2 : s1;
}
// append separator character to a path, if not present already
// does not append to empty strings.
// does append to solitary drive letters; this isn't quite legit but as we don't
// support the use of drive references without a path (i.e. references that rely
// on the uniquely-DOS/Windows concept of a CWD for *each* mounted device), it
// doesn't really matter.
void appendPathSeparator (char *str)
{
if (str[0] != '\0') // only append to non-empty strings
if (!hasTrailingPathSeparator(str))
strcat(str, "\\");
}
// tests if path has trailing separator character
// does not return true if path is a solitary drive letter (e.g. "C:").
// the same consideration applies regarding relative drive references as
// mentioned in appendPathSeparator.
bool hasTrailingPathSeparator (const char *str)
{
return isPathSeparator(str[strlen (str) - 1]);
}
// strips trailing separator character from path
// will strip a trailing separator right after drive letter (e.g. "C:\").
// calling functions must be careful not to let this trip them up if they
// are passed any user-specified input.
void trimTrailingPathSeparator (char *str)
{
char *s = str + strlen (str) - 1 ;
if (isPathSeparator(*s))
*s = '\0' ;
}
// strips leading and trailing double-quotes from a string.
// does not check that both are present, just removes them
// if they are there. returns new start of string.
char *trimDoubleQuotes (char *str)
{
if (str[0] == '"')
str++;
char *s = str + strlen (str) - 1 ;
if (*s == '"')
*s = '\0' ;
return str;
}
// strips trailing separator character from path
void validatePath (char *s)
{
if (s [1] == ':' && strlen (s) < 4)
return ; // make sure we don't trim a trailing separator right after a drive letter (e.g. "C:\")
trimTrailingPathSeparator (s);
}
int joinPath (char *out, const char *path, const char *name)
{
strcpy (out, path) ;
appendPathSeparator (out);
strcat (out, name) ;
return ((int) strlen (out)) ;
}
bool reg_printf (bool useHKCU, char *keyName, char *valName, char *format, ...)
{
char str [2048] ;
HKEY hKey ;
va_list arg_ptr ;
if (strlen (format) > sizeof (str) - 256)
return (false) ;
if (RegCreateKeyEx (useHKCU ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, keyName, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL) == ERROR_SUCCESS)
{
va_start (arg_ptr, format) ;
vsprintf (str, format, arg_ptr) ;
va_end (arg_ptr) ;
RegSetValueEx (hKey, valName, 0, REG_SZ, (BYTE *) str, (int) strlen (str) + 1) ;
RegCloseKey (hKey) ;
return (true) ;
}
return (false) ;
}
// conditional version of reg_printf
bool cond_reg_printf (char *keyName, char *valName, char *format, ...)
{
char str [2048] ;
DWORD len = sizeof (str) ;
HKEY hKey ;
va_list arg_ptr ;
if (strlen (format) > sizeof (str) - 256)
return (false) ;
if (RegOpenKeyEx (HKEY_CURRENT_USER, keyName, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
if (RegQueryValueEx (hKey, valName, 0, NULL, (BYTE *) str, &len) == ERROR_SUCCESS)
{
RegCloseKey (hKey) ;
// it already exists - if it doesn't have zero length we don't update it
if (str [0])
return (true) ;
}
else
RegCloseKey (hKey) ;
}
if (RegCreateKeyEx (HKEY_CURRENT_USER, keyName, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL) == ERROR_SUCCESS)
{
va_start (arg_ptr, format) ;
vsprintf (str, format, arg_ptr) ;
va_end (arg_ptr) ;
RegSetValueEx (hKey, valName, 0, REG_SZ, (BYTE *) str, (int) strlen (str) + 1) ;
RegCloseKey (hKey) ;
return (true) ;
}
return (false) ;
}
static bool reg_dword (char *keyName, char *valName, DWORD value)
{
HKEY hKey ;
if (RegCreateKeyEx (HKEY_CURRENT_USER, keyName, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL) == ERROR_SUCCESS)
{
RegSetValueEx (hKey, valName, 0, REG_DWORD, (BYTE *) &value, 4) ;
RegCloseKey (hKey) ;
return (true) ;
}
return (false) ;
}
char *GetInstallTime (void)
{
HKEY key ;
DWORD len ;
static char str [64] ;
len = sizeof (str) ;
if (RegOpenKeyEx (HKEY_CURRENT_USER, "Software\\" REGKEY, 0, KEY_READ, &key) == ERROR_SUCCESS)
{
if (RegQueryValueEx (key, INSTALLTIMEKEY, 0, NULL, (BYTE *) str, &len) == ERROR_SUCCESS)
{
RegCloseKey (key) ;
return (str) ;
}
RegCloseKey (key) ;
}
return (NULL) ;
}
bool checkRegKey (void)
{
char str [_MAX_PATH] ;
HKEY key ;
DWORD val;
DWORD len = sizeof (str) ;
FILETIME file_time ;
SYSTEMTIME system_time ;
if (GetInstallTime () == NULL)
{
GetSystemTime (&system_time) ;
if (SystemTimeToFileTime (&system_time, &file_time))
reg_printf (true, "Software\\" REGKEY, INSTALLTIMEKEY, "%I64u", ((__int64) file_time.dwHighDateTime << 32) | file_time.dwLowDateTime) ;
}
if (RegOpenKeyEx (HKEY_CURRENT_USER, "Software\\" REGKEY "\\" REGVERKEY "\\Windows", 0, KEY_READ, &key) == ERROR_SUCCESS)
{
if (RegQueryValueEx (key, "Home", 0, NULL, (BYTE *) str, &len) != 0)
str[0] = '\0';
if (str [0] == '\0')
{
RegCloseKey (key) ;
return (false) ;
}
len = sizeof(val);
if (RegQueryValueEx (key, "FreshInstall", 0, NULL, (BYTE *) &val, &len) == 0)
{
FreshInstall = val != 0;
RegCloseKey (key) ;
if (FreshInstall && RegOpenKeyEx (HKEY_CURRENT_USER, "Software\\" REGKEY "\\" REGVERKEY "\\Windows", 0, KEY_READ | KEY_SET_VALUE, &key) == ERROR_SUCCESS)
{
RegDeleteValue(key, "FreshInstall");
RegCloseKey (key) ;
}
}
else
RegCloseKey (key) ;
}
else
return (false) ;
if (RegOpenKeyEx (HKEY_CURRENT_USER, "Software\\" REGKEY "\\CurrentVersion\\Windows", 0, KEY_READ | KEY_WRITE, &key) == ERROR_SUCCESS)
{
len = sizeof (str) ;
if (RegQueryValueEx (key, VERSIONVAL, 0, NULL, (BYTE *) str, &len) != 0 || strcmp (str, POV_RAY_VERSION) != 0)
RegSetValueEx (key, VERSIONVAL, 0, REG_SZ, (BYTE *) POV_RAY_VERSION, (int) strlen (POV_RAY_VERSION) + 1) ;
RegCloseKey (key) ;
}
return true;
}
bool getHome (void)
{
HKEY key ;
DWORD len ;
if (debugging)
debug_output ("querying registry\n") ;
DocumentsPath [0] = BinariesPath [0] = '\0' ;
if (RegOpenKeyEx (HKEY_CURRENT_USER, "Software\\" REGKEY "\\" REGVERKEY "\\Windows", 0, KEY_READ, &key) == ERROR_SUCCESS)
{
len = sizeof (LastInferredHome) ;
RegQueryValueEx (key, "LastInferredHome", 0, NULL, (BYTE *) LastInferredHome, &len) ;
len = sizeof (BinariesPath) ;
RegQueryValueEx (key, "Home", 0, NULL, (BYTE *) BinariesPath, &len) ;
if (debugging && BinariesPath[0] != '\0')
debug_output("Win32 getHome() succeeded (HKCU::Home), BinariesPath is '%s'\n", BinariesPath) ;
len = sizeof (DocumentsPath) ;
RegQueryValueEx (key, "DocPath", 0, NULL, (BYTE *) DocumentsPath, &len) ;
if (debugging && DocumentsPath[0] != '\0')
debug_output("Win32 getHome() succeeded (HKCU::DocPath), DocumentsPath is '%s'\n", DocumentsPath) ;
RegCloseKey (key) ;
return (BinariesPath[0] != '\0') ;
}
if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software\\" REGKEY "\\" REGVERKEY "\\Windows", 0, KEY_READ, &key) == ERROR_SUCCESS)
{
len = sizeof (BinariesPath) ;
RegQueryValueEx (key, "Home", 0, NULL, (BYTE *) BinariesPath, &len) ;
RegCloseKey (key) ;
if (debugging && BinariesPath[0] != '\0')
debug_output("Win32 getHome() succeeded (HKLM::Home), BinariesPath is '%s'\n", BinariesPath) ;
return (BinariesPath[0] != '\0') ;
}
return (false) ;
}
bool inferHome (void)
{
char exePath [_MAX_PATH] ;
char *s ;
if (GetModuleFileName (NULL, exePath, _MAX_PATH) == 0)
return (false) ;
// find path component
if ((s = findLastPathSeparator (exePath)) == NULL)
return (false) ;
*s = '\0' ;
// now step up one directory
if ((s = findLastPathSeparator (exePath)) == NULL)
return (false) ;
*++s = '\0' ;
// now look for some standard directories
strcpy (s, "help") ;
if (!dirExists (exePath))
{
strcpy (s, "sounds") ;
if (!dirExists (exePath))
{
strcpy (s, "tiles") ;
if (!dirExists (exePath))
return (false) ;
}
}
*s = '\0' ;
strcpy (BinariesPath, exePath) ;
homeInferred = true ;
return (true) ;
}
string get36Home(void)
{
char str[_MAX_PATH];
HKEY key ;
DWORD len = sizeof(str);
if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software\\" REGKEY "\\v3.6\\Windows", 0, KEY_READ, &key) == ERROR_SUCCESS)
{
if (RegQueryValueEx (key, "Home", 0, NULL, (BYTE *) str, &len) == 0)
{
RegCloseKey (key) ;
appendPathSeparator(str);
return string(str);
}
RegCloseKey (key) ;
}
if (RegOpenKeyEx (HKEY_CURRENT_USER, "Software\\" REGKEY "\\v3.6\\Windows", 0, KEY_READ, &key) == ERROR_SUCCESS)
{
if (RegQueryValueEx (key, "Home", 0, NULL, (BYTE *) str, &len) == 0)
{
RegCloseKey (key) ;
appendPathSeparator(str);
return string(str);
}
RegCloseKey (key) ;
}
return string();
}
bool copy36EditSettings(void)
{
HKEY hKeySrc ;
HKEY hKeyDst ;
DWORD result ;
HINSTANCE hLib ;
shCopyType *shCopyKey ;
if ((hLib = LoadLibrary ("shlwapi.dll")) == NULL)
return (false) ;
shCopyKey = (shCopyType *) GetProcAddress (hLib, "SHCopyKeyA") ;
if (shCopyKey == NULL)
{
FreeLibrary (hLib) ;
return (false) ;
}
if (RegOpenKeyEx (HKEY_CURRENT_USER, "Software\\" REGKEY "\\v3.6", 0, KEY_READ, &hKeySrc) != ERROR_SUCCESS)
{
FreeLibrary (hLib) ;
return (false) ;
}
if (RegCreateKeyEx (HKEY_CURRENT_USER, "Software\\" REGKEY "\\" REGVERKEY "\\POV-Edit", 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKeyDst, NULL) != ERROR_SUCCESS)
{
RegCloseKey (hKeySrc) ;
FreeLibrary (hLib) ;
return (false) ;
}
result = shCopyKey (hKeySrc, "POV-Edit", hKeyDst, NULL) ;
RegCloseKey (hKeySrc) ;
RegCloseKey (hKeyDst) ;
FreeLibrary (hLib) ;
return (result == ERROR_SUCCESS) ;
}
bool checkEditKey36 (void)
{
HKEY key ;
if (RegOpenKeyEx (HKEY_CURRENT_USER, "Software\\" REGKEY "\\v3.6\\POV-Edit", 0, KEY_READ, &key) == ERROR_SUCCESS)
{
RegCloseKey (key) ;
return (true) ;
}
return (false) ;
}
bool checkEditKey37 (void)
{
HKEY key ;
if (RegOpenKeyEx (HKEY_CURRENT_USER, "Software\\" REGKEY "\\" REGVERKEY "\\POV-Edit", 0, KEY_READ, &key) == ERROR_SUCCESS)
{
RegCloseKey (key) ;
return (true) ;
}
return (false) ;
}
// called if either our registry entries don't seem to be set up, or if they do exist and
// the FreshInstall option is set in the registry.
bool CloneOptions (void)
{
if (debugging)
debug_output("attempting to create registry entries\n") ;
// don't do this if we're being called as a result of the FreshInstall registry setting
if (!FreshInstall)
{
if (!reg_printf (true, "Software\\" REGKEY "\\" REGVERKEY "\\Windows", "Home", "%s", BinariesPath))
return (false) ;
if (!homeInferred)
reg_printf (true, "Software\\" REGKEY "\\CurrentVersion\\Windows", "Home", "%s", BinariesPath) ;
}
reg_printf (true, "Software\\" REGKEY "\\CurrentVersion\\Windows", VERSIONVAL, "%s", POV_RAY_VERSION) ;
cond_reg_printf ("Software\\" REGKEY "\\" REGVERKEY "\\POV-Edit\\Open", "Open0", "%sChanges.txt,1,1,0,0,8,2", DocumentsPath) ;
cond_reg_printf ("Software\\" REGKEY "\\" REGVERKEY "\\POV-Edit\\Recent", "Recent0", "%sChanges.txt,1,1,0,0,8,2", DocumentsPath) ;
cond_reg_printf ("Software\\" REGKEY "\\" REGVERKEY "\\POV-Edit\\Open", "Open1", "%sRevision.txt,1,1,0,0,8,2", DocumentsPath) ;
cond_reg_printf ("Software\\" REGKEY "\\" REGVERKEY "\\POV-Edit\\Recent", "Recent1", "%sRevision.txt,1,1,0,0,8,2", DocumentsPath) ;
cond_reg_printf ("Software\\" REGKEY "\\" REGVERKEY "\\POV-Edit\\Open", "Open2", "%sscenes\\advanced\\biscuit.pov,1,1,0,6,8,2", DocumentsPath) ;
cond_reg_printf ("Software\\" REGKEY "\\" REGVERKEY "\\POV-Edit\\Recent", "Recent2", "%sscenes\\advanced\\biscuit.pov,1,1,0,6,8,2", DocumentsPath) ;
cond_reg_printf ("Software\\" REGKEY "\\" REGVERKEY "\\POV-Edit\\Open", "Open3", "%sscenes\\advanced\\woodbox.pov,1,1,0,6,8,2", DocumentsPath) ;
cond_reg_printf ("Software\\" REGKEY "\\" REGVERKEY "\\POV-Edit\\Recent", "Recent3", "%sscenes\\advanced\\woodbox.pov,1,1,0,6,8,2", DocumentsPath) ;
return (true) ;
}
int parse_commandline (char *s)
{
char *prevWord = NULL ;
char inQuote = '\0' ;
static char str [_MAX_PATH * 3] ;
static char filename [_MAX_PATH] ;
argc = 0 ;
GetModuleFileName (hInstance, filename, sizeof (filename) - 1) ;
argv [argc++] = filename ;
s = strncpy (str, s, sizeof (str) - 1) ;
while (*s)
{
switch (*s)
{
case '"' :
case '\'' :
if (inQuote)
{
if (*s == inQuote)
inQuote = 0 ;
}
else
{
inQuote = *s ;
if (prevWord == NULL)
prevWord = s ;
}
break ;
case ' ' :
case '\t' :
if (!inQuote)
{
if (prevWord != NULL)
{
*s = '\0' ;
argv [argc++] = prevWord ;
prevWord = NULL ;
}
}
break ;
default :
if (prevWord == NULL)
prevWord = s ;
break ;
}
if (argc >= MAX_ARGV - 1)
break ;
s++ ;
}
if (prevWord != NULL && argc < MAX_ARGV - 1)
argv [argc++] = prevWord ;
argv [argc] = NULL ;
return (argc) ;
}
int InstallSettings (char *binpath, char *docpath, bool quiet)
{
char base [_MAX_PATH] ;
char str [_MAX_PATH] ;
char *s ;
// we attempt to infer the install dir if it's not supplied
if (binpath == NULL)
{
if (_getcwd (base, sizeof (base) - 1) == NULL)
{
if (!quiet)
MessageBox (NULL, "Could not get current directory - cannot infer home path. Please supply it on the command-line",
"POV-Ray for Windows - running INSTALL option", MB_OK | MB_ICONSTOP) ;
return 5 ;
}
if (strlen (base) < 3 || (strlen (base) == 3 && base [1] == ':' && isPathSeparator(base [2])))
{
if (!quiet)
{
sprintf (str, "Current dir '%s' is root - cannot infer home path. Please supply it on the command-line.", base) ;
MessageBox (NULL, str, "POV-Ray for Windows - running INSTALL option", MB_OK | MB_ICONSTOP) ;
}
return 10 ;
}
// the strlen test covers the case where base is "\\" (i.e. a network path) or a bare drive (e.g. "c:").
if (!StripPathComponent (base, 1) || strlen(base) < 3)
{
if (!quiet)
MessageBox (NULL, "Cannot infer home path. Please supply it on the command-line.",
"POV-Ray for Windows - running INSTALL option", MB_OK | MB_ICONSTOP) ;
return 15 ;
}
}
else
{
strcpy (str, binpath) ;
s = trimDoubleQuotes(str);
validatePath (s) ;
if (GetFullPathName (s, sizeof (base), base, NULL) == 0)
{
sprintf (str, "GetFullPathName() for '%s' failed [0x%08x]", s, GetLastError ()) ;
MessageBox (NULL, str, "POV-Ray for Windows - running INSTALL option", MB_OK | MB_ICONSTOP) ;
}
}
if (!dirExists (base))
{
if (!quiet)
{
sprintf (str, "Could not stat directory '%s'", base) ;
MessageBox (NULL, str, "POV-Ray for Windows - running INSTALL option", MB_OK | MB_ICONSTOP) ;
}
return 20 ;
}
if (docpath != NULL)
{
if (!dirExists(docpath))
{
// if the path doesn't exist and quiet is selected, we continue, assuming that
// by the time povray is ready to be used, it will either exist or be able to be
// created. note that we do auto-create the path if possible (i.e. the parent
// directory exists and is writable by the user who launches POV-Ray).
if (!quiet)
if (MessageBox (NULL, "Could not verify supplied user files path: use it anyway?\n(Selecting NO will exit.)", "POV-Ray for Windows - running INSTALL option", MB_YESNO | MB_ICONEXCLAMATION) == IDNO)
return 30;
}
reg_printf (true, "Software\\" REGKEY "\\" REGVERKEY "\\Windows", "DocPath", "%s\\", docpath);
}
if (!reg_printf (true, "Software\\" REGKEY "\\" REGVERKEY "\\Windows", "Home", "%s\\", base))
{
if (!quiet)
MessageBox (NULL, "Failed to write to HKCU in registry", "POV-Ray for Windows - running INSTALL option", MB_OK | MB_ICONSTOP) ;
return 35 ;
}
// it's ok for this to fail as they may not have administrative rights
reg_printf (false, "Software\\" REGKEY "\\" REGVERKEY "\\Windows", "Home", "%s\\", base) ;
if (!quiet)
{
sprintf (str, "[Home path is %s]\n\nSuccess!", base) ;
MessageBox (NULL, str, "POV-Ray for Windows - running INSTALL option", MB_OK | MB_ICONINFORMATION) ;
}
return (0) ;
}
char *GetExceptionDescription (DWORD code)
{
switch (code)
{
case EXCEPTION_ACCESS_VIOLATION :
return ("a memory access violation") ;
case EXCEPTION_DATATYPE_MISALIGNMENT :
return ("a datatype misalignment") ;
case EXCEPTION_FLT_DENORMAL_OPERAND :
return ("a denormal floating point operand") ;
case EXCEPTION_FLT_DIVIDE_BY_ZERO :
return ("a floating point divide by zero") ;
case EXCEPTION_FLT_INEXACT_RESULT :
return ("an inexact floating-point result") ;
case EXCEPTION_FLT_INVALID_OPERATION :
return ("an invalid floating-point operation") ;
case EXCEPTION_FLT_OVERFLOW :
return ("a floating-point overflow") ;
case EXCEPTION_FLT_STACK_CHECK :
return ("a floating-point stack over/underflow") ;
case EXCEPTION_FLT_UNDERFLOW :
return ("a floating-point underflow") ;
case EXCEPTION_INT_DIVIDE_BY_ZERO :
return ("an integer divide by zero") ;
case EXCEPTION_INT_OVERFLOW :
return ("an integer overflow") ;
case EXCEPTION_PRIV_INSTRUCTION :
return ("the execution of a privileged instruction") ;
case EXCEPTION_IN_PAGE_ERROR :
return ("a page error") ;
case EXCEPTION_ILLEGAL_INSTRUCTION :
return ("the execution of an illegal instruction") ;
case EXCEPTION_NONCONTINUABLE_EXCEPTION :
return ("a continuation after a noncontinuable exception") ;
case EXCEPTION_STACK_OVERFLOW :
return ("a stack overflow") ;
case EXCEPTION_INVALID_DISPOSITION :
return ("an invalid disposition") ;
case EXCEPTION_GUARD_PAGE :
return ("a guard page exception") ;
case EXCEPTION_INVALID_HANDLE :
return ("an invalid handle exception") ;
default :
return NULL ;
}
}
#if POV_RAY_IS_OFFICIAL == 1
// this pulls in the code for update checks and crash dump submission.
// it is only used in official releases made by the POV-Ray developers,
// so the source is not included in the public distribution.
#include "official.h"
#else
LONG WINAPI ExceptionHandler(struct _EXCEPTION_POINTERS* ExceptionInfo)
{
char *s;
long timestamp = _time32(NULL);
PCONTEXT c ;
static char str[2048] ;
static boost::mutex mtx;
boost::mutex::scoped_lock l(mtx);
c = ExceptionInfo->ContextRecord ;
const char *desc = GetExceptionDescription(ExceptionInfo->ExceptionRecord->ExceptionCode);
if (desc == NULL)
desc = "an" ;
sprintf (str,
"Unfortunately, it appears that %s at address 0x%p has caused this unofficial POV-Ray build to crash. "
"This dialog will allow you to choose whether or not a dump file (useful for diagnostics) is written.\n\n"
"NOTE: If you were running a render, you should be able to recover the part that had already been generated. "
"See the 'Continue' (+c) option in the documentation for more information about this.\n\n"
"Would you like to write a dump file?",
desc,
#ifdef _WIN64
c->Rip);
#else
c->Eip);
#endif
DWORD result = MessageBox (NULL, str, "POV-Ray for Windows", MB_ICONSTOP | MB_TOPMOST | MB_TASKMODAL | MB_YESNO | MB_DEFBUTTON1 | MB_SETFOREGROUND) ;
if (result == IDYES)
{
// write a full dump first, then a minidump. it's no big deal if the full dump write fails.
WriteDump(ExceptionInfo, true, timestamp);
if ((s = WriteDump(ExceptionInfo, false, timestamp)) != NULL)
{
MessageBox (main_window, "The dump was successfully saved.", "POV-Ray for Windows", MB_OK) ;
sprintf(str, "/select,%s", s);
ShellExecute (NULL, NULL, "explorer.exe", str, NULL, SW_SHOWNORMAL) ;
ExitProcess (1) ;
}
}
else
MessageBox (main_window, "POV-Ray will now exit.", "POV-Ray for Windows", MB_OK) ;
ExitProcess (1) ;
return (EXCEPTION_CONTINUE_SEARCH) ; // make compiler happy
}
#endif // POV_RAY_IS_OFFICIAL
int execute_tool (char *s)
{
int error ;
STARTUPINFO startupInfo ;
PROCESS_INFORMATION procInfo ;
if (strlen (s) == 0)
{
PovMessageBox ("No command to run!", "Tool Error") ;
return (0) ;
}
if (*s == '$')
{
switch (toupper (s[1]))
{
case 'S' :
s += 2 ;
while (*s == ' ')
s++ ;
if (strlen (s) == 0)
{
PovMessageBox ("No file to open!", "Tool Error") ;
return (0) ;
}
if ((error = PtrToInt (ShellExecute (main_window, "open", s, NULL, NULL, SW_SHOWNORMAL))) <= 32)
PovMessageBox ("ShellExecute failed", "Tool Error") ;
return (error) ;
case 'E' :
s += 2 ;
while (*s == ' ')
s++ ;
if (strlen (s) == 0)
{
PovMessageBox ("No file to open!", "Tool Error") ;
return (0) ;
}
return EditOpenFile(s) ? 0 : 1;
}
}
startupInfo.cb = sizeof (STARTUPINFO) ;
startupInfo.lpReserved = 0 ;
startupInfo.lpDesktop = NULL ;
startupInfo.lpTitle = NULL ;
startupInfo.dwX = 0 ;
startupInfo.dwY = 0 ;
startupInfo.dwXSize = 0 ;
startupInfo.dwYSize = 0 ;
startupInfo.dwXCountChars = 0 ;
startupInfo.dwYCountChars = 0 ;
startupInfo.dwFillAttribute = 0 ;
startupInfo.dwFlags = STARTF_USESHOWWINDOW ;
startupInfo.wShowWindow = SW_SHOW ;
startupInfo.cbReserved2 = 0 ;
startupInfo.lpReserved2 = 0 ;
if (CreateProcess (NULL, s, NULL, NULL, false, 0, NULL, NULL, &startupInfo, &procInfo) == false)
{
error = GetLastError () ;
PovMessageBox ("Could not run program", "Tool Error") ;
return (error) ;
}
// clean up
CloseHandle (procInfo.hProcess) ;
CloseHandle (procInfo.hThread) ;
return (0) ;
}
void RenderInsertMenu (void)
{
int val ;
char str [_MAX_PATH] ;
char *s1 ;
char *s2 ;
FILE *f ;
stop_rendering = false ;
sprintf (str, "%sInsert Menu\\Images.ini", DocumentsPath) ;
if ((f = fopen (str, "rt")) == NULL)
{
MessageBox (main_window, "Cannot open 'Images.ini' in Insert Menu directory", "Insert Menu Images", MB_OK | MB_ICONEXCLAMATION) ;
return ;
}
InsertMenuSection = 0 ;
InsertMenuSections.clear();
while (fgets (str, sizeof (str), f) != NULL)
{
s1 = clean (str) ;
if (*s1 == '[')
{
if ((s2 = strchr (s1, ']')) != NULL)
{
*s2 = '\0' ;
val = atoi (++s1) ;
if (val == 0)
continue ;
InsertMenuSections.push_back(val);
if (InsertMenuSections.size() == MAX_INSERT_MENU_SECTIONS)
break ;
}
}
}
fclose (f) ;
if (InsertMenuSections.empty())
{
MessageBox (main_window, "No insert menu sections found in 'Images.ini'", "Insert Menu Images", MB_OK | MB_ICONSTOP) ;
return ;
}
sprintf (str, "There are %u insert menu images to render. Press OK to start rendering these now.\n\n"
"Once the render has started you can press the 'Stop Rendering' button to cancel the render job.", (unsigned int) InsertMenuSections.size()) ;
if (MessageBox (main_window, str, "Insert Menu Images", MB_OKCANCEL | MB_ICONINFORMATION) == IDCANCEL)
return ;
PVEnableMenuItem (CM_RENDERSHOW, MF_GRAYED) ;
update_menu_for_render (true) ;
rendering_insert_menu = was_insert_render = no_status_output = true ;
EditShowMessages (true) ;
CalculateClientWindows (true) ;
ShowWindow (message_window, SW_SHOW) ;
sprintf (str, "%sInsert Menu", DocumentsPath) ;
SetCurrentDirectory (str) ;
StartInsertRender = true ;
}
int GetUCS2String(POVMSObjectPtr object, POVMSType key, char *result, int *maxlen)
{
UCS2 *str = new UCS2 [*maxlen] ;
int err = POVMSUtil_GetUCS2String (object, key, str, maxlen) ;
if (err == kNoErr)
{
string abc = UCS2toASCIIString (str) ;
strcpy (result, abc.c_str ()) ;
}
delete str ;
return err ;
}
void ShowIsPaused(void)
{
if (GetSession().BackendFailed() == false && rendersleep == false)
{
SleepTimeStart = clock () ;
status_printf (StatusPPS, PPS_String (GetSession().GetPixelsRendered(), ClockTimeTotal / CLOCKS_PER_SEC)) ;
say_status_message (StatusPPS, "PAUSED") ;
rendersleep = true ;
SendMessage (toolbar_window, TB_CHECKBUTTON, (WPARAM) CM_RENDERSLEEP, MAKELONG (1, 0)) ;
}
}
char *getCommandLine (void)
{
HKEY key ;
static char str [2048] ;
DWORD len = sizeof (str) ;
str [0] = '\0' ;
if (RegOpenKeyEx (HKEY_CURRENT_USER, "Software\\" REGKEY "\\" REGVERKEY "\\Windows", 0, KEY_READ, &key) == ERROR_SUCCESS)
{
RegQueryValueEx (key, "Command Line", 0, NULL, (BYTE *) str, &len) ;
RegCloseKey (key) ;
}
return (str) ;
}
void setRunOnce (void)
{
#ifndef NOSETRUNONCE
char str [_MAX_PATH] ;
HKEY key ;
DWORD result ;
if (RegCreateKeyEx (HKEY_CURRENT_USER,
"Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce",
0,
"",
REG_OPTION_NON_VOLATILE,
KEY_WRITE,
NULL,
&key,
&result) == ERROR_SUCCESS)
{
GetModuleFileName (hInstance, str, sizeof (str)) ;
RegSetValueEx (key, "POV-Ray for Windows", 0, REG_SZ, (BYTE *) str, (int) strlen (str) + 1) ;
RegCloseKey (key) ;
}
#endif
}
void display_cleanup (bool unconditional)
{
if (unconditional)
{
gDisplay.reset();
return;
}
Display *d = gDisplay.get();
if (d == NULL)
return;
WinDisplay *wd = dynamic_cast(d);
if (wd == NULL)
return;
if (wd->GetWidth() != render_width || wd->GetHeight() != render_height)
{
gDisplay.reset();
return;
}
wd->Clear();
}
void cancel_render (void)
{
stop_rendering = true ;
if (GetSession().BackendFailed())
{
render_stopped() ;
return;
}
if (GetSession().CancelRender() == vfeNotRunning)
{
// we possibly have an anamolous situation
MessageBox (NULL, "Warning: had to force state to stopped", "Cancel Render", MB_OK | MB_ICONEXCLAMATION) ;
render_stopped() ;
}
}
// TODO: we have a problem here - if the parse completes very quickly, it's
// possible for the VFE code to process the change to rendering mode before
// we exit start_rendering() [note that the VFE worker thread runs at a higher
// priority than the main UI thread]. as a result, on occasion, the VFE code
// will call the display's Show() method at the same time as we are executing
// display_cleanup(). display_cleanup() can cause the value of the pointer in
// gDisplay to change, and potentially destroy the old one ...
bool start_rendering (bool ignore_source_file)
{
int threadCount = ThreadCount ;
char str [sizeof (command_line)] ;
char path [_MAX_PATH] ;
char file [_MAX_PATH] ;
vfeSession& Session (GetSession());
if (Session.BackendFailed())
{
MessageBox (main_window, "The render backend has shut down due to an error: please re-start POV-Ray", "Error", MB_OK | MB_ICONSTOP) ;
return false;
}
Session.Clear();
Session.ClearOptions();
if (!keep_messages)
clear_messages(false);
if (running_benchmark)
threadCount = benchmark_mode || benchmark_multithread ? ThreadCount : 1 ;
ErrorOccurred = ErrorNotified = false ;
ErrorMessage.clear() ;
ErrorFilename.clear() ;
status_buffer [0] = '\0' ;
povray_return_code = 0 ;
rendersleep = false ;
SleepTimeTotal = 0 ;
render_anim_count = 0 ;
rendering_animation = false ;
stop_rendering = false ;
was_insert_render = false ;
first_frame = true ;
render_width = render_height = 0 ;
KernelTimeStart = GetCPUTime (true, false) ;
UserTimeStart = GetCPUTime (false, true) ;
CPUTimeTotal = KernelTimeTotal = UserTimeTotal = 0 ;
ClockTimeStart = clock () ;
SecondCountStart = time (NULL) ;
SleepTimeTotal = ClockTimeTotal = 0 ;
status_printf (StatusPPS, "") ;
say_status_message (StatusRendertime, "") ;
output_to_file = false ;
InputFileName.clear();
LastRenderPercentage = 0;
SendMessage (StatusPanelItems [IDC_STATUS_PROGRESS].hwnd, PBM_SETPOS, 0, 0) ;
say_status_message (StatusMessage, "") ;
PVEnableMenuItem (CM_RENDERSHOW, MF_GRAYED) ;
update_menu_for_render (true) ;
SendMessage (toolbar_combobox, CB_GETLBTEXT, SendMessage (toolbar_combobox, CB_GETCURSEL, 0, 0), (LPARAM) SecondaryRenderIniFileSection) ;
if (!StartInsertRender)
{
if (!temp_render_region)
if (RegionStr [0] != '\0' && strstr (command_line, RegionStr + 1) == NULL)
RegionStr [0] = '\0' ;
}
if (save_settings)
{
if (restore_command_line)
{
strcpy (str, command_line) ;
strcpy (command_line, old_command_line) ;
}
write_INI_settings (true) ;
if (restore_command_line)
strcpy (command_line, str) ;
EditSaveState () ;
}
try
{
// set up render options
vfeRenderOptions opts ;
opts.SetThreadCount (threadCount);
opts.AddINI (DefaultRenderIniFileName) ;
if (AutoAppendPaths || homeInferred)
{
sprintf (str, "%sinclude", DocumentsPath) ;
opts.AddLibraryPath (str);
opts.AddLibraryPath (FontPath);
}
if (!StartInsertRender)
{
if (running_demo == 0)
{
if (SecondaryRenderIniFileName [0] != '\0')
{
if (!hasTrailingPathSeparator(SecondaryRenderIniFileName))
{
splitpath (SecondaryRenderIniFileName, NULL, str) ;
if (str [0] != '\0')
{
if (SecondaryRenderIniFileSection [0] == '\0')
wrapped_printf ("Preset INI file is '%s'.", SecondaryRenderIniFileName) ;
else
wrapped_printf ("Preset INI file is '%s', section is '%s'.", SecondaryRenderIniFileName, SecondaryRenderIniFileSection) ;
sprintf (str, "%s%s", SecondaryRenderIniFileName, SecondaryRenderIniFileSection) ;
opts.AddINI (str);
}
}
}
if (!ignore_source_file && strlen (source_file_name) != 0)
{
wrapped_printf ("Preset source file is '%s'.", source_file_name) ;
splitpath (source_file_name, dir, NULL) ;
SetCurrentDirectory (dir) ;
sprintf (str, "%s\\povray.ini", dir) ;
if (fileExists (str))
{
wrapped_printf ("File '%s' exists - merging it.", str) ;
opts.AddINI (str);
}
switch (get_file_type (source_file_name))
{
case filePOV :
case fileINC :
opts.SetSourceFile (source_file_name);
break ;
case fileINI :
opts.AddINI (source_file_name);
break ;
default :
message_printf ("POV-Ray for Windows doesn't recognize this file type ; assuming POV source.\n") ;
opts.SetSourceFile (source_file_name);
break ;
}
}
}
if (running_benchmark)
{
opts.AddINI (demo_ini_name) ;
opts.SetSourceFile (demo_file_name) ;
if (strlen (demo_file_name) != 0)
{
splitpath (demo_file_name, dir, NULL) ;
SetCurrentDirectory (dir) ;
}
}
else
{
if (RegionStr [0] != 0)
{
if (strstr (command_line, RegionStr) == NULL && strstr (command_line, RegionStr + 1) == NULL)
{
if (!running_demo)
message_printf ("Selected render region is '%s'.\n", RegionStr + 1) ;
opts.AddCommand (RegionStr);
}
}
if (strlen (command_line))
{
if (!running_demo)
wrapped_printf ("Rendering using command line '%s'.", command_line) ;
opts.AddCommand (command_line);
}
}
}
else
{
// we are rendering the insert menu
if (InsertMenuSection >= InsertMenuSections.size())
throw POV_EXCEPTION_STRING("Insert menu render error - we should be stopped already!");
int section = InsertMenuSections [InsertMenuSection] ;
sprintf (str, "Images.ini[%d]", section) ;
opts.AddINI (str);
}
int result = Session.SetOptions (opts) ;
if (result == vfeNoInputFile)
throw POV_EXCEPTION_STRING("No source file specified, either directly or via an INI file.");
else if (result != vfeNoError)
throw POV_EXCEPTION_STRING (Session.GetErrorString());
// TODO FIXME - magic values
if (opts.GetOptions().TryGetInt(kPOVAttrib_RenderBlockSize, 32) < 4)
throw POV_EXCEPTION (kParseErr, "Minimum permitted render block size is 4 (+BS4 or Render_Block_Size=4)") ;
InputFileName = UCS2toASCIIString (Session.GetInputFilename());
output_to_file = opts.GetOptions().TryGetBool(kPOVAttrib_OutputToFile, true) ;
render_width = Session.GetRenderWidth();
render_height = Session.GetRenderHeight();
PutHKCU("LastRender", "SceneFile", "") ;
PutHKCU("LastRender", "OutputFile", "") ;
PutHKCU("LastRender", "IniOutputFile", "") ;
PutHKCU("LastRender", "IniOutputFile", "") ;
UseAlpha = Session.GetBoolOption("Output_Alpha", false);
if (Session.GetBoolOption("Display", true) == false)
display_cleanup(true);
if (StartInsertRender)
status_printf (StatusMessage, "Rendering Insert Menu entry %d of %d", InsertMenuSection + 1, (unsigned int) InsertMenuSections.size()) ;
else
status_printf (StatusMessage, "Parsing %s", InputFileName.c_str()) ;
if (!running_demo && !demo_mode && !benchmark_mode)
{
GetCurrentDirectory (sizeof (dir), dir) ;
PutHKCU("LastRender", "CurrentDirectory", dir) ;
splitpath ((char *) InputFileName.c_str (), path, file) ;
PutHKCU("LastRender", "SourceFile", get_full_name (file)) ;
splitfn (str, NULL, file, NULL) ;
PutHKCU("LastRender", "SceneFile", file) ;
}
result = Session.StartRender();
if (result < 0)
throw POV_EXCEPTION_CODE (result);
else if (result > 0)
throw POV_EXCEPTION_STRING (Session.GetErrorString());
}
catch (std::exception& e)
{
int errorCode = 0 ;
if (dynamic_cast (&e) != NULL)
{
errorCode = dynamic_cast (&e)->code() ;
povray_return_code = errorCode ;
}
if (povray_return_code == 0)
povray_return_code = -1 ;
ErrorOccurred = true ;
PVEnableMenuItem (CM_RENDERSHOW, MF_ENABLED) ;
update_menu_for_render (false) ;
if (restore_command_line)
{
strcpy (command_line, old_command_line) ;
SendMessage (toolbar_cmdline, WM_SETTEXT, 0, (UINT_PTR) command_line) ;
restore_command_line = false ;
}
// make sure any outstanding messages are processed
ProcessSession();
EditShowMessages (true) ;
if (ErrorNotified == false)
{
sprintf (str, "Failed to start render: %s", e.what()) ;
say_status_message (StatusMessage, str) ;
message_printf ("%s\n", str) ;
}
if (errorCode != kCannotOpenFileErr && errorCode != kParseErr && errorCode != kOutOfMemoryErr)
{
sprintf (str, "Failed to set render options (%s).\nSee message pane for more details.", e.what()) ;
MessageBox (main_window, str, "Error", MB_OK | MB_ICONSTOP) ;
}
if (ErrorFilename.empty() == false)
EditShowParseError (ErrorFilename.c_str(), ErrorMessage.c_str(), ErrorLine, ErrorCol) ;
if (parse_error_sound_enabled)
{
PlaySound (parse_error_sound, NULL, SND_NOWAIT | SND_ASYNC | SND_NODEFAULT) ;
if (!running_demo && !demo_mode && !benchmark_mode)
FeatureNotify ("ParserErrorSound",
"POV-Ray - Parse Error Sound",
"You can change the sound played upon parse errors "
"from the Render Menu.\n\n"
"Click Help for more information.",
"sounds", false) ;
}
buffer_message (mDivider, "\n") ;
EditShowMessages (true) ;
return (false) ;
}
if (!StartInsertRender)
{
if (MenuBarDraw)
{
DrawMenuBar (main_window) ;
MenuBarDraw = false ;
}
bool show = EditShowMessages (true) ;
CalculateClientWindows (true) ;
if (show)
ShowWindow (message_window, SW_SHOW) ;
PutHKCU ("Info", "Rendering", 1) ;
display_cleanup (false) ;
}
currentX = seconds_for_last_line = -1 ;
message_printf ("Rendering with %d thread%s.\n", threadCount, threadCount > 1 ? "s" : "") ;
if (GetRenderWindow())
GetRenderWindow()->SetRenderState (true);
ExternalEvent (EventStartRendering, 0) ;
set_render_priority (render_priority) ;
// FIXME - ought to wait for the render to start here
rendering = true ;
// StartProfile () ;
return (true) ;
}
void render_stopped (void)
{
char *s ;
char str [4096] ;
vfeWinSession& Session (GetSession());
run_renderer = false ;
rendering = false ;
status_buffer[0] = '\0' ;
delay_next_status = 0 ;
SetStatusPanelItemText (IDC_STATUS_DATA_FRAME, "N/A") ;
SendMessage (StatusPanelItems [IDC_STATUS_PROGRESS].hwnd, PBM_SETPOS, 0, 0) ;
bool success = Session.Succeeded();
if (message_output_x > 0)
buffer_message (mIDE, "\n") ;
s = EditGetFilename(true) ;
if (s != NULL && *s != '\0')
{
sprintf (str, "POV-Ray - %s", s) ;
SetCaption (str) ;
}
else
SetCaption ("POV-Ray for Windows") ;
if ((!success || ErrorOccurred) && povray_return_code == 0)
povray_return_code = -1 ;
PrintRenderTimes (true, !ErrorOccurred && !stop_rendering) ;
ExternalEvent (EventStopRendering, povray_return_code) ;
// EndProfile () ;
if (restore_command_line && !rendering_insert_menu)
{
strcpy (command_line, old_command_line) ;
SendMessage (toolbar_cmdline, WM_SETTEXT, 0, (LPARAM) command_line) ;
restore_command_line = false ;
}
if (rendering_insert_menu)
{
if (InsertMenuSection < InsertMenuSections.size() - 1 && !(stop_rendering || quit))
{
wrapped_printf ("Completed rendering Insert Menu section %d. result = %d", InsertMenuSections [InsertMenuSection++], povray_return_code) ;
StartInsertRender = true ;
return ;
}
StartInsertRender = rendering_insert_menu = no_status_output = false ;
was_insert_render = true ;
}
else if (running_benchmark || running_demo)
{
if (running_benchmark == false)
{
strcpy (command_line, old_command_line) ;
restore_command_line = false ;
}
_unlink (demo_file_name) ;
_unlink (demo_ini_name) ;
running_benchmark = running_demo = false ;
if (benchmark_mode) // only applies to benchmarks started from the command-line
PostQuitMessage (0) ;
if (demo_mode)
{
PovMessageBox ("Demonstration completed. POV-Ray will now exit.", "Finished test run") ;
PostQuitMessage (0) ;
}
}
PutHKCU("Info", "Rendering", 0U) ;
set_render_priority (CM_RENDERPRIORITY_NORMAL) ;
if (quit != 0 || exit_after_render)
{
DestroyWindow (main_window) ;
return ;
}
WinDisplay *rw = GetRenderWindow();
if (rw != NULL)
{
rw->SetCaption ("POV-Ray Render Window");
rw->SetRenderState (false);
if (render_auto_close)
rw->Hide();
}
if (main_window_hidden)
TaskBarModifyIcon (main_window, 0, "POV-Ray (Restore: DblClk ; Menu: Mouse2)") ;
InvalidateRect (statuspanel, NULL, false) ;
if (success == true && ErrorOccurred == false)
{
if (render_complete_sound_enabled)
{
PlaySound (render_complete_sound, NULL, SND_ASYNC | SND_NODEFAULT) ;
if (!running_demo && !demo_mode && !benchmark_mode)
FeatureNotify ("RenderCompleteSound",
"POV-Ray - Render Complete Sound",
"You can change the sound played upon completion of rendering "
"from the Render Menu.\n\nIt is also possible to tell POV-Ray "
"for Windows to do other things when a render stops (such as "
"display a message or exit.)",
"sounds", false) ;
}
say_status_message (StatusMessage, "") ;
EditShowMessages (false) ;
CalculateClientWindows (false) ;
switch (on_completion)
{
case CM_COMPLETION_EXIT :
DestroyWindow (main_window) ;
break ;
case CM_COMPLETION_MESSAGE :
PovMessageBox ("Render completed", "Message from POV-Ray for Windows") ;
break ;
}
if (!running_demo && !demo_mode && !benchmark_mode && !rendering_insert_menu && !running_benchmark && !was_insert_render)
{
string ofn = UCS2toASCIIString (Session.GetOutputFilename());
if (output_to_file && ofn.size () != 0)
{
sprintf (str, "Output -> '%s'", ofn.c_str ()) ;
say_status_message (StatusMessage, str) ;
//buffer_stream_message (mIDE, str) ;
sprintf (str, "Your output file has been written to the following location:\n\n"
" %s\n\n"
"Press F1 to learn more about how to control where files are written.",
ofn.c_str ()) ;
FeatureNotify ("OutputFileLocation", "POV-Ray - Output File Notification", str, "Output_File_Name", false) ;
PutHKCU("LastRender", "OutputFile", ofn.c_str ()) ;
}
else
{
if (!running_benchmark)
{
FeatureNotify ("OutputFileOff",
"POV-Ray - No Output File",
"A render has completed but file output was turned off. No file "
"was written.\n\nPress F1 for help on output file control.",
"Output_To_File",
false) ;
}
}
}
}
else
{
if (ErrorNotified == false && ErrorMessage.empty() == false)
say_status_message (StatusMessage, ErrorMessage.c_str()) ;
else if (stop_rendering)
say_status_message (StatusMessage, "Render cancelled by user") ;
else
say_status_message (StatusMessage, "Render failed") ;
if (ErrorFilename.empty() == false)
EditShowParseError (ErrorFilename.c_str(), ErrorMessage.c_str(), ErrorLine, ErrorCol) ;
if (stop_rendering)
{
if (render_error_sound_enabled)
{
PlaySound (render_error_sound, NULL, SND_ASYNC | SND_NODEFAULT) ;
if (!running_demo && !demo_mode && !benchmark_mode)
FeatureNotify ("RenderErrorSound",
"POV-Ray - Render Stopped Sound",
"You can change the sound played upon render errors/cancellation "
"from the Render Menu.",
"sounds", false) ;
}
}
else
{
if (parse_error_sound_enabled)
{
PlaySound (parse_error_sound, NULL, SND_NOWAIT | SND_ASYNC | SND_NODEFAULT) ;
if (!running_demo && !demo_mode && !benchmark_mode)
FeatureNotify ("ParserErrorSound",
"POV-Ray - Parse Error Sound",
"You can change the sound played upon parse errors "
"from the Render Menu.\n\n"
"Click Help for more information.",
"sounds", false) ;
}
}
}
// update the mapping of included files to source files
const set& rf = Session.GetReadFiles();
for (set::const_iterator it = rf.begin(); it != rf.end(); it++)
{
if (_stricmp(it->c_str(), InputFileName.c_str()) == 0)
continue;
if (is_non_primary_file(it->c_str()))
{
FileType ft = get_file_type(it->c_str());
if (ft < fileFirstImageType || ft > fileLastImageType)
{
pair