/*ScianRecorders.c
  Eric Pepke
  Operates recorders from within scian
*/

#include "Scian.h"
#include "ScianTypes.h"
#include "ScianIDs.h"
#include "ScianWindows.h"
#include "ScianObjWindows.h"
#include "ScianDialogs.h"
#include "ScianScripts.h"
#include "ScianErrors.h"
#include "ScianSymbols.h"
#include "ScianLists.h"
#include "ScianControls.h"
#include "ScianButtons.h"
#include "ScianTextBoxes.h"
#include "ScianTitleBoxes.h"
#include "ScianIcons.h"
#include "ScianStyle.h"
#include "ScianObjFunctions.h"
#include "ScianDatabase.h"
#include "ScianGarbageMan.h"
#include "ScianScrDump.h"
#include "ScianRecorders.h"
#include "ScianColors.h"
#include "ScianObjFunctions.h"
#include "ScianTemplates.h"
#include "ScianTemplateHelper.h"
#include "ScianLVR5000.h"
#include "ScianTQ2026F.h"
#include "ScianSnap.h"

ObjPtr curRecorder = NULLOBJ;		/*Current recorder*/
Bool recordEnabled = false;		/*True iff recording is enabled*/
extern double videoClock;		/*Clock for the duration of the video*/
real fps = 30.0;			/*Default is 30 fps*/
Bool recording = false;			/*True iff recording*/
ObjPtr recorderClass;			/*Class of all recorders*/
ObjPtr commRecorderClass;		/*Class of recorders that do comm*/

#ifdef PROTO
ObjPtr NewRecorder(ObjPtr class, char *name, char *brandName)
#else
ObjPtr NewRecorder(class, name, brandName)
ObjPtr class;
char *name;
char *brandName;
#endif
{
    ObjPtr retVal;

    retVal = NewObject(class ? class : recorderClass, 0L);
    SetVar(retVal, NAME, NewString(name));
    SetVar(retVal, BRANDNAME, NewString(brandName));
    return retVal;
}

#ifdef PROTO
void RegisterRecorder(ObjPtr recorder)
#else
void RegisterRecorder(recorder)
ObjPtr recorder;
#endif
{
    AddObjToDatabase(recorder);
    ApplySavedSettings(recorder);
    if (!curRecorder)
    {
	curRecorder = recorder;
    }
}

#ifdef PROTO
Bool SetRecorder(char *name) 
#else
Bool SetRecorder(name)
char *name;
#endif
/*Sets the current recorder to a recorder named name*/
{
    int k;
    ObjPtr allRecorders;
    ObjPtr keyList;
    ThingListPtr runner;

    if (logging)
    {
	char cmd[256];
	char *s;

	sprintf(cmd, "set recorder ");
	s = &(cmd[0]);
	while (*s) ++s;
	*s++ = ' ';
	s = PrintScriptString(s, name);
	*s++ = '\n';
	*s = 0;
	Log(cmd);
    }

    keyList = NewList();
    PostfixList(keyList, NewSymbol(CLASSID));
    PostfixList(keyList, NewInt(CLASS_RECORDER));

    allRecorders = SearchDatabase(keyList);
    if (allRecorders)
    {
	runner = LISTOF(allRecorders);

	while (runner)
	{
 	    ObjPtr var, curName;
	    curName = GetStringVar("SetRecorder", runner -> thing, NAME);
	    if (curName)
	    {
		/*Test name*/
		if (0 == strcmp2(GetString(curName), name))
		{
		    /*It's a match*/
		    curRecorder = runner -> thing;
		    return true;
		}

		/*Test brand name plus name*/
		var = GetVar(runner -> thing, BRANDNAME);
		if (var)
		{
		    strcpy(tempStr, GetString(var));
		    strcat(tempStr, " ");
		    strcat(tempStr, GetString(curName));
		}
	    }
	    runner = runner -> next;
	}
    }
    return false;
}

#ifdef PROTO
void SetFPS(real newFPS)
#else
void SetFPS(newFPS)
void newFPS;
#endif
/*Sets the number of frames per second to newFPS*/
{
    if (curRecorder)
    {
	SetVar(curRecorder, FRAMERATE, NewReal(newFPS));
	ImInvalid(curRecorder);
    }
}

#ifdef PROTO
void SetScreenSize(int x, int y)
#else
void SetScreenSize(x, y)
int x, y;
#endif
{
    if (curRecorder)
    {
	SetVar(curRecorder, FRAMEWIDTH, NewInt(x));
	SetVar(curRecorder, FRAMEHEIGHT, NewInt(y));
    }
}

#ifdef PROTO
void GetScreenSize(int *x, int *y)
#else
void GetScreenSize(*x, *y)
int *x, *y;
#endif
{
    if (curRecorder)
    {
	ObjPtr var;

	var = GetIntVar("GetScreenSize", curRecorder, FRAMEWIDTH);
	if (var)
	{
	    *x = GetInt(var);
	}
	else
	{
	    *x = VSCRWIDTH;
	}

	var = GetIntVar("GetScreenSize", curRecorder, FRAMEHEIGHT);
	if (var)
	{
	    *y = GetInt(var);
	}
	else
	{
	    *y = VSCRHEIGHT;
	}
    }
    else
    {
	*x = VSCRWIDTH;
	*y = VSCRHEIGHT;
    }
}

Bool PrepareToRecord(nFrames)
int nFrames;
/*Prepares to record nFrames*/
{
    int retVal;
    FuncTyp method;
    ObjPtr var;

    if (!curRecorder)
    {
	return false;
    }
    var = GetRealVar("PrepareToRecord", curRecorder, FRAMERATE);
    if (!var)
    {
	fps = 30.0;
    }
    else
    {
	fps = GetReal(var);
    }

    PushNonVisWindows();

    SetSystemClock(0.0);
    if (recordEnabled)
    {
	method = GetMethod(curRecorder, PREPARETORECORD);
	if (method)
	{
	    return IsTrue((*method)(curRecorder, nFrames)) ? true : false;
	}
    }
    else
    {
	return true;
    }
    return false;
}

Bool ConnectRecorder()
/*Connects to the current recorder*/
{
    int retVal;
    FuncTyp method;

    if (recordEnabled)
    {
	method = GetMethod(curRecorder, CONNECT);
	if (method)
	{
	    Bool truth;
	    truth = IsTrue((*method)(curRecorder)) ? true : false;
	    return truth;
	}
	return false;
    }
    else
    {
	printf("This is a dry run.  Status messages will be printed, but no actual recording\n");
	printf("will be done.  To record for real, rerun SciAn including the -v flag on the\n");
	printf("command line.\n");
	return true;
    }
}

Bool StopRecording()
/*Stops recording on the current recorder*/
{
    int retVal;
    FuncTyp method;

    MySetCursor(0);
    SetSystemClock(0.0);
    if (recordEnabled)
    {
	method = GetMethod(curRecorder, STOPRECORDING);
	if (method)
	{
	    return IsTrue((*method)(curRecorder)) ? true : false;
	}
    }
    else
    {
	return true;
    }
    return false;
}

#ifdef PROTO
void Delay(double time)
#else
void Delay(time)
double time;
#endif
/*Delays for time seconds*/
{
    struct tms buffer;
    long started;

    started = times(&buffer);
    do
    {
    } while (times(&buffer) - started < (long) (time * HEARTBEAT));
}

Bool SnapOneFrame()
/*Snaps a single frame shot on the current recorder*/
{
    int retVal = false;
    FuncTyp method;

    MySetCursor(-1);
    PushNonVisWindows();

    if (recordEnabled)
    {
	Delay(0.2);

	method = GetMethod(curRecorder, SNAPONEFRAME);
	if (method)
	{
	    retVal = IsTrue((*method)(curRecorder)) ? true : false;
	}
    }
    else
    {
	retVal = true;
	printf("Snap at %lg\n", videoClock);
    }
    if (retVal)
    {
	videoClock += 1.0 / fps;
    }
    return retVal;
}

void DisconnectRecorder()
/*Connects to the current recorder*/
{
    int retVal;
    FuncTyp method;

    if (recordEnabled)
    {
	method = GetMethod(curRecorder, DISCONNECT);
	if (method)
	{
	    (*method)(curRecorder);
	}
    }
}

static ObjPtr HideRecorderDriversWindow(window)
ObjPtr window;
/*Hide a recorder drivers window, just return OK*/
{
    return ObjTrue;
}

WinInfoPtr NewRecorderDriversWindow(void)
/*Create a new recorder drivers window*/
{
    WinInfoPtr objWin;
    ThingListPtr runner;
    ObjPtr panel, contents, corral, button;
    int bw;
    int l, r, b, t;
    ObjPtr allRecorderDrivers;
    ObjPtr keyList;

    /*Create the window*/
    objWin = NewObjWindow(NULLOBJ, "Recorder Drivers", WINDBUF, RDWINWIDTH, RDWINHEIGHT, SCRWIDTH, SCRHEIGHT);

    /*Set a null but successful HIDE routine*/
    SetMethod((ObjPtr) objWin, HIDE, HideRecorderDriversWindow);

    /*Add a help string*/
    SetVar((ObjPtr) objWin, HELPSTRING, 
	NewString("This window shows all the recorder drivers in Scian."));

    /*Put in a panel*/
    panel = NewPanel(greyPanelClass, 0, RDWINWIDTH, 0, RDWINHEIGHT);
    SetVar(panel, STICKINESS, NewInt(STICKYLEFT + STICKYRIGHT +
				     STICKYBOTTOM + STICKYTOP));

    contents = GetVar((ObjPtr) objWin, CONTENTS);
    PrefixList(contents, panel);
    SetVar(panel, PARENT, (ObjPtr) objWin);

    l = 0; r = RDWINWIDTH; b = 0; t = RDWINHEIGHT;

    /*Put in buttons and an icon corral*/
    contents = GetListVar("NewRecorderDriversWindow", panel, CONTENTS);
    if (!contents)
    {
	return 0;
    }
    /*Make an icon corral*/
    corral = NewIconCorral(NULLOBJ, l + MINORBORDER, r - MINORBORDER, b + 2 * MINORBORDER + BUTTONHEIGHT, t - MINORBORDER, BARRIGHT + BARBOTTOM);
    SetVar(corral, STICKINESS, NewInt(STICKYLEFT + STICKYRIGHT +
				     STICKYBOTTOM + STICKYTOP));
    SetVar(corral, TOPDOWN, ObjTrue);
    SetVar(corral, NAME, NewString("Recorder Drivers Corral"));
    SetVar(corral, HELPSTRING,
	NewString("This corral contains icons for all the recorder drivers in \
SciAn.  Recorder drivers are used to do frame-by-frame recording from a script.  \
The active recorder driver is indicated by a red pilot light on the icon.  \
To change the active recorder, select it and press the Activate button.  To \
show controls for any of the recorders, select them and press the Show Controls \
button."));
    PrefixList(contents, corral);
    SetVar(corral, PARENT, panel);

    l += MINORBORDER;
    r -= MINORBORDER;
    b += MINORBORDER;
    t = b + BUTTONHEIGHT;
    bw = (r - l - MINORBORDER) / 2;

    /*Make a show info button*/
    button = NewFunctionButton(objWin,
		l, l + bw, 
		b, b + BUTTONHEIGHT, OF_SHOW_CONTROLS); 
    if (button)
    {
	SetVar(button, PARENT, panel);
	SetVar(button, STICKINESS, NewInt(STICKYBOTTOM + STICKYLEFT + FLOATINGRIGHT));
	PrefixList(contents, button);
    }

    /*Make an activate recorder function button*/
    button = NewFunctionButton(objWin, r - bw, r, b, b + BUTTONHEIGHT, OF_ACTIVATE);
    if (button)
    {
	SetVar(button, PARENT, panel);
	SetVar(button, STICKINESS, NewInt(STICKYBOTTOM + STICKYRIGHT + FLOATINGLEFT));
	PrefixList(contents, button);
    }

    /*Drop all the recorders into the window*/
    keyList = NewList();
    PostfixList(keyList, NewSymbol(CLASSID));
    PostfixList(keyList, NewInt(CLASS_RECORDER));

    allRecorderDrivers = SearchDatabase(keyList);

    runner = LISTOF(SortListByStringVar(allRecorderDrivers, NAME, true));

    while(runner)
    {
	ObjPtr icon, name;
	FuncTyp method;
	
	icon = GetVar(runner -> thing, DEFAULTICON);
	if (!icon)
	{
	    icon = NewIcon(0, 0, ICONRECORDER, "?");
	}
	else
	{
	    icon = NewObject(icon, 0L);
	}
	name = GetVar(runner -> thing, NAME);
	SetVar(icon, NAME, name);
	SetVar(icon, FORMAT, GetVar(runner -> thing, BRANDNAME));

	SetVar(icon, ICONLOC, NULLOBJ);
	SetVar(icon, REPOBJ, runner -> thing);
	SetVar(icon, CORRAL, corral);
	DropIconSeriesInCorral(corral, icon);

	runner = runner -> next;
    }

    return objWin;
}

static ObjPtr ShowRecorderControls(object, windowName)
ObjPtr object;
char *windowName;
/*Makes a new control window to control recorder object*/
{
    WinInfoPtr controlWindow;
    ObjPtr var;
    ObjPtr panel;
    ObjPtr controlField;
    ObjPtr contents;
    ObjPtr curObj;
    ObjPtr firstButton = NULLOBJ;
    int left, right, bottom, top, width;
    WinInfoPtr dialogExists;
    Bool abortp = false;
    Bool saveSettings = false;

    dialogExists = DialogExists((WinInfoPtr) object, NewString("Controls"));
    controlWindow = GetDialog((WinInfoPtr) object, NewString("Controls"), windowName, 
	CWINWIDTH, CWINHEIGHT, CWINWIDTH, CWINHEIGHT, WINFIXEDSIZE + WINDBUF);
    
    if (!dialogExists)
    {
	SetVar((ObjPtr) controlWindow, REPOBJ, object);

	/*Add in help string*/
	SetVar((ObjPtr) controlWindow, HELPSTRING,
	    NewString("This window shows controls for a recorder driver.  \
At the right is an icon corral showing a series of icons.  Each icon represents a \
set of attributes of the visualization object.  On the left are the controls for \
the selected set of attributes.  \
Use Help In Context and click on the various controls to find out what they do.  \
Click on a different icon to choose a different set of attributes."));

	/*Add in a panel*/
	panel = NewPanel(greyPanelClass, 0, CWINWIDTH, 0, CWINHEIGHT);
	if (!panel)
	{
	    return ObjFalse;
	}
	contents = GetVar((ObjPtr) controlWindow, CONTENTS);
	PrefixList(contents, panel);
	SetVar(panel, PARENT, (ObjPtr) controlWindow);

	contents = GetVar(panel, CONTENTS);

	/*Add in a control field*/
	controlField = NewControlField(CWINWIDTH - CORRALBORDER - CWINCORRALWIDTH,
			       CWINWIDTH - CORRALBORDER,
			       CORRALBORDER,
			       CWINHEIGHT - CORRALBORDER,
				"Recorder Attributes", OBJECTSFROMTOP | BARRIGHT);
	SetVar(controlField, HELPSTRING,
	   NewString("This icon button group shows sets of attributes of the visualization \
object that can be modified.  The left side of the panel shows controls for the \
attribute given by the selected icon button.  To show another set of \
attributes, press another button."));
	SetVar(controlField, PARENT, panel);
	PrefixList(contents, controlField);

	contents = GetVar(controlField, CONTENTS);

	/*Fill the control field up with buttons*/
	curObj = object;
	top = -MAJORBORDER;
	width = CWINCCWIDTH;

	saveSettings = GetPredicate(curObj, CANSAVESETTINGS);


	while (curObj)
	{
	    ObjPtr icon;
	    icon = Get1Var(curObj, CONTROLICON);
	    if (icon)
	    {
		ObjPtr button;
		ObjPtr panelContents;
		FuncTyp method;
		int whichIcon;
		char *name;

		if (firstButton && abortp) break;

		var = GetIntVar("ShowVisControls", icon, WHICHICON);
		if (var)
		{
		    whichIcon = GetInt(var);
		}
		else
		{
		    whichIcon = ICONQUESTION;
		}

		var = GetStringVar("ShowVisControls", icon, NAME);
		if (var)
		{
		    name = GetString(var);
		}
		else
		{
		    name = "Unknown";
		}

		button = NewIconLabeledButton(0, width, top - CWINICONBUTHEIGHT, top,
			whichIcon, UIYELLOW, name, BS_PITTED);
		SetMethod(button, ICONEXTRADRAW, GetMethod(icon, ICONEXTRADRAW));
		SetVar(button, REPOBJ, object);
		SetMethod(button, CHANGEDVALUE, ChangeControlPanelButton);

		SetVar(button, PANEL, panel);

		if (!firstButton)
		{
		    firstButton = button;
		}

		if (saveSettings)
		{
		    SetVar(button, CANSAVESETTINGS, ObjTrue);
		}
		/*Make a new panel contents just for this button*/
		panelContents = NewList();
		PrefixList(panelContents, controlField);
		SetVar(panelContents, PARENT, panel);
		SetVar(button, PANELCONTENTS, panelContents);
		SetVar(button, PARENT, panelContents);

		/*Give the button a chance to add controls*/
		method = Get1Method(curObj, ADDCONTROLS);
		if (method)
		{
		    SetVar(button, CONTROLSADDED, ObjFalse);
		    SetMethod(button, ADDCONTROLS, method);
		}
		else
		{
		    SetVar(button, CONTROLSADDED, ObjTrue);
		}
		PrefixList(contents, button);
		SetVar(button, PARENT, controlField);
		top -= CWINICONBUTHEIGHT + MINORBORDER;
    
	    }
	    if (GetPredicate(curObj, ABORTCONTROLS)) abortp = true;
	    curObj = ClassOf(curObj);
	}

	/*Adjust the scroll bars*/
	RecalcScroll(controlField);

	if (firstButton)
	{
	    SetValue(firstButton, NewInt(1));
	    SetVar(controlField, BUTTON, firstButton);
	}
    }

    return (ObjPtr) controlWindow;
}

WinInfoPtr RecorderDriversWindow()
/*Returns or creates a recorder drivers window*/
{
    WinInfoPtr retVal;

    retVal = GetWinFromTitle("Recorder Drivers");
    if (!retVal)
    {
	retVal = NewRecorderDriversWindow();
    }
    return retVal;
}

WinInfoPtr AnimationControlsWindow()
/*Returns or creates an animation controls window*/
{
    WinInfoPtr retVal;
#if 0
    retVal = GetWinFromTitle("Recorder Drivers");
    if (!retVal)
    {
	retVal = NewRecorderDriversWindow();
    }
    return retVal;
#endif
}

static ObjPtr AddFrameControls(object, panelContents)
ObjPtr object, panelContents;
/*Adds frame controls to a panel*/
{
    ObjPtr textBox;

    /*Add FrameRate label*/
    textBox = TemplateTextBox(FramesTemplate, "Frame Rate Label", ONE_LINE, "Frame Rate:");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);

    /*Add text box*/
    textBox = TemplateTextBox(FramesTemplate, "Frame Rate", EDITABLE + WITH_PIT + ONE_LINE, "");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    AssocTextRealControlWithVar(textBox, object, FRAMERATE, 0.0, plusInf, TR_NE_BOTTOM);
    SetTextAlign(textBox, RIGHTALIGN);
    SetVar(textBox, HELPSTRING, NewString("This text box controls the frame rate in frames \
per second used to control the video recorder.  Full speed frame rate for NTSC video \
as used in the USA and Japan is 30 frames per second.*  PAL and SECAM video use \
25 frames per second, and motion picture recording uses 24 frames per second.  Edit \
the text and press the Enter key to change the frame rate.\n\n\
*Actually, NTSC frame rate is 29.97 frames per second, but most people call it \
30 to make it easier to deal with.  The 1% difference is not enough to notice \
for most practical purposes.  You can use non-integral frame \
rates if you like, but it is usually not worth the bother."));

    /*Add fps label*/
    textBox = TemplateTextBox(FramesTemplate, "FPS Label", ONE_LINE, "frames per second (fps)");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);

    /*Add FrameHeight label*/
    textBox = TemplateTextBox(FramesTemplate, "Frame Height Label", ONE_LINE, "Frame Height:");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);

    /*Add text box*/
    textBox = TemplateTextBox(FramesTemplate, "Frame Height", EDITABLE + WITH_PIT + ONE_LINE, "");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    AssocTextIntControlWithVar(textBox, object, FRAMEHEIGHT, 0.0, plusInf, TR_NE_BOTTOM + TR_INT_ONLY);
    SetTextAlign(textBox, RIGHTALIGN);
    SetVar(textBox, HELPSTRING, NewString("This text box controls the height \
of the recording frame in screen pixels.  By default, this is set to the width of the Silicon Graphics \
NTSC screen.  If you are using a scan converter instead of the NTSC mode, change this \
to match the portion of the screen you wish to use.  \
The recording frame always has its origin at the bottom left of the screen."));

    /*Add fps label*/
    textBox = TemplateTextBox(FramesTemplate, "Pixels 1 Label", ONE_LINE, "pixels");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);

    /*Add FrameHeight label*/
    textBox = TemplateTextBox(FramesTemplate, "Frame Width Label", ONE_LINE, "Frame Width:");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);

    /*Add text box*/
    textBox = TemplateTextBox(FramesTemplate, "Frame Width", EDITABLE + WITH_PIT + ONE_LINE, "");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    AssocTextIntControlWithVar(textBox, object, FRAMEWIDTH, 0.0, plusInf, TR_NE_BOTTOM + TR_INT_ONLY);
    SetTextAlign(textBox, RIGHTALIGN);
    SetVar(textBox, HELPSTRING, NewString("This text box controls the width \
of the recording frame in screen pixels.  By default, this is set to the width of the Silicon Graphics \
NTSC screen.  If you are using a scan converter instead of the NTSC mode, change this \
to match the portion of the screen you wish to use.  \
The recording frame always has its origin at the bottom left of the screen."));

    /*Add fps label*/
    textBox = TemplateTextBox(FramesTemplate, "Pixels 2 Label", ONE_LINE, "pixels");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);

    return ObjTrue;
}

static ObjPtr AddCommControls(object, panelContents)
ObjPtr object, panelContents;
/*Adds controls appropriate to a communications object to panelContents*/
{
    ObjPtr titleBox, button, radio, var;
    ObjPtr textBox;

    /*Make the port device editing box and label*/
    textBox = TemplateTextBox(CommTemplate, "Port Device Text", 0, "Port Device:");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);

    textBox = TemplateTextBox(CommTemplate, "Port Device", EDITABLE + WITH_PIT + ONE_LINE, "");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    SetVar(textBox, HELPSTRING, NewString("This text box controls the serial \
port device used to control the video recorder.  Enter a new device and press \
the Enter key to change it.\n\n\
Each physical serial port on the computer is controlled by a software device \
driver.  This serial port device needs to be set to the correct device \
for the port you are using to drive your video recorder.  If you do not \
know the correct device name, look at the label on the serial port connection \
and consult the documentation for your computer."));
    AssocDirectControlWithVar(textBox, object, PORTDEV);

    /*Make the radio buttons for baud rate*/
    titleBox = TemplateTitleBox(CommTemplate, "Baud Rate");
    SetVar(titleBox, PARENT, panelContents);
    PrefixList(panelContents, titleBox);

    radio = NewRadioButtonGroup("Baud Rate Radio Buttons");
    SetVar(radio, PARENT, panelContents);
    PrefixList(panelContents, radio);
    SetVar(radio, HELPSTRING, NewString("This radio button group controls the baud rate \
for communication with the video recorder.  The baud rate must be set to the \
same value that your video recorder uses.  If you do not know what this is, consult \
the documentation for your video recorder."));

    button = TemplateRadioButton(CommTemplate, "300");
    AddRadioButton(radio, button);
    SetVar(radio, HELPSTRING, NewString("This radio button sets the baud rate to \
300 baud."));

    button = TemplateRadioButton(CommTemplate, "1200");
    AddRadioButton(radio, button);
    SetVar(radio, HELPSTRING, NewString("This radio button sets the baud rate to \
1200 baud."));

    button = TemplateRadioButton(CommTemplate, "2400");
    AddRadioButton(radio, button);
    SetVar(radio, HELPSTRING, NewString("This radio button sets the baud rate to \
2400 baud."));

    button = TemplateRadioButton(CommTemplate, "4800");
    AddRadioButton(radio, button);
    SetVar(radio, HELPSTRING, NewString("This radio button sets the baud rate to \
4800 baud."));

    button = TemplateRadioButton(CommTemplate, "9600");
    AddRadioButton(radio, button);
    SetVar(radio, HELPSTRING, NewString("This radio button sets the baud rate to \
9600 baud."));

    button = TemplateRadioButton(CommTemplate, "19200");
    AddRadioButton(radio, button);
    SetVar(radio, HELPSTRING, NewString("This radio button sets the baud rate to \
19200 baud."));

    AssocDirectControlWithVar(radio, object, BAUDRATE);

    return ObjTrue;
}

static ObjPtr DrawExtraRecorderIcon(icon, x, y)
ObjPtr icon;
int x, y;
/*Draws the extra stuff associated with an icon*/
{
    ObjPtr repObj;
    repObj = GetVar(icon, REPOBJ);
    if (repObj && repObj == curRecorder)
    {
	DrawIcon(x, y, ICONSTAR, 
		(char *) 0, (char *) 0, 
		UIRED, DI_DRAWFORE | DI_DRAWBACK);
    }
    return ObjTrue;
}

static ObjPtr ActivateRecorder(recorder)
ObjPtr recorder;
/*Activates a recorder*/
{
    SetVar(curRecorder, APPEARANCE, ObjTrue);
    curRecorder = recorder;
    SetVar(curRecorder, APPEARANCE, ObjTrue);
    return ObjTrue;
}

static ObjPtr MakeRecIconAppearance(icon)
ObjPtr icon;
/*Makes an icon's appearance*/
{
    ImInvalid(icon);
    SetVar(icon, APPEARANCE, ObjTrue);
    return ObjTrue;
}

void InitRecorders(void)
/*Initializes the recorder system*/
{
    ObjPtr icon;
    char *devName;
    ObjPtr recorder;
    ObjPtr snapshot;

    recorderClass = NewObject(NULLOBJ, 0L);
    SetVar(recorderClass, CLASSID, NewInt(CLASS_RECORDER));
    AddToReferenceList(recorderClass);
    SetMethod(recorderClass, SHOWCONTROLS, NewControlWindow);
    SetMethod(recorderClass, NEWCTLWINDOW, ShowRecorderControls);
    SetVar(recorderClass, DOUBLECLICK, NewString(OF_SHOW_CONTROLS));
    icon = NewIcon(0, 0, ICONFRAME, "Frames");
    SetVar(recorderClass, CONTROLICON, icon);
    SetVar(recorderClass, CANSAVESETTINGS, ObjTrue);
    SetVar(recorderClass, SAVEEXTENSION, NewString("recdvr"));
    SetMethod(recorderClass, SAVECPANEL, SaveSnapshotControls);
    SetMethod(recorderClass, SAVEALLCONTROLS, LogSnapshotControls);
    SetMethod(recorderClass, ADDCONTROLS, AddFrameControls);
    SetVar(recorderClass, FRAMERATE, NewReal(30));
    AddSnapVar(recorderClass, FRAMERATE);
    SetVar(recorderClass, FRAMEWIDTH, NewInt(VSCRWIDTH));
    AddSnapVar(recorderClass, FRAMEWIDTH);
    SetVar(recorderClass, FRAMEHEIGHT, NewInt(VSCRHEIGHT));
    AddSnapVar(recorderClass, FRAMEHEIGHT);
    SetMethod(recorderClass, ACTIVATEOBJECT, ActivateRecorder);

    icon = NewIcon(0, 0, ICONRECORDER, "?");
    SetMethod(icon, ICONEXTRADRAW, DrawExtraRecorderIcon);
    SetVar(icon, HELPSTRING, NewString("This icon represents a video recorder \
driver.  Recorder drivers are used to do frame-by-frame recording from a script.  \
The active recorder driver is indicated by a red pilot light on the icon.  \
To change the active recorder, select it and press the Activate button.  To \
show controls for any of the recorders, select them and press the Show Controls \
button."));
    SetVar(recorderClass, DEFAULTICON, icon);
    DeclareIndirectDependency(icon, APPEARANCE, REPOBJ, APPEARANCE);
    SetMethod(icon, APPEARANCE, MakeRecIconAppearance);

    commRecorderClass = NewObject(recorderClass, 0L);
    AddToReferenceList(commRecorderClass);
    SetVar(commRecorderClass, NAME, NewString("Communication"));
    SetVar(commRecorderClass, BAUDRATE, NewInt(4));	/*9600 baud*/
    SetMethod(commRecorderClass, ADDCONTROLS, AddCommControls);
    icon = NewIcon(0, 0, ICONCOMM, "Communications");
    SetVar(commRecorderClass, CONTROLICON, icon);
    devName = getenv("SCIAN_RECORDER_DEV");
    if (!devName || !devName[0])
    {
    }
    else
    {
	SetVar(commRecorderClass, PORTDEV, NewString(devName));
    }


    devName = getenv("SCIAN_RECORDER_DEV");
    if (!devName || !devName[0])
    {
	devName = "/dev/ttyd4";
    }
    SetVar(commRecorderClass, PORTDEV, NewString(devName));

    AddSnapVar(commRecorderClass, BAUDRATE);
    AddSnapVar(commRecorderClass, PORTDEV);

    InitLVR5000();
    InitTQ2026F();
    InitScrDump();
}

void KillRecorders(void)
/*Gets rid of all the recorders*/
{
    RemoveFromReferenceList(commRecorderClass);
    RemoveFromReferenceList(recorderClass);
    KillScrDump();
    KillTQ2026F();
    KillLVR5000();
}
